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