PageRenderTime 26ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/phpunit/php-token-stream/src/Token/Stream.php

https://gitlab.com/Laolballs/cbtapp
PHP | 594 lines | 364 code | 81 blank | 149 comment | 48 complexity | 922d0ea60d7eddeaab33a7a3593372da MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the PHP_TokenStream package.
  4. *
  5. * (c) Sebastian Bergmann <sebastian@phpunit.de>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. /**
  11. * A stream of PHP tokens.
  12. *
  13. * @author Sebastian Bergmann <sebastian@phpunit.de>
  14. * @copyright Sebastian Bergmann <sebastian@phpunit.de>
  15. * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License
  16. * @version Release: @package_version@
  17. * @link http://github.com/sebastianbergmann/php-token-stream/tree
  18. * @since Class available since Release 1.0.0
  19. */
  20. class PHP_Token_Stream implements ArrayAccess, Countable, SeekableIterator
  21. {
  22. /**
  23. * @var array
  24. */
  25. protected static $customTokens = array(
  26. '(' => 'PHP_Token_OPEN_BRACKET',
  27. ')' => 'PHP_Token_CLOSE_BRACKET',
  28. '[' => 'PHP_Token_OPEN_SQUARE',
  29. ']' => 'PHP_Token_CLOSE_SQUARE',
  30. '{' => 'PHP_Token_OPEN_CURLY',
  31. '}' => 'PHP_Token_CLOSE_CURLY',
  32. ';' => 'PHP_Token_SEMICOLON',
  33. '.' => 'PHP_Token_DOT',
  34. ',' => 'PHP_Token_COMMA',
  35. '=' => 'PHP_Token_EQUAL',
  36. '<' => 'PHP_Token_LT',
  37. '>' => 'PHP_Token_GT',
  38. '+' => 'PHP_Token_PLUS',
  39. '-' => 'PHP_Token_MINUS',
  40. '*' => 'PHP_Token_MULT',
  41. '/' => 'PHP_Token_DIV',
  42. '?' => 'PHP_Token_QUESTION_MARK',
  43. '!' => 'PHP_Token_EXCLAMATION_MARK',
  44. ':' => 'PHP_Token_COLON',
  45. '"' => 'PHP_Token_DOUBLE_QUOTES',
  46. '@' => 'PHP_Token_AT',
  47. '&' => 'PHP_Token_AMPERSAND',
  48. '%' => 'PHP_Token_PERCENT',
  49. '|' => 'PHP_Token_PIPE',
  50. '$' => 'PHP_Token_DOLLAR',
  51. '^' => 'PHP_Token_CARET',
  52. '~' => 'PHP_Token_TILDE',
  53. '`' => 'PHP_Token_BACKTICK'
  54. );
  55. /**
  56. * @var string
  57. */
  58. protected $filename;
  59. /**
  60. * @var array
  61. */
  62. protected $tokens = array();
  63. /**
  64. * @var integer
  65. */
  66. protected $position = 0;
  67. /**
  68. * @var array
  69. */
  70. protected $linesOfCode = array('loc' => 0, 'cloc' => 0, 'ncloc' => 0);
  71. /**
  72. * @var array
  73. */
  74. protected $classes;
  75. /**
  76. * @var array
  77. */
  78. protected $functions;
  79. /**
  80. * @var array
  81. */
  82. protected $includes;
  83. /**
  84. * @var array
  85. */
  86. protected $interfaces;
  87. /**
  88. * @var array
  89. */
  90. protected $traits;
  91. /**
  92. * @var array
  93. */
  94. protected $lineToFunctionMap = array();
  95. /**
  96. * Constructor.
  97. *
  98. * @param string $sourceCode
  99. */
  100. public function __construct($sourceCode)
  101. {
  102. if (is_file($sourceCode)) {
  103. $this->filename = $sourceCode;
  104. $sourceCode = file_get_contents($sourceCode);
  105. }
  106. $this->scan($sourceCode);
  107. }
  108. /**
  109. * Destructor.
  110. */
  111. public function __destruct()
  112. {
  113. $this->tokens = array();
  114. }
  115. /**
  116. * @return string
  117. */
  118. public function __toString()
  119. {
  120. $buffer = '';
  121. foreach ($this as $token) {
  122. $buffer .= $token;
  123. }
  124. return $buffer;
  125. }
  126. /**
  127. * @return string
  128. * @since Method available since Release 1.1.0
  129. */
  130. public function getFilename()
  131. {
  132. return $this->filename;
  133. }
  134. /**
  135. * Scans the source for sequences of characters and converts them into a
  136. * stream of tokens.
  137. *
  138. * @param string $sourceCode
  139. */
  140. protected function scan($sourceCode)
  141. {
  142. $line = 1;
  143. $tokens = token_get_all($sourceCode);
  144. $numTokens = count($tokens);
  145. $lastNonWhitespaceTokenWasDoubleColon = FALSE;
  146. for ($i = 0; $i < $numTokens; ++$i) {
  147. $token = $tokens[$i];
  148. unset($tokens[$i]);
  149. if (is_array($token)) {
  150. $name = substr(token_name($token[0]), 2);
  151. $text = $token[1];
  152. if ($lastNonWhitespaceTokenWasDoubleColon && $name == 'CLASS') {
  153. $name = 'CLASS_NAME_CONSTANT';
  154. }
  155. $tokenClass = 'PHP_Token_' . $name;
  156. } else {
  157. $text = $token;
  158. $tokenClass = self::$customTokens[$token];
  159. }
  160. $this->tokens[] = new $tokenClass($text, $line, $this, $i);
  161. $lines = substr_count($text, "\n");
  162. $line += $lines;
  163. if ($tokenClass == 'PHP_Token_HALT_COMPILER') {
  164. break;
  165. }
  166. else if ($tokenClass == 'PHP_Token_COMMENT' ||
  167. $tokenClass == 'PHP_Token_DOC_COMMENT') {
  168. $this->linesOfCode['cloc'] += $lines + 1;
  169. }
  170. if ($name == 'DOUBLE_COLON') {
  171. $lastNonWhitespaceTokenWasDoubleColon = TRUE;
  172. }
  173. else if ($name != 'WHITESPACE') {
  174. $lastNonWhitespaceTokenWasDoubleColon = FALSE;
  175. }
  176. }
  177. $this->linesOfCode['loc'] = substr_count($sourceCode, "\n");
  178. $this->linesOfCode['ncloc'] = $this->linesOfCode['loc'] -
  179. $this->linesOfCode['cloc'];
  180. }
  181. /**
  182. * @return integer
  183. */
  184. public function count()
  185. {
  186. return count($this->tokens);
  187. }
  188. /**
  189. * @return PHP_Token[]
  190. */
  191. public function tokens()
  192. {
  193. return $this->tokens;
  194. }
  195. /**
  196. * @return array
  197. */
  198. public function getClasses()
  199. {
  200. if ($this->classes !== NULL) {
  201. return $this->classes;
  202. }
  203. $this->parse();
  204. return $this->classes;
  205. }
  206. /**
  207. * @return array
  208. */
  209. public function getFunctions()
  210. {
  211. if ($this->functions !== NULL) {
  212. return $this->functions;
  213. }
  214. $this->parse();
  215. return $this->functions;
  216. }
  217. /**
  218. * @return array
  219. */
  220. public function getInterfaces()
  221. {
  222. if ($this->interfaces !== NULL) {
  223. return $this->interfaces;
  224. }
  225. $this->parse();
  226. return $this->interfaces;
  227. }
  228. /**
  229. * @return array
  230. * @since Method available since Release 1.1.0
  231. */
  232. public function getTraits()
  233. {
  234. if ($this->traits !== NULL) {
  235. return $this->traits;
  236. }
  237. $this->parse();
  238. return $this->traits;
  239. }
  240. /**
  241. * Gets the names of all files that have been included
  242. * using include(), include_once(), require() or require_once().
  243. *
  244. * Parameter $categorize set to TRUE causing this function to return a
  245. * multi-dimensional array with categories in the keys of the first dimension
  246. * and constants and their values in the second dimension.
  247. *
  248. * Parameter $category allow to filter following specific inclusion type
  249. *
  250. * @param bool $categorize OPTIONAL
  251. * @param string $category OPTIONAL Either 'require_once', 'require',
  252. * 'include_once', 'include'.
  253. * @return array
  254. * @since Method available since Release 1.1.0
  255. */
  256. public function getIncludes($categorize = FALSE, $category = NULL)
  257. {
  258. if ($this->includes === NULL) {
  259. $this->includes = array(
  260. 'require_once' => array(),
  261. 'require' => array(),
  262. 'include_once' => array(),
  263. 'include' => array()
  264. );
  265. foreach ($this->tokens as $token) {
  266. switch (get_class($token)) {
  267. case 'PHP_Token_REQUIRE_ONCE':
  268. case 'PHP_Token_REQUIRE':
  269. case 'PHP_Token_INCLUDE_ONCE':
  270. case 'PHP_Token_INCLUDE': {
  271. $this->includes[$token->getType()][] = $token->getName();
  272. }
  273. break;
  274. }
  275. }
  276. }
  277. if (isset($this->includes[$category])) {
  278. $includes = $this->includes[$category];
  279. }
  280. else if ($categorize === FALSE) {
  281. $includes = array_merge(
  282. $this->includes['require_once'],
  283. $this->includes['require'],
  284. $this->includes['include_once'],
  285. $this->includes['include']
  286. );
  287. } else {
  288. $includes = $this->includes;
  289. }
  290. return $includes;
  291. }
  292. /**
  293. * Returns the name of the function or method a line belongs to.
  294. *
  295. * @return string or null if the line is not in a function or method
  296. * @since Method available since Release 1.2.0
  297. */
  298. public function getFunctionForLine($line)
  299. {
  300. $this->parse();
  301. if (isset($this->lineToFunctionMap[$line])) {
  302. return $this->lineToFunctionMap[$line];
  303. }
  304. }
  305. protected function parse()
  306. {
  307. $this->interfaces = array();
  308. $this->classes = array();
  309. $this->traits = array();
  310. $this->functions = array();
  311. $class = FALSE;
  312. $classEndLine = FALSE;
  313. $trait = FALSE;
  314. $traitEndLine = FALSE;
  315. $interface = FALSE;
  316. $interfaceEndLine = FALSE;
  317. foreach ($this->tokens as $token) {
  318. switch (get_class($token)) {
  319. case 'PHP_Token_HALT_COMPILER': {
  320. return;
  321. }
  322. break;
  323. case 'PHP_Token_INTERFACE': {
  324. $interface = $token->getName();
  325. $interfaceEndLine = $token->getEndLine();
  326. $this->interfaces[$interface] = array(
  327. 'methods' => array(),
  328. 'parent' => $token->getParent(),
  329. 'keywords' => $token->getKeywords(),
  330. 'docblock' => $token->getDocblock(),
  331. 'startLine' => $token->getLine(),
  332. 'endLine' => $interfaceEndLine,
  333. 'package' => $token->getPackage(),
  334. 'file' => $this->filename
  335. );
  336. }
  337. break;
  338. case 'PHP_Token_CLASS':
  339. case 'PHP_Token_TRAIT': {
  340. $tmp = array(
  341. 'methods' => array(),
  342. 'parent' => $token->getParent(),
  343. 'interfaces'=> $token->getInterfaces(),
  344. 'keywords' => $token->getKeywords(),
  345. 'docblock' => $token->getDocblock(),
  346. 'startLine' => $token->getLine(),
  347. 'endLine' => $token->getEndLine(),
  348. 'package' => $token->getPackage(),
  349. 'file' => $this->filename
  350. );
  351. if ($token instanceof PHP_Token_CLASS) {
  352. $class = $token->getName();
  353. $classEndLine = $token->getEndLine();
  354. $this->classes[$class] = $tmp;
  355. } else {
  356. $trait = $token->getName();
  357. $traitEndLine = $token->getEndLine();
  358. $this->traits[$trait] = $tmp;
  359. }
  360. }
  361. break;
  362. case 'PHP_Token_FUNCTION': {
  363. $name = $token->getName();
  364. $tmp = array(
  365. 'docblock' => $token->getDocblock(),
  366. 'keywords' => $token->getKeywords(),
  367. 'visibility'=> $token->getVisibility(),
  368. 'signature' => $token->getSignature(),
  369. 'startLine' => $token->getLine(),
  370. 'endLine' => $token->getEndLine(),
  371. 'ccn' => $token->getCCN(),
  372. 'file' => $this->filename
  373. );
  374. if ($class === FALSE &&
  375. $trait === FALSE &&
  376. $interface === FALSE) {
  377. $this->functions[$name] = $tmp;
  378. $this->addFunctionToMap(
  379. $name, $tmp['startLine'], $tmp['endLine']
  380. );
  381. }
  382. else if ($class !== FALSE) {
  383. $this->classes[$class]['methods'][$name] = $tmp;
  384. $this->addFunctionToMap(
  385. $class . '::' . $name,
  386. $tmp['startLine'],
  387. $tmp['endLine']
  388. );
  389. }
  390. else if ($trait !== FALSE) {
  391. $this->traits[$trait]['methods'][$name] = $tmp;
  392. $this->addFunctionToMap(
  393. $trait . '::' . $name,
  394. $tmp['startLine'],
  395. $tmp['endLine']
  396. );
  397. }
  398. else {
  399. $this->interfaces[$interface]['methods'][$name] = $tmp;
  400. }
  401. }
  402. break;
  403. case 'PHP_Token_CLOSE_CURLY': {
  404. if ($classEndLine !== FALSE &&
  405. $classEndLine == $token->getLine()) {
  406. $class = FALSE;
  407. $classEndLine = FALSE;
  408. }
  409. else if ($traitEndLine !== FALSE &&
  410. $traitEndLine == $token->getLine()) {
  411. $trait = FALSE;
  412. $traitEndLine = FALSE;
  413. }
  414. else if ($interfaceEndLine !== FALSE &&
  415. $interfaceEndLine == $token->getLine()) {
  416. $interface = FALSE;
  417. $interfaceEndLine = FALSE;
  418. }
  419. }
  420. break;
  421. }
  422. }
  423. }
  424. /**
  425. * @return array
  426. */
  427. public function getLinesOfCode()
  428. {
  429. return $this->linesOfCode;
  430. }
  431. /**
  432. */
  433. public function rewind()
  434. {
  435. $this->position = 0;
  436. }
  437. /**
  438. * @return boolean
  439. */
  440. public function valid()
  441. {
  442. return isset($this->tokens[$this->position]);
  443. }
  444. /**
  445. * @return integer
  446. */
  447. public function key()
  448. {
  449. return $this->position;
  450. }
  451. /**
  452. * @return PHP_Token
  453. */
  454. public function current()
  455. {
  456. return $this->tokens[$this->position];
  457. }
  458. /**
  459. */
  460. public function next()
  461. {
  462. $this->position++;
  463. }
  464. /**
  465. * @param mixed $offset
  466. */
  467. public function offsetExists($offset)
  468. {
  469. return isset($this->tokens[$offset]);
  470. }
  471. /**
  472. * @param mixed $offset
  473. * @return mixed
  474. */
  475. public function offsetGet($offset)
  476. {
  477. return $this->tokens[$offset];
  478. }
  479. /**
  480. * @param mixed $offset
  481. * @param mixed $value
  482. */
  483. public function offsetSet($offset, $value)
  484. {
  485. $this->tokens[$offset] = $value;
  486. }
  487. /**
  488. * @param mixed $offset
  489. */
  490. public function offsetUnset($offset)
  491. {
  492. unset($this->tokens[$offset]);
  493. }
  494. /**
  495. * Seek to an absolute position.
  496. *
  497. * @param integer $position
  498. * @throws OutOfBoundsException
  499. */
  500. public function seek($position)
  501. {
  502. $this->position = $position;
  503. if (!$this->valid()) {
  504. throw new OutOfBoundsException('Invalid seek position');
  505. }
  506. }
  507. private function addFunctionToMap($name, $startLine, $endLine)
  508. {
  509. for ($line = $startLine; $line <= $endLine; $line++) {
  510. $this->lineToFunctionMap[$line] = $name;
  511. }
  512. }
  513. }