PageRenderTime 41ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/Reflection/File.php

https://github.com/zf/ZendFramework-Library
PHP | 432 lines | 217 code | 46 blank | 169 comment | 34 complexity | 081e861cf9f61b43b818befb273110f7 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-2015 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-2015 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. $embeddedVariableTrapped = false;
  283. $openBraces = 0;
  284. $this->_checkFileDocBlock($tokens);
  285. foreach ($tokens as $token) {
  286. /*
  287. * Tokens are characters representing symbols or arrays
  288. * representing strings. The keys/values in the arrays are
  289. *
  290. * - 0 => token id,
  291. * - 1 => string,
  292. * - 2 => line number
  293. *
  294. * Token ID's are explained here:
  295. * http://www.php.net/manual/en/tokens.php.
  296. */
  297. if (is_array($token)) {
  298. $type = $token[0];
  299. $value = $token[1];
  300. $lineNum = $token[2];
  301. } else {
  302. // It's a symbol
  303. // Maintain the count of open braces
  304. if ($token == '{') {
  305. $openBraces++;
  306. } else if ($token == '}') {
  307. if ( $embeddedVariableTrapped ) {
  308. $embeddedVariableTrapped = false;
  309. } else {
  310. $openBraces--;
  311. }
  312. }
  313. continue;
  314. }
  315. switch ($type) {
  316. case T_STRING_VARNAME:
  317. case T_DOLLAR_OPEN_CURLY_BRACES:
  318. case T_CURLY_OPEN:
  319. $embeddedVariableTrapped = true;
  320. continue;
  321. // Name of something
  322. case T_STRING:
  323. if ($functionTrapped) {
  324. $this->_functions[] = $value;
  325. $functionTrapped = false;
  326. } elseif ($classTrapped) {
  327. $this->_classes[] = $value;
  328. $classTrapped = false;
  329. }
  330. continue;
  331. // Required file names are T_CONSTANT_ENCAPSED_STRING
  332. case T_CONSTANT_ENCAPSED_STRING:
  333. if ($requireTrapped) {
  334. $this->_requiredFiles[] = $value ."\n";
  335. $requireTrapped = false;
  336. }
  337. continue;
  338. // Functions
  339. case T_FUNCTION:
  340. if ($openBraces == 0) {
  341. $functionTrapped = true;
  342. }
  343. break;
  344. // Classes
  345. case T_CLASS:
  346. case T_INTERFACE:
  347. $classTrapped = true;
  348. break;
  349. // All types of requires
  350. case T_REQUIRE:
  351. case T_REQUIRE_ONCE:
  352. case T_INCLUDE:
  353. case T_INCLUDE_ONCE:
  354. $requireTrapped = true;
  355. break;
  356. // Default case: do nothing
  357. default:
  358. break;
  359. }
  360. }
  361. $this->_endLine = count(explode("\n", $this->_contents));
  362. }
  363. /**
  364. * Validate / check a file level docblock
  365. *
  366. * @param array $tokens Array of tokenizer tokens
  367. * @return void
  368. */
  369. protected function _checkFileDocBlock($tokens) {
  370. foreach ($tokens as $token) {
  371. $type = $token[0];
  372. $value = $token[1];
  373. $lineNum = $token[2];
  374. if(($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) {
  375. continue;
  376. } elseif ($type == T_DOC_COMMENT) {
  377. $this->_docComment = $value;
  378. $this->_startLine = $lineNum + substr_count($value, "\n") + 1;
  379. return;
  380. } else {
  381. // Only whitespace is allowed before file docblocks
  382. return;
  383. }
  384. }
  385. }
  386. }