ProjectTask.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 1.2.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Console\Command\Task;
  16. use Cake\Console\Command\Task\BakeTask;
  17. use Cake\Utility\Folder;
  18. /**
  19. * Task class for creating new project apps and plugins
  20. *
  21. */
  22. class ProjectTask extends BakeTask {
  23. /**
  24. * App path (used in testing).
  25. *
  26. * @var string
  27. */
  28. public $appPath = null;
  29. /**
  30. * Checks that given project path does not already exist, and
  31. * finds the app directory in it. Then it calls bake() with that information.
  32. *
  33. * @return mixed
  34. */
  35. public function main() {
  36. $project = null;
  37. if (isset($this->args[0])) {
  38. $project = $this->args[0];
  39. } else {
  40. $appContents = array_diff(scandir(APP), ['.', '..']);
  41. if (empty($appContents)) {
  42. $suggestedPath = rtrim(APP, DS);
  43. } else {
  44. $suggestedPath = APP . 'MyApp';
  45. }
  46. }
  47. while (!$project) {
  48. $prompt = 'What is the path to the project you want to bake?';
  49. $project = $this->in($prompt, null, $suggestedPath);
  50. }
  51. $namespace = basename($project);
  52. if (!preg_match('/^\w[\w\d_]+$/', $namespace)) {
  53. $this->err('Project Name/Namespace needs to start with a letter and can only contain letters, digits and underscore');
  54. $this->args = [];
  55. return $this->main();
  56. }
  57. if ($project && !Folder::isAbsolute($project) && isset($_SERVER['PWD'])) {
  58. $project = $_SERVER['PWD'] . DS . $project;
  59. }
  60. $response = false;
  61. while (!$response && is_dir($project) === true && file_exists($project . 'Config' . 'boostrap.php')) {
  62. $prompt = sprintf('<warning>A project already exists in this location:</warning> %s Overwrite?', $project);
  63. $response = $this->in($prompt, ['y', 'n'], 'n');
  64. if (strtolower($response) === 'n') {
  65. $response = $project = false;
  66. }
  67. }
  68. if ($project === false) {
  69. $this->out('Aborting project creation.');
  70. return;
  71. }
  72. if ($this->bake($project)) {
  73. $this->out('<success>Project baked successfully!</success>');
  74. return $project;
  75. }
  76. }
  77. /**
  78. * Uses either the CLI option or looks in $PATH and cwd for composer.
  79. *
  80. * @return string|false Either the path to composer or false if it cannot be found.
  81. */
  82. public function findComposer() {
  83. if (!empty($this->params['composer'])) {
  84. $path = $this->params['composer'];
  85. if (file_exists($path)) {
  86. return $path;
  87. }
  88. }
  89. $composer = false;
  90. $path = env('PATH');
  91. if (!empty($path)) {
  92. $paths = explode(PATH_SEPARATOR, $path);
  93. $composer = $this->_searchPath($paths);
  94. }
  95. return $composer;
  96. }
  97. /**
  98. * Search the $PATH for composer.
  99. *
  100. * @param array $path The paths to search.
  101. * @return string|bool
  102. */
  103. protected function _searchPath($path) {
  104. $composer = ['composer.phar', 'composer'];
  105. foreach ($path as $dir) {
  106. foreach ($composer as $cmd) {
  107. if (is_file($dir . DS . $cmd)) {
  108. $this->_io->verbose('Found composer executable in ' . $dir);
  109. return $dir . DS . $cmd;
  110. }
  111. }
  112. }
  113. return false;
  114. }
  115. /**
  116. * Uses composer to generate a new package using the cakephp/app project.
  117. *
  118. * @param string $path Project path
  119. * @return mixed
  120. */
  121. public function bake($path) {
  122. $composer = $this->findComposer();
  123. if (!$composer) {
  124. $this->error('Cannot bake project. Could not find composer. Add composer to your PATH, or use the -composer option.');
  125. return false;
  126. }
  127. $this->out('<info>Downloading a new cakephp app from packagist.org</info>');
  128. $command = 'php ' . escapeshellarg($composer) . ' create-project -s dev cakephp/app ' . escapeshellarg($path);
  129. try {
  130. $this->callProcess($command);
  131. } catch (\RuntimeException $e) {
  132. $error = $e->getMessage();
  133. $this->error('Installation from packagist.org failed with: %s', $error);
  134. return false;
  135. }
  136. return true;
  137. }
  138. /**
  139. * get the option parser.
  140. *
  141. * @return \Cake\Console\ConsoleOptionParser
  142. */
  143. public function getOptionParser() {
  144. $parser = parent::getOptionParser();
  145. return $parser->description(
  146. 'Generate a new CakePHP project skeleton.'
  147. )->addArgument('name', [
  148. 'help' => 'Application directory to make, if it starts with "/" the path is absolute.'
  149. ])->addOption('empty', [
  150. 'boolean' => true,
  151. 'help' => 'Create empty files in each of the directories. Good if you are using git'
  152. ])->addOption('theme', [
  153. 'short' => 't',
  154. 'help' => 'Theme to use when baking code.'
  155. ])->addOption('composer', [
  156. 'default' => ROOT . '/composer.phar',
  157. 'help' => 'The path to the composer executable.'
  158. ])->removeOption('plugin');
  159. }
  160. }