TokenTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of PHP CS Fixer.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. namespace PhpCsFixer\Tests\Tokenizer;
  13. use PhpCsFixer\Tests\TestCase;
  14. use PhpCsFixer\Tokenizer\CT;
  15. use PhpCsFixer\Tokenizer\Token;
  16. /**
  17. * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
  18. *
  19. * @internal
  20. *
  21. * @covers \PhpCsFixer\Tokenizer\Token
  22. */
  23. final class TokenTest extends TestCase
  24. {
  25. /**
  26. * @param mixed $input
  27. *
  28. * @dataProvider provideConstructorValidationCases
  29. */
  30. public function testConstructorValidation($input): void
  31. {
  32. $this->expectException(\InvalidArgumentException::class);
  33. new Token($input);
  34. }
  35. public function provideConstructorValidationCases(): array
  36. {
  37. return [
  38. [null],
  39. [123],
  40. [new \stdClass()],
  41. [['asd', 'asd']],
  42. [[null, 'asd']],
  43. [[new \stdClass(), 'asd']],
  44. [[T_WHITESPACE, null]],
  45. [[T_WHITESPACE, 123]],
  46. [[T_WHITESPACE, '']],
  47. [[T_WHITESPACE, new \stdClass()]],
  48. ];
  49. }
  50. public function testGetPrototype(): void
  51. {
  52. static::assertSame($this->getBraceTokenPrototype(), $this->getBraceToken()->getPrototype());
  53. static::assertSame($this->getForeachTokenPrototype(), $this->getForeachToken()->getPrototype());
  54. }
  55. public function testIsArray(): void
  56. {
  57. static::assertFalse($this->getBraceToken()->isArray());
  58. static::assertTrue($this->getForeachToken()->isArray());
  59. }
  60. /**
  61. * @dataProvider provideIsCastCases
  62. */
  63. public function testIsCast(Token $token, bool $isCast): void
  64. {
  65. static::assertSame($isCast, $token->isCast());
  66. }
  67. public function provideIsCastCases(): array
  68. {
  69. return [
  70. [$this->getBraceToken(), false],
  71. [$this->getForeachToken(), false],
  72. [new Token([T_ARRAY_CAST, '(array)', 1]), true],
  73. [new Token([T_BOOL_CAST, '(bool)', 1]), true],
  74. [new Token([T_DOUBLE_CAST, '(double)', 1]), true],
  75. [new Token([T_INT_CAST, '(int)', 1]), true],
  76. [new Token([T_OBJECT_CAST, '(object)', 1]), true],
  77. [new Token([T_STRING_CAST, '(string)', 1]), true],
  78. [new Token([T_UNSET_CAST, '(unset)', 1]), true],
  79. ];
  80. }
  81. /**
  82. * @dataProvider provideIsClassyCases
  83. */
  84. public function testIsClassy(Token $token, bool $isClassy): void
  85. {
  86. static::assertSame($isClassy, $token->isClassy());
  87. }
  88. public function provideIsClassyCases(): array
  89. {
  90. return [
  91. [$this->getBraceToken(), false],
  92. [$this->getForeachToken(), false],
  93. [new Token([T_CLASS, 'class', 1]), true],
  94. [new Token([T_INTERFACE, 'interface', 1]), true],
  95. [new Token([T_TRAIT, 'trait', 1]), true],
  96. ];
  97. }
  98. /**
  99. * @dataProvider provideIsCommentCases
  100. */
  101. public function testIsComment(Token $token, bool $isComment): void
  102. {
  103. static::assertSame($isComment, $token->isComment());
  104. }
  105. public function provideIsCommentCases(): \Generator
  106. {
  107. yield from [
  108. [$this->getBraceToken(), false],
  109. [$this->getForeachToken(), false],
  110. [new Token([T_COMMENT, '/* comment */', 1]), true],
  111. [new Token([T_DOC_COMMENT, '/** docs */', 1]), true],
  112. ];
  113. // @TODO: drop condition when PHP 8.0+ is required
  114. if (\defined('T_ATTRIBUTE')) {
  115. yield [new Token([T_ATTRIBUTE, '#[', 1]), false];
  116. }
  117. }
  118. /**
  119. * @dataProvider provideIsObjectOperatorCases
  120. */
  121. public function testIsObjectOperator(Token $token, bool $isObjectOperator): void
  122. {
  123. static::assertSame($isObjectOperator, $token->isObjectOperator());
  124. }
  125. public function provideIsObjectOperatorCases(): \Generator
  126. {
  127. yield from [
  128. [$this->getBraceToken(), false],
  129. [$this->getForeachToken(), false],
  130. [new Token([T_COMMENT, '/* comment */']), false],
  131. [new Token([T_DOUBLE_COLON, '::']), false],
  132. [new Token([T_OBJECT_OPERATOR, '->']), true],
  133. ];
  134. if (\defined('T_NULLSAFE_OBJECT_OPERATOR')) {
  135. yield [new Token([T_NULLSAFE_OBJECT_OPERATOR, '?->']), true];
  136. }
  137. }
  138. public function testIsGivenKind(): void
  139. {
  140. $braceToken = $this->getBraceToken();
  141. $foreachToken = $this->getForeachToken();
  142. static::assertFalse($braceToken->isGivenKind(T_FOR));
  143. static::assertFalse($braceToken->isGivenKind(T_FOREACH));
  144. static::assertFalse($braceToken->isGivenKind([T_FOR]));
  145. static::assertFalse($braceToken->isGivenKind([T_FOREACH]));
  146. static::assertFalse($braceToken->isGivenKind([T_FOR, T_FOREACH]));
  147. static::assertFalse($foreachToken->isGivenKind(T_FOR));
  148. static::assertTrue($foreachToken->isGivenKind(T_FOREACH));
  149. static::assertFalse($foreachToken->isGivenKind([T_FOR]));
  150. static::assertTrue($foreachToken->isGivenKind([T_FOREACH]));
  151. static::assertTrue($foreachToken->isGivenKind([T_FOR, T_FOREACH]));
  152. }
  153. public function testIsKeywords(): void
  154. {
  155. static::assertTrue($this->getForeachToken()->isKeyword());
  156. static::assertFalse($this->getBraceToken()->isKeyword());
  157. }
  158. /**
  159. * @param ?int $tokenId
  160. *
  161. * @dataProvider provideMagicConstantCases
  162. */
  163. public function testIsMagicConstant(?int $tokenId, string $content, bool $isConstant = true): void
  164. {
  165. $token = new Token(
  166. null === $tokenId ? $content : [$tokenId, $content]
  167. );
  168. static::assertSame($isConstant, $token->isMagicConstant());
  169. }
  170. public function provideMagicConstantCases(): \Generator
  171. {
  172. $cases = [
  173. [T_CLASS_C, '__CLASS__'],
  174. [T_DIR, '__DIR__'],
  175. [T_FILE, '__FILE__'],
  176. [T_FUNC_C, '__FUNCTION__'],
  177. [T_LINE, '__LINE__'],
  178. [T_METHOD_C, '__METHOD__'],
  179. [T_NS_C, '__NAMESPACE__'],
  180. [T_TRAIT_C, '__TRAIT__'],
  181. ];
  182. foreach ($cases as $case) {
  183. yield [$case[0], strtolower($case[1])];
  184. }
  185. foreach ([$this->getForeachToken(), $this->getBraceToken()] as $token) {
  186. yield [$token->getId(), $token->getContent(), false];
  187. yield [$token->getId(), strtolower($token->getContent()), false];
  188. }
  189. }
  190. /**
  191. * @dataProvider provideIsNativeConstantCases
  192. */
  193. public function testIsNativeConstant(Token $token, bool $isNativeConstant): void
  194. {
  195. static::assertSame($isNativeConstant, $token->isNativeConstant());
  196. }
  197. public function provideIsNativeConstantCases(): array
  198. {
  199. return [
  200. [$this->getBraceToken(), false],
  201. [$this->getForeachToken(), false],
  202. [new Token([T_STRING, 'null', 1]), true],
  203. [new Token([T_STRING, 'false', 1]), true],
  204. [new Token([T_STRING, 'true', 1]), true],
  205. [new Token([T_STRING, 'tRuE', 1]), true],
  206. [new Token([T_STRING, 'TRUE', 1]), true],
  207. ];
  208. }
  209. /**
  210. * @dataProvider provideIsWhitespaceCases
  211. */
  212. public function testIsWhitespace(Token $token, bool $isWhitespace, ?string $whitespaces = null): void
  213. {
  214. if (null !== $whitespaces) {
  215. static::assertSame($isWhitespace, $token->isWhitespace($whitespaces));
  216. } else {
  217. static::assertSame($isWhitespace, $token->isWhitespace(null));
  218. static::assertSame($isWhitespace, $token->isWhitespace());
  219. }
  220. }
  221. public function provideIsWhitespaceCases(): array
  222. {
  223. return [
  224. [$this->getBraceToken(), false],
  225. [$this->getForeachToken(), false],
  226. [new Token(' '), true],
  227. [new Token("\t "), true],
  228. [new Token("\t "), false, ' '],
  229. [new Token([T_WHITESPACE, "\r", 1]), true],
  230. [new Token([T_WHITESPACE, "\0", 1]), true],
  231. [new Token([T_WHITESPACE, "\x0B", 1]), true],
  232. [new Token([T_WHITESPACE, "\n", 1]), true],
  233. [new Token([T_WHITESPACE, "\n", 1]), false, " \t"],
  234. ];
  235. }
  236. /**
  237. * @param mixed $prototype
  238. *
  239. * @dataProvider provideCreatingTokenCases
  240. */
  241. public function testCreatingToken($prototype, ?int $expectedId, ?string $expectedContent, ?bool $expectedIsArray, ?string $expectedExceptionClass = null): void
  242. {
  243. if (null !== $expectedExceptionClass) {
  244. $this->expectException($expectedExceptionClass);
  245. }
  246. $token = new Token($prototype);
  247. static::assertSame($expectedId, $token->getId());
  248. static::assertSame($expectedContent, $token->getContent());
  249. static::assertSame($expectedIsArray, $token->isArray());
  250. }
  251. public function provideCreatingTokenCases(): array
  252. {
  253. return [
  254. [[T_FOREACH, 'foreach'], T_FOREACH, 'foreach', true],
  255. ['(', null, '(', false],
  256. [123, null, null, null, \InvalidArgumentException::class],
  257. [false, null, null, null, \InvalidArgumentException::class],
  258. [null, null, null, null, \InvalidArgumentException::class],
  259. ];
  260. }
  261. public function testEqualsDefaultIsCaseSensitive(): void
  262. {
  263. $token = new Token([T_FUNCTION, 'function', 1]);
  264. static::assertTrue($token->equals([T_FUNCTION, 'function']));
  265. static::assertFalse($token->equals([T_FUNCTION, 'Function']));
  266. }
  267. /**
  268. * @param array|string|Token $other
  269. *
  270. * @dataProvider provideEqualsCases
  271. */
  272. public function testEquals(Token $token, bool $equals, $other, bool $caseSensitive = true): void
  273. {
  274. static::assertSame($equals, $token->equals($other, $caseSensitive));
  275. }
  276. public function provideEqualsCases(): \Generator
  277. {
  278. $brace = $this->getBraceToken();
  279. $function = new Token([T_FUNCTION, 'function', 1]);
  280. yield [$brace, false, '!'];
  281. yield [$brace, false, '!', false];
  282. yield [$brace, true, '('];
  283. yield [$brace, true, '(', false];
  284. yield [$function, false, '('];
  285. yield [$function, false, '(', false];
  286. yield [$function, false, [T_NAMESPACE]];
  287. yield [$function, false, [T_NAMESPACE], false];
  288. yield [$function, false, [T_VARIABLE, 'function']];
  289. yield [$function, false, [T_VARIABLE, 'function'], false];
  290. yield [$function, false, [T_VARIABLE, 'Function']];
  291. yield [$function, false, [T_VARIABLE, 'Function'], false];
  292. yield [$function, true, [T_FUNCTION]];
  293. yield [$function, true, [T_FUNCTION], false];
  294. yield [$function, true, [T_FUNCTION, 'function']];
  295. yield [$function, true, [T_FUNCTION, 'function'], false];
  296. yield [$function, false, [T_FUNCTION, 'Function']];
  297. yield [$function, true, [T_FUNCTION, 'Function'], false];
  298. yield [$function, false, [T_FUNCTION, 'junction'], false];
  299. yield [$function, true, new Token([T_FUNCTION, 'function'])];
  300. yield [$function, false, new Token([T_FUNCTION, 'Function'])];
  301. yield [$function, true, new Token([T_FUNCTION, 'Function']), false];
  302. // if it is an array any additional field is checked too
  303. yield [$function, false, [T_FUNCTION, 'function', 'unexpected']];
  304. yield [new Token('&'), true, '&'];
  305. if (\defined('T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG')) { // @TODO: drop condition with new MAJOR release 4.0
  306. yield [new Token('&'), true, new Token([T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG, '&'])];
  307. yield [new Token('&'), true, new Token([T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, '&'])];
  308. yield [new Token([T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG, '&']), true, '&'];
  309. yield [new Token([T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG, '&']), true, new Token([T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG, '&'])];
  310. yield [new Token([T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, '&']), true, '&'];
  311. yield [new Token([T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, '&']), true, new Token([T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, '&'])];
  312. }
  313. }
  314. public function testEqualsAnyDefaultIsCaseSensitive(): void
  315. {
  316. $token = new Token([T_FUNCTION, 'function', 1]);
  317. static::assertTrue($token->equalsAny([[T_FUNCTION, 'function']]));
  318. static::assertFalse($token->equalsAny([[T_FUNCTION, 'Function']]));
  319. }
  320. /**
  321. * @dataProvider provideEqualsAnyCases
  322. */
  323. public function testEqualsAny(bool $equalsAny, array $other, bool $caseSensitive = true): void
  324. {
  325. $token = new Token([T_FUNCTION, 'function', 1]);
  326. static::assertSame($equalsAny, $token->equalsAny($other, $caseSensitive));
  327. }
  328. public function provideEqualsAnyCases(): \Generator
  329. {
  330. $brace = $this->getBraceToken();
  331. $foreach = $this->getForeachToken();
  332. yield [false, []];
  333. yield [false, [$brace]];
  334. yield [false, [$brace, $foreach]];
  335. yield [true, [$brace, $foreach, [T_FUNCTION]]];
  336. yield [true, [$brace, $foreach, [T_FUNCTION, 'function']]];
  337. yield [false, [$brace, $foreach, [T_FUNCTION, 'Function']]];
  338. yield [true, [$brace, $foreach, [T_FUNCTION, 'Function']], false];
  339. yield [false, [[T_VARIABLE, 'junction'], [T_FUNCTION, 'junction']], false];
  340. }
  341. /**
  342. * @param array|bool $caseSensitive
  343. *
  344. * @dataProvider provideIsKeyCaseSensitiveCases
  345. */
  346. public function testIsKeyCaseSensitive(bool $isKeyCaseSensitive, $caseSensitive, int $key): void
  347. {
  348. static::assertSame($isKeyCaseSensitive, Token::isKeyCaseSensitive($caseSensitive, $key));
  349. }
  350. public function provideIsKeyCaseSensitiveCases(): \Generator
  351. {
  352. yield [true, true, 0];
  353. yield [true, true, 1];
  354. yield [true, [], 0];
  355. yield [true, [true], 0];
  356. yield [true, [false, true], 1];
  357. yield [true, [false, true, false], 1];
  358. yield [true, [false], 10];
  359. yield [false, false, 10];
  360. yield [false, [false], 0];
  361. yield [false, [true, false], 1];
  362. yield [false, [true, false, true], 1];
  363. yield [false, [1 => false], 1];
  364. }
  365. /**
  366. * @dataProvider provideTokenGetNameCases
  367. */
  368. public function testTokenGetNameForId(?string $expected, int $id): void
  369. {
  370. static::assertSame($expected, Token::getNameForId($id));
  371. }
  372. public function provideTokenGetNameCases(): array
  373. {
  374. return [
  375. [
  376. null,
  377. -1,
  378. ],
  379. [
  380. 'T_CLASS',
  381. T_CLASS,
  382. ],
  383. [
  384. 'CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE',
  385. CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
  386. ],
  387. ];
  388. }
  389. /**
  390. * @dataProvider provideGetNameCases
  391. */
  392. public function testGetName(Token $token, ?string $expected = null): void
  393. {
  394. static::assertSame($expected, $token->getName());
  395. }
  396. public function provideGetNameCases(): \Generator
  397. {
  398. yield [
  399. new Token([T_FUNCTION, 'function', 1]),
  400. 'T_FUNCTION',
  401. ];
  402. yield [
  403. new Token(')'),
  404. null,
  405. ];
  406. yield [
  407. new Token(''),
  408. null,
  409. ];
  410. }
  411. /**
  412. * @dataProvider provideToArrayCases
  413. */
  414. public function testToArray(Token $token, array $expected): void
  415. {
  416. static::assertSame($expected, $token->toArray());
  417. }
  418. public function provideToArrayCases(): \Generator
  419. {
  420. yield [
  421. new Token([T_FUNCTION, 'function', 1]),
  422. [
  423. 'id' => T_FUNCTION,
  424. 'name' => 'T_FUNCTION',
  425. 'content' => 'function',
  426. 'isArray' => true,
  427. 'changed' => false,
  428. ],
  429. ];
  430. yield [
  431. new Token(')'),
  432. [
  433. 'id' => null,
  434. 'name' => null,
  435. 'content' => ')',
  436. 'isArray' => false,
  437. 'changed' => false,
  438. ],
  439. ];
  440. yield [
  441. new Token(''),
  442. [
  443. 'id' => null,
  444. 'name' => null,
  445. 'content' => '',
  446. 'isArray' => false,
  447. 'changed' => false,
  448. ],
  449. ];
  450. }
  451. private function getBraceToken(): Token
  452. {
  453. return new Token($this->getBraceTokenPrototype());
  454. }
  455. private function getBraceTokenPrototype(): string
  456. {
  457. return '(';
  458. }
  459. private function getForeachToken(): Token
  460. {
  461. return new Token($this->getForeachTokenPrototype());
  462. }
  463. private function getForeachTokenPrototype(): array
  464. {
  465. static $prototype = [T_FOREACH, 'foreach'];
  466. return $prototype;
  467. }
  468. }