classmap_generator.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Loader
  17. * @subpackage Exception
  18. * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. /**
  22. * Generate class maps for use with autoloading.
  23. *
  24. * Usage:
  25. * --help|-h Get usage message
  26. * --library|-l [ <string> ] Library to parse; if none provided, assumes
  27. * current directory
  28. * --output|-o [ <string> ] Where to write autoload file; if not provided,
  29. * assumes "autoload_classmap.php" in library directory
  30. * --append|-a Append to autoload file if it exists
  31. * --overwrite|-w Whether or not to overwrite existing autoload
  32. * file
  33. * --ignore|-i [ <string> ] Comma-separated namespaces to ignore
  34. */
  35. $libPath = dirname(__FILE__) . '/../library';
  36. if (!is_dir($libPath)) {
  37. // Try to load StandardAutoloader from include_path
  38. if (false === include('Zend/Loader/StandardAutoloader.php')) {
  39. echo "Unable to locate autoloader via include_path; aborting" . PHP_EOL;
  40. exit(2);
  41. }
  42. } else {
  43. // Try to load StandardAutoloader from library
  44. if (false === include(dirname(__FILE__) . '/../library/Zend/Loader/StandardAutoloader.php')) {
  45. echo "Unable to locate autoloader via library; aborting" . PHP_EOL;
  46. exit(2);
  47. }
  48. }
  49. $libraryPath = getcwd();
  50. // Setup autoloading
  51. $loader = new Zend_Loader_StandardAutoloader(array('autoregister_zf' => true));
  52. $loader->setFallbackAutoloader(true);
  53. $loader->register();
  54. $rules = array(
  55. 'help|h' => 'Get usage message',
  56. 'library|l-s' => 'Library to parse; if none provided, assumes current directory',
  57. 'output|o-s' => 'Where to write autoload file; if not provided, assumes "autoload_classmap.php" in library directory',
  58. 'append|a' => 'Append to autoload file if it exists',
  59. 'overwrite|w' => 'Whether or not to overwrite existing autoload file',
  60. 'ignore|i-s' => 'Comma-separated namespaces to ignore',
  61. );
  62. try {
  63. $opts = new Zend_Console_Getopt($rules);
  64. $opts->parse();
  65. } catch (Zend_Console_Getopt_Exception $e) {
  66. echo $e->getUsageMessage();
  67. exit(2);
  68. }
  69. if ($opts->getOption('h')) {
  70. echo $opts->getUsageMessage();
  71. exit(0);
  72. }
  73. $ignoreNamespaces = array();
  74. if (isset($opts->i)) {
  75. $ignoreNamespaces = explode(',', $opts->i);
  76. }
  77. $relativePathForClassmap = '';
  78. if (isset($opts->l)) {
  79. if (!is_dir($opts->l)) {
  80. echo 'Invalid library directory provided' . PHP_EOL
  81. . PHP_EOL;
  82. echo $opts->getUsageMessage();
  83. exit(2);
  84. }
  85. $libraryPath = $opts->l;
  86. }
  87. $libraryPath = str_replace(DIRECTORY_SEPARATOR, '/', realpath($libraryPath));
  88. $usingStdout = false;
  89. $appending = $opts->getOption('a');
  90. $output = $libraryPath . '/autoload_classmap.php';
  91. if (isset($opts->o)) {
  92. $output = $opts->o;
  93. if ('-' == $output) {
  94. $output = STDOUT;
  95. $usingStdout = true;
  96. } elseif (is_dir($output)) {
  97. echo 'Invalid output file provided' . PHP_EOL
  98. . PHP_EOL;
  99. echo $opts->getUsageMessage();
  100. exit(2);
  101. } elseif (!is_writeable(dirname($output))) {
  102. echo "Cannot write to '$output'; aborting." . PHP_EOL
  103. . PHP_EOL
  104. . $opts->getUsageMessage();
  105. exit(2);
  106. } elseif (file_exists($output) && !$opts->getOption('w') && !$appending) {
  107. echo "Autoload file already exists at '$output'," . PHP_EOL
  108. . "but 'overwrite' or 'appending' flag was not specified; aborting." . PHP_EOL
  109. . PHP_EOL
  110. . $opts->getUsageMessage();
  111. exit(2);
  112. } else {
  113. // We need to add the $libraryPath into the relative path that is created in the classmap file.
  114. $classmapPath = str_replace(DIRECTORY_SEPARATOR, '/', realpath(dirname($output)));
  115. // Simple case: $libraryPathCompare is in $classmapPathCompare
  116. if (strpos($libraryPath, $classmapPath) === 0) {
  117. $relativePathForClassmap = substr($libraryPath, strlen($classmapPath) + 1) . '/';
  118. } else {
  119. $libraryPathParts = explode('/', $libraryPath);
  120. $classmapPathParts = explode('/', $classmapPath);
  121. // Find the common part
  122. $count = count($classmapPathParts);
  123. for ($i = 0; $i < $count; $i++) {
  124. if (!isset($libraryPathParts[$i]) || $libraryPathParts[$i] != $classmapPathParts[$i]) {
  125. // Common part end
  126. break;
  127. }
  128. }
  129. // Add parent dirs for the subdirs of classmap
  130. $relativePathForClassmap = str_repeat('../', $count - $i);
  131. // Add library subdirs
  132. $count = count($libraryPathParts);
  133. for (; $i < $count; $i++) {
  134. $relativePathForClassmap .= $libraryPathParts[$i] . '/';
  135. }
  136. }
  137. }
  138. }
  139. if (!$usingStdout) {
  140. if ($appending) {
  141. echo "Appending to class file map '$output' for library in '$libraryPath'..." . PHP_EOL;
  142. } else {
  143. echo "Creating class file map for library in '$libraryPath'..." . PHP_EOL;
  144. }
  145. }
  146. // Get the ClassFileLocator, and pass it the library path
  147. $l = new Zend_File_ClassFileLocator($libraryPath);
  148. // Iterate over each element in the path, and create a map of
  149. // classname => filename, where the filename is relative to the library path
  150. $map = new stdClass;
  151. foreach ($l as $file) {
  152. $filename = str_replace($libraryPath . '/', '', str_replace(DIRECTORY_SEPARATOR, '/', $file->getPath()) . '/' . $file->getFilename());
  153. // Add in relative path to library
  154. $filename = $relativePathForClassmap . $filename;
  155. foreach ($file->getClasses() as $class) {
  156. foreach ($ignoreNamespaces as $ignoreNs) {
  157. if ($ignoreNs == substr($class, 0, strlen($ignoreNs))) {
  158. continue 2;
  159. }
  160. }
  161. $map->{$class} = $filename;
  162. }
  163. }
  164. if ($appending) {
  165. $content = var_export((array) $map, true) . ';';
  166. // Prefix with dirname(__FILE__); modify the generated content
  167. $content = preg_replace("#(=> ')#", "=> dirname(__FILE__) . '/", $content);
  168. // Fix \' strings from injected DIRECTORY_SEPARATOR usage in iterator_apply op
  169. $content = str_replace("\\'", "'", $content);
  170. // Convert to an array and remove the first "array("
  171. $content = explode("\n", $content);
  172. array_shift($content);
  173. // Load existing class map file and remove the closing "bracket ");" from it
  174. $existing = file($output, FILE_IGNORE_NEW_LINES);
  175. array_pop($existing);
  176. // Merge
  177. $content = implode("\n", array_merge($existing, $content));
  178. } else {
  179. // Create a file with the class/file map.
  180. // Stupid syntax highlighters make separating < from PHP declaration necessary
  181. $content = '<' . "?php\n"
  182. . "// Generated by ZF's ./bin/classmap_generator.php\n"
  183. . 'return ' . var_export((array) $map, true) . ';';
  184. // Prefix with dirname(__FILE__); modify the generated content
  185. $content = preg_replace("#(=> ')#", "=> dirname(__FILE__) . '/", $content);
  186. // Fix \' strings from injected DIRECTORY_SEPARATOR usage in iterator_apply op
  187. $content = str_replace("\\'", "'", $content);
  188. }
  189. // Remove unnecessary double-backslashes
  190. $content = str_replace('\\\\', '\\', $content);
  191. // Exchange "array (" width "array("
  192. $content = str_replace('array (', 'array(', $content);
  193. // Align "=>" operators to match coding standard
  194. preg_match_all('(\n\s+([^=]+)=>)', $content, $matches, PREG_SET_ORDER);
  195. $maxWidth = 0;
  196. foreach ($matches as $match) {
  197. $maxWidth = max($maxWidth, strlen($match[1]));
  198. }
  199. $content = preg_replace('(\n\s+([^=]+)=>)e', "'\n \\1' . str_repeat(' ', " . $maxWidth . " - strlen('\\1')) . '=>'", $content);
  200. // Make the file end by EOL
  201. $content = rtrim($content, "\n") . "\n";
  202. // Write the contents to disk
  203. file_put_contents($output, $content);
  204. if (!$usingStdout) {
  205. echo "Wrote classmap file to '" . realpath($output) . "'" . PHP_EOL;
  206. }