PageRenderTime 57ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/foody.blogtamsudev.vn/vendor/phpunit/php-token-stream/src/Token/Stream.php

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