ReflectionClosure.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  1. <?php
  2. /* ===========================================================================
  3. * Copyright (c) 2018-2019 Zindex Software
  4. *
  5. * Licensed under the MIT License
  6. * =========================================================================== */
  7. namespace Opis\Closure;
  8. use Closure;
  9. use ReflectionFunction;
  10. class ReflectionClosure extends ReflectionFunction
  11. {
  12. protected $code;
  13. protected $tokens;
  14. protected $hashedName;
  15. protected $useVariables;
  16. protected $isStaticClosure;
  17. protected $isScopeRequired;
  18. protected $isBindingRequired;
  19. protected static $files = array();
  20. protected static $classes = array();
  21. protected static $functions = array();
  22. protected static $constants = array();
  23. protected static $structures = array();
  24. /**
  25. * ReflectionClosure constructor.
  26. * @param Closure $closure
  27. * @param string|null $code
  28. * @throws \ReflectionException
  29. */
  30. public function __construct(Closure $closure, $code = null)
  31. {
  32. $this->code = $code;
  33. parent::__construct($closure);
  34. }
  35. /**
  36. * @return bool
  37. */
  38. public function isStatic()
  39. {
  40. if ($this->isStaticClosure === null) {
  41. $this->isStaticClosure = strtolower(substr($this->getCode(), 0, 6)) === 'static';
  42. }
  43. return $this->isStaticClosure;
  44. }
  45. /**
  46. * @return string
  47. */
  48. public function getCode()
  49. {
  50. if($this->code !== null){
  51. return $this->code;
  52. }
  53. $fileName = $this->getFileName();
  54. $line = $this->getStartLine() - 1;
  55. $match = ClosureStream::STREAM_PROTO . '://';
  56. if ($line === 1 && substr($fileName, 0, strlen($match)) === $match) {
  57. return $this->code = substr($fileName, strlen($match));
  58. }
  59. $className = null;
  60. if (null !== $className = $this->getClosureScopeClass()) {
  61. $className = '\\' . trim($className->getName(), '\\');
  62. }
  63. if($php7 = PHP_MAJOR_VERSION === 7){
  64. switch (PHP_MINOR_VERSION){
  65. case 0:
  66. $php7_types = array('string', 'int', 'bool', 'float');
  67. break;
  68. case 1:
  69. $php7_types = array('string', 'int', 'bool', 'float', 'void');
  70. break;
  71. case 2:
  72. default:
  73. $php7_types = array('string', 'int', 'bool', 'float', 'void', 'object');
  74. }
  75. }
  76. $ns = $this->getNamespaceName();
  77. $nsf = $ns == '' ? '' : ($ns[0] == '\\' ? $ns : '\\' . $ns);
  78. $_file = var_export($fileName, true);
  79. $_dir = var_export(dirname($fileName), true);
  80. $_namespace = var_export($ns, true);
  81. $_class = var_export(trim($className, '\\'), true);
  82. $_function = $ns . ($ns == '' ? '' : '\\') . '{closure}';
  83. $_method = ($className == '' ? '' : trim($className, '\\') . '::') . $_function;
  84. $_function = var_export($_function, true);
  85. $_method = var_export($_method, true);
  86. $_trait = null;
  87. $tokens = $this->getTokens();
  88. $state = $lastState = 'start';
  89. $inside_anonymous = false;
  90. $anonymous_mark = 0;
  91. $open = 0;
  92. $code = '';
  93. $id_start = $id_start_ci = $id_name = $context = '';
  94. $classes = $functions = $constants = null;
  95. $use = array();
  96. $lineAdd = 0;
  97. $isUsingScope = false;
  98. $isUsingThisObject = false;
  99. for($i = 0, $l = count($tokens); $i < $l; $i++) {
  100. $token = $tokens[$i];
  101. switch ($state) {
  102. case 'start':
  103. if ($token[0] === T_FUNCTION || $token[0] === T_STATIC) {
  104. $code .= $token[1];
  105. $state = $token[0] === T_FUNCTION ? 'function' : 'static';
  106. }
  107. break;
  108. case 'static':
  109. if ($token[0] === T_WHITESPACE || $token[0] === T_COMMENT || $token[0] === T_FUNCTION) {
  110. $code .= $token[1];
  111. if ($token[0] === T_FUNCTION) {
  112. $state = 'function';
  113. }
  114. } else {
  115. $code = '';
  116. $state = 'start';
  117. }
  118. break;
  119. case 'function':
  120. switch ($token[0]){
  121. case T_STRING:
  122. $code = '';
  123. $state = 'named_function';
  124. break;
  125. case '(':
  126. $code .= '(';
  127. $state = 'closure_args';
  128. break;
  129. default:
  130. $code .= is_array($token) ? $token[1] : $token;
  131. }
  132. break;
  133. case 'named_function':
  134. if($token[0] === T_FUNCTION || $token[0] === T_STATIC){
  135. $code = $token[1];
  136. $state = $token[0] === T_FUNCTION ? 'function' : 'static';
  137. }
  138. break;
  139. case 'closure_args':
  140. switch ($token[0]){
  141. case T_NS_SEPARATOR:
  142. case T_STRING:
  143. $id_start = $token[1];
  144. $id_start_ci = strtolower($id_start);
  145. $id_name = '';
  146. $context = 'args';
  147. $state = 'id_name';
  148. $lastState = 'closure_args';
  149. break;
  150. case T_USE:
  151. $code .= $token[1];
  152. $state = 'use';
  153. break;
  154. case '=':
  155. $code .= $token;
  156. $lastState = 'closure_args';
  157. $state = 'ignore_next';
  158. break;
  159. case ':':
  160. $code .= ':';
  161. $state = 'return';
  162. break;
  163. case '{':
  164. $code .= '{';
  165. $state = 'closure';
  166. $open++;
  167. break;
  168. default:
  169. $code .= is_array($token) ? $token[1] : $token;
  170. }
  171. break;
  172. case 'use':
  173. switch ($token[0]){
  174. case T_VARIABLE:
  175. $use[] = substr($token[1], 1);
  176. $code .= $token[1];
  177. break;
  178. case '{':
  179. $code .= '{';
  180. $state = 'closure';
  181. $open++;
  182. break;
  183. case ':':
  184. $code .= ':';
  185. $state = 'return';
  186. break;
  187. default:
  188. $code .= is_array($token) ? $token[1] : $token;
  189. break;
  190. }
  191. break;
  192. case 'return':
  193. switch ($token[0]){
  194. case T_WHITESPACE:
  195. case T_COMMENT:
  196. case T_DOC_COMMENT:
  197. $code .= $token[1];
  198. break;
  199. case T_NS_SEPARATOR:
  200. case T_STRING:
  201. $id_start = $token[1];
  202. $id_start_ci = strtolower($id_start);
  203. $id_name = '';
  204. $context = 'return_type';
  205. $state = 'id_name';
  206. $lastState = 'return';
  207. break 2;
  208. case '{':
  209. $code .= '{';
  210. $state = 'closure';
  211. $open++;
  212. break;
  213. default:
  214. $code .= is_array($token) ? $token[1] : $token;
  215. break;
  216. }
  217. break;
  218. case 'closure':
  219. switch ($token[0]){
  220. case T_CURLY_OPEN:
  221. case T_DOLLAR_OPEN_CURLY_BRACES:
  222. case T_STRING_VARNAME:
  223. case '{':
  224. $code .= '{';
  225. $open++;
  226. break;
  227. case '}':
  228. $code .= '}';
  229. if(--$open === 0){
  230. break 3;
  231. } elseif ($inside_anonymous) {
  232. $inside_anonymous = !($open === $anonymous_mark);
  233. }
  234. break;
  235. case T_LINE:
  236. $code .= $token[2] - $line + $lineAdd;
  237. break;
  238. case T_FILE:
  239. $code .= $_file;
  240. break;
  241. case T_DIR:
  242. $code .= $_dir;
  243. break;
  244. case T_NS_C:
  245. $code .= $_namespace;
  246. break;
  247. case T_CLASS_C:
  248. $code .= $_class;
  249. break;
  250. case T_FUNC_C:
  251. $code .= $_function;
  252. break;
  253. case T_METHOD_C:
  254. $code .= $_method;
  255. break;
  256. case T_COMMENT:
  257. if (substr($token[1], 0, 8) === '#trackme') {
  258. $timestamp = time();
  259. $code .= '/**' . PHP_EOL;
  260. $code .= '* Date : ' . date(DATE_W3C, $timestamp) . PHP_EOL;
  261. $code .= '* Timestamp : ' . $timestamp . PHP_EOL;
  262. $code .= '* Line : ' . ($line + 1) . PHP_EOL;
  263. $code .= '* File : ' . $_file . PHP_EOL . '*/' . PHP_EOL;
  264. $lineAdd += 5;
  265. } else {
  266. $code .= $token[1];
  267. }
  268. break;
  269. case T_VARIABLE:
  270. if($token[1] == '$this' && !$inside_anonymous){
  271. $isUsingThisObject = true;
  272. }
  273. $code .= $token[1];
  274. break;
  275. case T_STATIC:
  276. $isUsingScope = true;
  277. $code .= $token[1];
  278. break;
  279. case T_NS_SEPARATOR:
  280. case T_STRING:
  281. $id_start = $token[1];
  282. $id_start_ci = strtolower($id_start);
  283. $id_name = '';
  284. $context = 'root';
  285. $state = 'id_name';
  286. $lastState = 'closure';
  287. break 2;
  288. case T_NEW:
  289. $code .= $token[1];
  290. $context = 'new';
  291. $state = 'id_start';
  292. $lastState = 'closure';
  293. break 2;
  294. case T_USE:
  295. $code .= $token[1];
  296. $context = 'use';
  297. $state = 'id_start';
  298. $lastState = 'closure';
  299. break;
  300. case T_INSTANCEOF:
  301. $code .= $token[1];
  302. $context = 'instanceof';
  303. $state = 'id_start';
  304. $lastState = 'closure';
  305. break;
  306. case T_OBJECT_OPERATOR:
  307. case T_DOUBLE_COLON:
  308. $code .= $token[1];
  309. $lastState = 'closure';
  310. $state = 'ignore_next';
  311. break;
  312. case T_FUNCTION:
  313. $code .= $token[1];
  314. $state = 'closure_args';
  315. break;
  316. case T_TRAIT_C:
  317. if ($_trait === null) {
  318. $startLine = $this->getStartLine();
  319. $endLine = $this->getEndLine();
  320. $structures = $this->getStructures();
  321. $_trait = '';
  322. foreach ($structures as &$struct) {
  323. if ($struct['type'] === 'trait' &&
  324. $struct['start'] <= $startLine &&
  325. $struct['end'] >= $endLine
  326. ) {
  327. $_trait = ($ns == '' ? '' : $ns . '\\') . $struct['name'];
  328. break;
  329. }
  330. }
  331. $_trait = var_export($_trait, true);
  332. }
  333. $code .= $_trait;
  334. break;
  335. default:
  336. $code .= is_array($token) ? $token[1] : $token;
  337. }
  338. break;
  339. case 'ignore_next':
  340. switch ($token[0]){
  341. case T_WHITESPACE:
  342. case T_COMMENT:
  343. case T_DOC_COMMENT:
  344. $code .= $token[1];
  345. break;
  346. case T_CLASS:
  347. case T_NEW:
  348. case T_STATIC:
  349. case T_VARIABLE:
  350. case T_STRING:
  351. case T_CLASS_C:
  352. case T_FILE:
  353. case T_DIR:
  354. case T_METHOD_C:
  355. case T_FUNC_C:
  356. case T_FUNCTION:
  357. case T_INSTANCEOF:
  358. case T_LINE:
  359. case T_NS_C:
  360. case T_TRAIT_C:
  361. case T_USE:
  362. $code .= $token[1];
  363. $state = $lastState;
  364. break;
  365. default:
  366. $state = $lastState;
  367. $i--;
  368. }
  369. break;
  370. case 'id_start':
  371. switch ($token[0]){
  372. case T_WHITESPACE:
  373. case T_COMMENT:
  374. case T_DOC_COMMENT:
  375. $code .= $token[1];
  376. break;
  377. case T_NS_SEPARATOR:
  378. case T_STRING:
  379. case T_STATIC:
  380. $id_start = $token[1];
  381. $id_start_ci = strtolower($id_start);
  382. $id_name = '';
  383. $state = 'id_name';
  384. break 2;
  385. case T_VARIABLE:
  386. $code .= $token[1];
  387. $state = $lastState;
  388. break;
  389. case T_CLASS:
  390. $code .= $token[1];
  391. $state = 'anonymous';
  392. break;
  393. default:
  394. $i--;//reprocess last
  395. $state = 'id_name';
  396. }
  397. break;
  398. case 'id_name':
  399. switch ($token[0]){
  400. case T_NS_SEPARATOR:
  401. case T_STRING:
  402. $id_name .= $token[1];
  403. break;
  404. case T_WHITESPACE:
  405. case T_COMMENT:
  406. case T_DOC_COMMENT:
  407. $id_name .= $token[1];
  408. break;
  409. case '(':
  410. if($context === 'new' || false !== strpos($id_name, '\\')){
  411. if($id_start !== '\\'){
  412. if ($classes === null) {
  413. $classes = $this->getClasses();
  414. }
  415. if (isset($classes[$id_start_ci])) {
  416. $id_start = $classes[$id_start_ci];
  417. }
  418. if($id_start[0] !== '\\'){
  419. $id_start = $nsf . '\\' . $id_start;
  420. }
  421. }
  422. } else {
  423. if($id_start !== '\\'){
  424. if($functions === null){
  425. $functions = $this->getFunctions();
  426. }
  427. if(isset($functions[$id_start_ci])){
  428. $id_start = $functions[$id_start_ci];
  429. }
  430. }
  431. }
  432. $code .= $id_start . $id_name . '(';
  433. $state = $lastState;
  434. break;
  435. case T_VARIABLE:
  436. case T_DOUBLE_COLON:
  437. if($id_start !== '\\') {
  438. if($id_start_ci === 'self' || $id_start_ci === 'static' || $id_start_ci === 'parent'){
  439. $isUsingScope = true;
  440. } elseif (!($php7 && in_array($id_start_ci, $php7_types))){
  441. if ($classes === null) {
  442. $classes = $this->getClasses();
  443. }
  444. if (isset($classes[$id_start_ci])) {
  445. $id_start = $classes[$id_start_ci];
  446. }
  447. if($id_start[0] !== '\\'){
  448. $id_start = $nsf . '\\' . $id_start;
  449. }
  450. }
  451. }
  452. $code .= $id_start . $id_name . $token[1];
  453. $state = $token[0] === T_DOUBLE_COLON ? 'ignore_next' : $lastState;
  454. break;
  455. default:
  456. if($id_start !== '\\'){
  457. if($context === 'use' ||
  458. $context === 'instanceof' ||
  459. $context === 'args' ||
  460. $context === 'return_type' ||
  461. $context === 'extends'
  462. ){
  463. if($id_start_ci === 'self' || $id_start_ci === 'static' || $id_start_ci === 'parent'){
  464. $isUsingScope = true;
  465. } elseif (!($php7 && in_array($id_start_ci, $php7_types))){
  466. if($classes === null){
  467. $classes = $this->getClasses();
  468. }
  469. if(isset($classes[$id_start_ci])){
  470. $id_start = $classes[$id_start_ci];
  471. }
  472. if($id_start[0] !== '\\'){
  473. $id_start = $nsf . '\\' . $id_start;
  474. }
  475. }
  476. } else {
  477. if($constants === null){
  478. $constants = $this->getConstants();
  479. }
  480. if(isset($constants[$id_start])){
  481. $id_start = $constants[$id_start];
  482. }
  483. }
  484. }
  485. $code .= $id_start . $id_name;
  486. $state = $lastState;
  487. $i--;//reprocess last token
  488. }
  489. break;
  490. case 'anonymous':
  491. switch ($token[0]) {
  492. case T_NS_SEPARATOR:
  493. case T_STRING:
  494. $id_start = $token[1];
  495. $id_start_ci = strtolower($id_start);
  496. $id_name = '';
  497. $state = 'id_name';
  498. $context = 'extends';
  499. $lastState = 'anonymous';
  500. break;
  501. case '{':
  502. $state = 'closure';
  503. if (!$inside_anonymous) {
  504. $inside_anonymous = true;
  505. $anonymous_mark = $open;
  506. }
  507. $i--;
  508. break;
  509. default:
  510. $code .= is_array($token) ? $token[1] : $token;
  511. }
  512. break;
  513. }
  514. }
  515. $this->isBindingRequired = $isUsingThisObject;
  516. $this->isScopeRequired = $isUsingScope;
  517. $this->code = $code;
  518. $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use));
  519. return $this->code;
  520. }
  521. /**
  522. * @return array
  523. */
  524. public function getUseVariables()
  525. {
  526. if($this->useVariables !== null){
  527. return $this->useVariables;
  528. }
  529. $tokens = $this->getTokens();
  530. $use = array();
  531. $state = 'start';
  532. foreach ($tokens as &$token) {
  533. $is_array = is_array($token);
  534. switch ($state) {
  535. case 'start':
  536. if ($is_array && $token[0] === T_USE) {
  537. $state = 'use';
  538. }
  539. break;
  540. case 'use':
  541. if ($is_array) {
  542. if ($token[0] === T_VARIABLE) {
  543. $use[] = substr($token[1], 1);
  544. }
  545. } elseif ($token == ')') {
  546. break 2;
  547. }
  548. break;
  549. }
  550. }
  551. $this->useVariables = empty($use) ? $use : array_intersect_key($this->getStaticVariables(), array_flip($use));
  552. return $this->useVariables;
  553. }
  554. /**
  555. * return bool
  556. */
  557. public function isBindingRequired()
  558. {
  559. if($this->isBindingRequired === null){
  560. $this->getCode();
  561. }
  562. return $this->isBindingRequired;
  563. }
  564. /**
  565. * return bool
  566. */
  567. public function isScopeRequired()
  568. {
  569. if($this->isScopeRequired === null){
  570. $this->getCode();
  571. }
  572. return $this->isScopeRequired;
  573. }
  574. /**
  575. * @return string
  576. */
  577. protected function getHashedFileName()
  578. {
  579. if ($this->hashedName === null) {
  580. $this->hashedName = sha1($this->getFileName());
  581. }
  582. return $this->hashedName;
  583. }
  584. /**
  585. * @return array
  586. */
  587. protected function getFileTokens()
  588. {
  589. $key = $this->getHashedFileName();
  590. if (!isset(static::$files[$key])) {
  591. static::$files[$key] = token_get_all(file_get_contents($this->getFileName()));
  592. }
  593. return static::$files[$key];
  594. }
  595. /**
  596. * @return array
  597. */
  598. protected function getTokens()
  599. {
  600. if ($this->tokens === null) {
  601. $tokens = $this->getFileTokens();
  602. $startLine = $this->getStartLine();
  603. $endLine = $this->getEndLine();
  604. $results = array();
  605. $start = false;
  606. foreach ($tokens as &$token) {
  607. if (!is_array($token)) {
  608. if ($start) {
  609. $results[] = $token;
  610. }
  611. continue;
  612. }
  613. $line = $token[2];
  614. if ($line <= $endLine) {
  615. if ($line >= $startLine) {
  616. $start = true;
  617. $results[] = $token;
  618. }
  619. continue;
  620. }
  621. break;
  622. }
  623. $this->tokens = $results;
  624. }
  625. return $this->tokens;
  626. }
  627. /**
  628. * @return array
  629. */
  630. protected function getClasses()
  631. {
  632. $key = $this->getHashedFileName();
  633. if (!isset(static::$classes[$key])) {
  634. $this->fetchItems();
  635. }
  636. return static::$classes[$key];
  637. }
  638. /**
  639. * @return array
  640. */
  641. protected function getFunctions()
  642. {
  643. $key = $this->getHashedFileName();
  644. if (!isset(static::$functions[$key])) {
  645. $this->fetchItems();
  646. }
  647. return static::$functions[$key];
  648. }
  649. /**
  650. * @return array
  651. */
  652. protected function getConstants()
  653. {
  654. $key = $this->getHashedFileName();
  655. if (!isset(static::$constants[$key])) {
  656. $this->fetchItems();
  657. }
  658. return static::$constants[$key];
  659. }
  660. /**
  661. * @return array
  662. */
  663. protected function getStructures()
  664. {
  665. $key = $this->getHashedFileName();
  666. if (!isset(static::$structures[$key])) {
  667. $this->fetchItems();
  668. }
  669. return static::$structures[$key];
  670. }
  671. protected function fetchItems()
  672. {
  673. $key = $this->getHashedFileName();
  674. $classes = array();
  675. $functions = array();
  676. $constants = array();
  677. $structures = array();
  678. $tokens = $this->getFileTokens();
  679. $open = 0;
  680. $state = 'start';
  681. $lastState = '';
  682. $prefix = '';
  683. $name = '';
  684. $alias = '';
  685. $isFunc = $isConst = false;
  686. $startLine = $endLine = 0;
  687. $structType = $structName = '';
  688. $structIgnore = false;
  689. foreach ($tokens as $token) {
  690. switch ($state) {
  691. case 'start':
  692. switch ($token[0]) {
  693. case T_CLASS:
  694. case T_INTERFACE:
  695. case T_TRAIT:
  696. $state = 'before_structure';
  697. $startLine = $token[2];
  698. $structType = $token[0] == T_CLASS
  699. ? 'class'
  700. : ($token[0] == T_INTERFACE ? 'interface' : 'trait');
  701. break;
  702. case T_USE:
  703. $state = 'use';
  704. $prefix = $name = $alias = '';
  705. $isFunc = $isConst = false;
  706. break;
  707. case T_FUNCTION:
  708. $state = 'structure';
  709. $structIgnore = true;
  710. break;
  711. case T_NEW:
  712. $state = 'new';
  713. break;
  714. case T_OBJECT_OPERATOR:
  715. case T_DOUBLE_COLON:
  716. $state = 'invoke';
  717. break;
  718. }
  719. break;
  720. case 'use':
  721. switch ($token[0]) {
  722. case T_FUNCTION:
  723. $isFunc = true;
  724. break;
  725. case T_CONST:
  726. $isConst = true;
  727. break;
  728. case T_NS_SEPARATOR:
  729. $name .= $token[1];
  730. break;
  731. case T_STRING:
  732. $name .= $token[1];
  733. $alias = $token[1];
  734. break;
  735. case T_AS:
  736. $lastState = 'use';
  737. $state = 'alias';
  738. break;
  739. case '{':
  740. $prefix = $name;
  741. $name = $alias = '';
  742. $state = 'use-group';
  743. break;
  744. case ',':
  745. case ';':
  746. if ($name === '' || $name[0] !== '\\') {
  747. $name = '\\' . $name;
  748. }
  749. if ($alias !== '') {
  750. if ($isFunc) {
  751. $functions[strtolower($alias)] = $name;
  752. } elseif ($isConst) {
  753. $constants[$alias] = $name;
  754. } else {
  755. $classes[strtolower($alias)] = $name;
  756. }
  757. }
  758. $name = $alias = '';
  759. $state = $token === ';' ? 'start' : 'use';
  760. break;
  761. }
  762. break;
  763. case 'use-group':
  764. switch ($token[0]) {
  765. case T_NS_SEPARATOR:
  766. $name .= $token[1];
  767. break;
  768. case T_STRING:
  769. $name .= $token[1];
  770. $alias = $token[1];
  771. break;
  772. case T_AS:
  773. $lastState = 'use-group';
  774. $state = 'alias';
  775. break;
  776. case ',':
  777. case '}':
  778. if ($prefix === '' || $prefix[0] !== '\\') {
  779. $prefix = '\\' . $prefix;
  780. }
  781. if ($alias !== '') {
  782. if ($isFunc) {
  783. $functions[strtolower($alias)] = $prefix . $name;
  784. } elseif ($isConst) {
  785. $constants[$alias] = $prefix . $name;
  786. } else {
  787. $classes[strtolower($alias)] = $prefix . $name;
  788. }
  789. }
  790. $name = $alias = '';
  791. $state = $token === '}' ? 'use' : 'use-group';
  792. break;
  793. }
  794. break;
  795. case 'alias':
  796. if ($token[0] === T_STRING) {
  797. $alias = $token[1];
  798. $state = $lastState;
  799. }
  800. break;
  801. case 'new':
  802. switch ($token[0]) {
  803. case T_WHITESPACE:
  804. case T_COMMENT:
  805. case T_DOC_COMMENT:
  806. break 2;
  807. case T_CLASS:
  808. $state = 'structure';
  809. $structIgnore = true;
  810. break;
  811. default:
  812. $state = 'start';
  813. }
  814. break;
  815. case 'invoke':
  816. switch ($token[0]) {
  817. case T_WHITESPACE:
  818. case T_COMMENT:
  819. case T_DOC_COMMENT:
  820. break 2;
  821. default:
  822. $state = 'start';
  823. }
  824. break;
  825. case 'before_structure':
  826. if ($token[0] == T_STRING) {
  827. $structName = $token[1];
  828. $state = 'structure';
  829. }
  830. break;
  831. case 'structure':
  832. switch ($token[0]) {
  833. case '{':
  834. case T_CURLY_OPEN:
  835. case T_DOLLAR_OPEN_CURLY_BRACES:
  836. case T_STRING_VARNAME:
  837. $open++;
  838. break;
  839. case '}':
  840. if (--$open == 0) {
  841. if(!$structIgnore){
  842. $structures[] = array(
  843. 'type' => $structType,
  844. 'name' => $structName,
  845. 'start' => $startLine,
  846. 'end' => $endLine,
  847. );
  848. }
  849. $structIgnore = false;
  850. $state = 'start';
  851. }
  852. break;
  853. default:
  854. if (is_array($token)) {
  855. $endLine = $token[2];
  856. }
  857. }
  858. break;
  859. }
  860. }
  861. static::$classes[$key] = $classes;
  862. static::$functions[$key] = $functions;
  863. static::$constants[$key] = $constants;
  864. static::$structures[$key] = $structures;
  865. }
  866. }