2
0

File.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  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_Reflection
  17. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. /**
  22. * @see Zend_Reflection_Class
  23. */
  24. require_once 'Zend/Reflection/Class.php';
  25. /**
  26. * @see Zend_Reflection_Function
  27. */
  28. require_once 'Zend/Reflection/Function.php';
  29. /**
  30. * @category Zend
  31. * @package Zend_Reflection
  32. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  33. * @license http://framework.zend.com/license/new-bsd New BSD License
  34. */
  35. class Zend_Reflection_File implements Reflector
  36. {
  37. /**
  38. * @var string
  39. */
  40. protected $_filepath = null;
  41. /**
  42. * @var string
  43. */
  44. protected $_docComment = null;
  45. /**
  46. * @var int
  47. */
  48. protected $_startLine = 1;
  49. /**
  50. * @var int
  51. */
  52. protected $_endLine = null;
  53. /**
  54. * @var string[]
  55. */
  56. protected $_requiredFiles = array();
  57. /**
  58. * @var Zend_Reflection_Class[]
  59. */
  60. protected $_classes = array();
  61. /**
  62. * @var Zend_Reflection_Function[]
  63. */
  64. protected $_functions = array();
  65. /**
  66. * @var string
  67. */
  68. protected $_contents = null;
  69. /**
  70. * Constructor
  71. *
  72. * @param string $file
  73. * @return void
  74. */
  75. public function __construct($file)
  76. {
  77. $fileName = $file;
  78. $fileRealpath = realpath($fileName);
  79. if ($fileRealpath) {
  80. // realpath() doesn't return false if Suhosin is included
  81. // see http://uk3.php.net/manual/en/function.realpath.php#82770
  82. if (!file_exists($fileRealpath)) {
  83. $fileRealpath = false;
  84. }
  85. }
  86. if ($fileRealpath === false) {
  87. $fileRealpath = self::findRealpathInIncludePath($file);
  88. }
  89. if (!$fileRealpath || !in_array($fileRealpath, get_included_files())) {
  90. require_once 'Zend/Reflection/Exception.php';
  91. throw new Zend_Reflection_Exception('File ' . $file . ' must be required before it can be reflected');
  92. }
  93. $this->_fileName = $fileRealpath;
  94. $this->_contents = file_get_contents($this->_fileName);
  95. $this->_reflect();
  96. }
  97. /**
  98. * Find realpath of file based on include_path
  99. *
  100. * @param string $fileName
  101. * @return string
  102. */
  103. public static function findRealpathInIncludePath($fileName)
  104. {
  105. require_once 'Zend/Loader.php';
  106. $includePaths = Zend_Loader::explodeIncludePath();
  107. while (count($includePaths) > 0) {
  108. $filePath = array_shift($includePaths) . DIRECTORY_SEPARATOR . $fileName;
  109. if ( ($foundRealpath = realpath($filePath)) !== false) {
  110. break;
  111. }
  112. }
  113. return $foundRealpath;
  114. }
  115. /**
  116. * Export
  117. *
  118. * Required by the Reflector interface.
  119. *
  120. * @todo What should this do?
  121. * @return null
  122. */
  123. public static function export()
  124. {
  125. return null;
  126. }
  127. /**
  128. * Return the file name of the reflected file
  129. *
  130. * @return string
  131. */
  132. public function getFileName()
  133. {
  134. return $this->_fileName;
  135. }
  136. /**
  137. * Get the start line - Always 1, staying consistent with the Reflection API
  138. *
  139. * @return int
  140. */
  141. public function getStartLine()
  142. {
  143. return $this->_startLine;
  144. }
  145. /**
  146. * Get the end line / number of lines
  147. *
  148. * @return int
  149. */
  150. public function getEndLine()
  151. {
  152. return $this->_endLine;
  153. }
  154. /**
  155. * Return the doc comment
  156. *
  157. * @return string
  158. */
  159. public function getDocComment()
  160. {
  161. return $this->_docComment;
  162. }
  163. /**
  164. * Return the docblock
  165. *
  166. * @param string $reflectionClass Reflection class to use
  167. * @return Zend_Reflection_Docblock
  168. */
  169. public function getDocblock($reflectionClass = 'Zend_Reflection_Docblock')
  170. {
  171. $instance = new $reflectionClass($this);
  172. if (!$instance instanceof Zend_Reflection_Docblock) {
  173. require_once 'Zend/Reflection/Exception.php';
  174. throw new Zend_Reflection_Exception('Invalid reflection class specified; must extend Zend_Reflection_Docblock');
  175. }
  176. return $instance;
  177. }
  178. /**
  179. * Return the reflection classes of the classes found inside this file
  180. *
  181. * @param string $reflectionClass Name of reflection class to use for instances
  182. * @return array Array of Zend_Reflection_Class instances
  183. */
  184. public function getClasses($reflectionClass = 'Zend_Reflection_Class')
  185. {
  186. $classes = array();
  187. foreach ($this->_classes as $class) {
  188. $instance = new $reflectionClass($class);
  189. if (!$instance instanceof Zend_Reflection_Class) {
  190. require_once 'Zend/Reflection/Exception.php';
  191. throw new Zend_Reflection_Exception('Invalid reflection class provided; must extend Zend_Reflection_Class');
  192. }
  193. $classes[] = $instance;
  194. }
  195. return $classes;
  196. }
  197. /**
  198. * Return the reflection functions of the functions found inside this file
  199. *
  200. * @param string $reflectionClass Name of reflection class to use for instances
  201. * @return array Array of Zend_Reflection_Functions
  202. */
  203. public function getFunctions($reflectionClass = 'Zend_Reflection_Function')
  204. {
  205. $functions = array();
  206. foreach ($this->_functions as $function) {
  207. $instance = new $reflectionClass($function);
  208. if (!$instance instanceof Zend_Reflection_Function) {
  209. require_once 'Zend/Reflection/Exception.php';
  210. throw new Zend_Reflection_Exception('Invalid reflection class provided; must extend Zend_Reflection_Function');
  211. }
  212. $functions[] = $instance;
  213. }
  214. return $functions;
  215. }
  216. /**
  217. * Retrieve the reflection class of a given class found in this file
  218. *
  219. * @param null|string $name
  220. * @param string $reflectionClass Reflection class to use when creating reflection instance
  221. * @return Zend_Reflection_Class
  222. * @throws Zend_Reflection_Exception for invalid class name or invalid reflection class
  223. */
  224. public function getClass($name = null, $reflectionClass = 'Zend_Reflection_Class')
  225. {
  226. if ($name === null) {
  227. reset($this->_classes);
  228. $selected = current($this->_classes);
  229. $instance = new $reflectionClass($selected);
  230. if (!$instance instanceof Zend_Reflection_Class) {
  231. require_once 'Zend/Reflection/Exception.php';
  232. throw new Zend_Reflection_Exception('Invalid reflection class given; must extend Zend_Reflection_Class');
  233. }
  234. return $instance;
  235. }
  236. if (in_array($name, $this->_classes)) {
  237. $instance = new $reflectionClass($name);
  238. if (!$instance instanceof Zend_Reflection_Class) {
  239. require_once 'Zend/Reflection/Exception.php';
  240. throw new Zend_Reflection_Exception('Invalid reflection class given; must extend Zend_Reflection_Class');
  241. }
  242. return $instance;
  243. }
  244. require_once 'Zend/Reflection/Exception.php';
  245. throw new Zend_Reflection_Exception('Class by name ' . $name . ' not found.');
  246. }
  247. /**
  248. * Return the full contents of file
  249. *
  250. * @return string
  251. */
  252. public function getContents()
  253. {
  254. return $this->_contents;
  255. }
  256. /**
  257. * Serialize to string
  258. *
  259. * Required by the Reflector interface
  260. *
  261. * @todo What should this serialization look like?
  262. * @return string
  263. */
  264. public function __toString()
  265. {
  266. return '';
  267. }
  268. /**
  269. * This method does the work of "reflecting" the file
  270. *
  271. * Uses PHP's tokenizer to perform file reflection.
  272. *
  273. * @return void
  274. */
  275. protected function _reflect()
  276. {
  277. $contents = $this->_contents;
  278. $tokens = token_get_all($contents);
  279. $functionTrapped = false;
  280. $classTrapped = false;
  281. $requireTrapped = false;
  282. $openBraces = 0;
  283. $this->_checkFileDocBlock($tokens);
  284. foreach ($tokens as $token) {
  285. /*
  286. * Tokens are characters representing symbols or arrays
  287. * representing strings. The keys/values in the arrays are
  288. *
  289. * - 0 => token id,
  290. * - 1 => string,
  291. * - 2 => line number
  292. *
  293. * Token ID's are explained here:
  294. * http://www.php.net/manual/en/tokens.php.
  295. */
  296. if (is_array($token)) {
  297. $type = $token[0];
  298. $value = $token[1];
  299. $lineNum = $token[2];
  300. } else {
  301. // It's a symbol
  302. // Maintain the count of open braces
  303. if ($token == '{') {
  304. $openBraces++;
  305. } else if ($token == '}') {
  306. $openBraces--;
  307. }
  308. continue;
  309. }
  310. switch ($type) {
  311. // Name of something
  312. case T_STRING:
  313. if ($functionTrapped) {
  314. $this->_functions[] = $value;
  315. $functionTrapped = false;
  316. } elseif ($classTrapped) {
  317. $this->_classes[] = $value;
  318. $classTrapped = false;
  319. }
  320. continue;
  321. // Required file names are T_CONSTANT_ENCAPSED_STRING
  322. case T_CONSTANT_ENCAPSED_STRING:
  323. if ($requireTrapped) {
  324. $this->_requiredFiles[] = $value ."\n";
  325. $requireTrapped = false;
  326. }
  327. continue;
  328. // Functions
  329. case T_FUNCTION:
  330. if ($openBraces == 0) {
  331. $functionTrapped = true;
  332. }
  333. break;
  334. // Classes
  335. case T_CLASS:
  336. case T_INTERFACE:
  337. $classTrapped = true;
  338. break;
  339. // All types of requires
  340. case T_REQUIRE:
  341. case T_REQUIRE_ONCE:
  342. case T_INCLUDE:
  343. case T_INCLUDE_ONCE:
  344. $requireTrapped = true;
  345. break;
  346. // Default case: do nothing
  347. default:
  348. break;
  349. }
  350. }
  351. $this->_endLine = count(explode("\n", $this->_contents));
  352. }
  353. /**
  354. * Validate / check a file level docblock
  355. *
  356. * @param array $tokens Array of tokenizer tokens
  357. * @return void
  358. */
  359. protected function _checkFileDocBlock($tokens) {
  360. foreach ($tokens as $token) {
  361. $type = $token[0];
  362. $value = $token[1];
  363. $lineNum = $token[2];
  364. if(($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) {
  365. continue;
  366. } elseif ($type == T_DOC_COMMENT) {
  367. $this->_docComment = $value;
  368. $this->_startLine = $lineNum + substr_count($value, "\n") + 1;
  369. return;
  370. } else {
  371. // Only whitespace is allowed before file docblocks
  372. return;
  373. }
  374. }
  375. }
  376. }