packages/deriver/tests/Integration/StandardConstantsTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace Tests\Integration;
6
7use Deriver\Deriver\Candidate\Snapshot;
8use Deriver\Deriver\Evaluator\Call\Evaluators;
9use Deriver\Deriver\Evaluator\EvaluatorConfig;
10use Deriver\Deriver\Evaluator\Index\DefinitionIndex;
11use Deriver\Deriver\Evaluator\Request\ReturnQuery;
12use Deriver\Deriver\Evaluator\TargetProfile;
13use Deriver\Deriver\Evaluator\Term;
14use Deriver\Deriver\Lowerer\Declaration\Index;
15use Deriver\Deriver\Parser\Parser;
16use Deriver\Deriver\Parser\ParserConfig;
17use Deriver\Deriver\Publication\CandidateQuery;
18use Deriver\Deriver\Resolver\Resolver;
19use Deriver\Deriver\Sources\Source;
20use PHPUnit\Framework\Attributes\CoversNothing;
21use PHPUnit\Framework\Attributes\DataProvider;
22use PHPUnit\Framework\TestCase;
23
24#[CoversNothing]
25final class StandardConstantsTest extends TestCase
26{
27 /**
28 * @return iterable<string, array{string, mixed}>
29 */
30 public static function providerConstants(): iterable
31 {
32 yield 'literal case and qualification' => ['namespace C; function subject(){return [TRUE, false, NuLl, \\true, \\FALSE, \\NULL];}', [true,false,null,true,false,null]];
33 yield 'global standard fallback' => ['namespace C; function subject(){return [PHP_INT_SIZE, PHP_INT_MIN, PHP_INT_MAX];}', [8,-9223372036854775807 - 1,9223372036854775807]];
34 yield 'namespace declaration precedes fallback' => ['namespace C; const PHP_INT_SIZE=17; function subject(){return [PHP_INT_SIZE, \\PHP_INT_SIZE];}', [17,8]];
35 yield 'global declaration fallback' => ['namespace {const VALUE=17;} namespace C {function subject(){return VALUE;}}', 17];
36 yield 'defined constant fallback' => ['namespace {define("VALUE",17);} namespace C {function subject(){return VALUE;}}', 17];
37 yield 'defined namespace constant precedes fallback' => ['namespace {define("VALUE",4); define("C\\\\VALUE",17);} namespace C {function subject(){return VALUE;}}', 17];
38 yield 'imported declaration precedes standard spelling' => ['namespace D {const VALUE=17;} namespace C {use const D\\VALUE as PHP_INT_SIZE; function subject(){return PHP_INT_SIZE;}}', 17];
39 }
40
41 #[DataProvider('providerConstants')]
42 public function testResolvesConstantsWithoutUsingHostDefinitions(string $source, mixed $expected): void
43 {
44 $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('constants.php', '<?php ' . $source)));
45 $program = new Index('constants', [$tree]);
46 $target = new TargetProfile();
47 $query = new CandidateQuery(new DefinitionIndex($program), new EvaluatorConfig($target), new Evaluators($target), new Snapshot('constants', [], [], $target, 'environment'));
48 $result = $query->derive(new ReturnQuery('C\\subject'));
49 self::assertCount(1, $result);
50 self::assertSame($expected, $result[0]->result);
51 }
52
53 /**
54 * @return iterable<string, array{string}>
55 */
56 public static function providerUnresolved(): iterable
57 {
58 yield 'qualified name has no global fallback' => ['namespace\\PHP_INT_SIZE'];
59 yield 'standard names are case sensitive' => ['php_int_size'];
60 yield 'host-specific constants are not imported' => ['PHP_BINARY'];
61 }
62
63 #[DataProvider('providerUnresolved')]
64 public function testDoesNotInventAValueForAnUnresolvedConstant(string $expression): void
65 {
66 $source = 'namespace C; function subject(){return ' . $expression . ';}';
67 $tree = (new Resolver())->resolve((new Parser(new ParserConfig('8.3')))->parse(new Source('constants.php', '<?php ' . $source)));
68 $program = new Index('constants', [$tree]);
69 $target = new TargetProfile();
70 $query = new CandidateQuery(new DefinitionIndex($program), new EvaluatorConfig($target), new Evaluators($target), new Snapshot('constants', [], [], $target, 'environment'));
71 $result = $query->derive(new ReturnQuery('C\\subject'));
72 self::assertCount(1, $result);
73 self::assertInstanceOf(Term::class, $result[0]->result);
74 self::assertSame('MISSING_CONSTANT', $result[0]->result->attributes['reason']);
75 }
76
77}
78