CommandScanner.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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 3.5.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Console;
  16. use Cake\Core\App;
  17. use Cake\Core\Configure;
  18. use Cake\Core\Plugin;
  19. use Cake\Filesystem\Folder;
  20. use Cake\Utility\Inflector;
  21. /**
  22. * Used by CommandCollection and CommandTask to scan the filesystem
  23. * for command classes.
  24. *
  25. * @internal
  26. */
  27. class CommandScanner
  28. {
  29. /**
  30. * Scan CakePHP internals for shells & commands.
  31. *
  32. * @return array A list of command metadata.
  33. */
  34. public function scanCore()
  35. {
  36. $coreShells = $this->scanDir(
  37. dirname(__DIR__) . DIRECTORY_SEPARATOR . 'Shell' . DIRECTORY_SEPARATOR,
  38. 'Cake\Shell\\',
  39. '',
  40. ['command_list']
  41. );
  42. $coreCommands = $this->scanDir(
  43. dirname(__DIR__) . DIRECTORY_SEPARATOR . 'Command' . DIRECTORY_SEPARATOR,
  44. 'Cake\Command\\',
  45. '',
  46. ['command_list']
  47. );
  48. return array_merge($coreShells, $coreCommands);
  49. }
  50. /**
  51. * Scan the application for shells & commands.
  52. *
  53. * @return array A list of command metadata.
  54. */
  55. public function scanApp()
  56. {
  57. $appNamespace = Configure::read('App.namespace');
  58. $appShells = $this->scanDir(
  59. App::path('Shell')[0],
  60. $appNamespace . '\Shell\\',
  61. '',
  62. []
  63. );
  64. $appCommands = $this->scanDir(
  65. App::path('Command')[0],
  66. $appNamespace . '\Command\\',
  67. '',
  68. []
  69. );
  70. return array_merge($appShells, $appCommands);
  71. }
  72. /**
  73. * Scan the named plugin for shells and commands
  74. *
  75. * @param string $plugin The named plugin.
  76. * @return array A list of command metadata.
  77. */
  78. public function scanPlugin($plugin)
  79. {
  80. if (!Plugin::loaded($plugin)) {
  81. return [];
  82. }
  83. $path = Plugin::classPath($plugin);
  84. $namespace = str_replace('/', '\\', $plugin);
  85. $prefix = Inflector::underscore($plugin) . '.';
  86. $commands = $this->scanDir($path . 'Command', $namespace . '\Command\\', $prefix, []);
  87. $shells = $this->scanDir($path . 'Shell', $namespace . '\Shell\\', $prefix, []);
  88. return array_merge($shells, $commands);
  89. }
  90. /**
  91. * Scan a directory for .php files and return the class names that
  92. * should be within them.
  93. *
  94. * @param string $path The directory to read.
  95. * @param string $namespace The namespace the shells live in.
  96. * @param string $prefix The prefix to apply to commands for their full name.
  97. * @param array $hide A list of command names to hide as they are internal commands.
  98. * @return array The list of shell info arrays based on scanning the filesystem and inflection.
  99. */
  100. protected function scanDir($path, $namespace, $prefix, array $hide)
  101. {
  102. $dir = new Folder($path);
  103. $contents = $dir->read(true, true);
  104. if (empty($contents[1])) {
  105. return [];
  106. }
  107. $classPattern = '/(Shell|Command)$/';
  108. $shells = [];
  109. foreach ($contents[1] as $file) {
  110. if (substr($file, -4) !== '.php') {
  111. continue;
  112. }
  113. $shell = substr($file, 0, -4);
  114. if (!preg_match($classPattern, $shell)) {
  115. continue;
  116. }
  117. $name = Inflector::underscore(preg_replace($classPattern, '', $shell));
  118. if (in_array($name, $hide, true)) {
  119. continue;
  120. }
  121. $class = $namespace . $shell;
  122. if (!is_subclass_of($class, Shell::class) && !is_subclass_of($class, Command::class)) {
  123. continue;
  124. }
  125. $shells[] = [
  126. 'file' => $path . $file,
  127. 'fullName' => $prefix . $name,
  128. 'name' => $name,
  129. 'class' => $class
  130. ];
  131. }
  132. return $shells;
  133. }
  134. }