PageRenderTime 26ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Reflection/ReflectionFile.php

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