packages/deriver/tests/Unit/Deriver/Evaluator/Builtin/LibraryTest.php
1<?php
2
3declare (strict_types=1);
4
5namespace Tests\Unit\Deriver\Evaluator\Builtin;
6
7use Deriver\Deriver;
8use Deriver\Deriver\Candidate\ResultRef;
9use Deriver\Deriver\Candidate\Snapshot;
10use Deriver\Deriver\Candidate\Statistics;
11use Deriver\Deriver\Contributions;
12use Deriver\Deriver\DeriverConfig;
13use Deriver\Deriver\Evaluator\Builtin\ArrayFunctions;
14use Deriver\Deriver\Evaluator\Builtin\FunctionEvaluator;
15use Deriver\Deriver\Evaluator\Builtin\Library;
16use Deriver\Deriver\Evaluator\Builtin\ScalarFunctions;
17use Deriver\Deriver\Evaluator\Call\ArgumentBindings;
18use Deriver\Deriver\Evaluator\Call\BoundArgument;
19use Deriver\Deriver\Evaluator\Call\CallDecision;
20use Deriver\Deriver\Evaluator\Call\CallDescription;
21use Deriver\Deriver\Evaluator\Call\CallDescriptor;
22use Deriver\Deriver\Evaluator\Call\Evaluators;
23use Deriver\Deriver\Evaluator\Call\Extensions;
24use Deriver\Deriver\Evaluator\Call\Plan\Action;
25use Deriver\Deriver\Evaluator\Call\Plan\Expression;
26use Deriver\Deriver\Evaluator\Call\Plan\PlanActions;
27use Deriver\Deriver\Evaluator\Call\Plan\PlanCompiler;
28use Deriver\Deriver\Evaluator\Call\Plan\PlanFootprints;
29use Deriver\Deriver\Evaluator\Call\Plan\PlanValidation;
30use Deriver\Deriver\Evaluator\Call\Plan\SemanticPlan;
31use Deriver\Deriver\Evaluator\Call\Registrations;
32use Deriver\Deriver\Evaluator\Call\StateRegistry;
33use Deriver\Deriver\Evaluator\Declaration\Parameter as DeclarationParameter;
34use Deriver\Deriver\Evaluator\Declaration\Signature;
35use Deriver\Deriver\Evaluator\EvaluatorConfig;
36use Deriver\Deriver\Evaluator\Evidence\Frontier;
37use Deriver\Deriver\Evaluator\Execution\Call\ArgumentBinding;
38use Deriver\Deriver\Evaluator\Execution\Call\ArgumentOrder;
39use Deriver\Deriver\Evaluator\Execution\Call\CallableCheck;
40use Deriver\Deriver\Evaluator\Execution\Call\CallExecutor;
41use Deriver\Deriver\Evaluator\Execution\Call\CallResolution;
42use Deriver\Deriver\Evaluator\Execution\Call\Contract\Inputs;
43use Deriver\Deriver\Evaluator\Execution\Call\Contract\NativeArguments;
44use Deriver\Deriver\Evaluator\Execution\Call\ParameterBinding;
45use Deriver\Deriver\Evaluator\Execution\Call\PassedArgument;
46use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Arguments;
47use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Modes;
48use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Resolution;
49use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Target;
50use Deriver\Deriver\Evaluator\Execution\Call\Preparation\Transfer;
51use Deriver\Deriver\Evaluator\Execution\Call\TypeBinding;
52use Deriver\Deriver\Evaluator\Execution\Call\TypeCheck;
53use Deriver\Deriver\Evaluator\Execution\Completion\Alternative;
54use Deriver\Deriver\Evaluator\Execution\Completion\Assessment;
55use Deriver\Deriver\Evaluator\Execution\Completion\Completion;
56use Deriver\Deriver\Evaluator\Execution\Completion\StorageSnapshot;
57use Deriver\Deriver\Evaluator\Execution\Context;
58use Deriver\Deriver\Evaluator\Execution\Control\ObservationLimit;
59use Deriver\Deriver\Evaluator\Execution\Control\StateJoin;
60use Deriver\Deriver\Evaluator\Execution\Control\Unwinding;
61use Deriver\Deriver\Evaluator\Execution\Demand\Discovery;
62use Deriver\Deriver\Evaluator\Execution\Dependencies;
63use Deriver\Deriver\Evaluator\Execution\Evaluator;
64use Deriver\Deriver\Evaluator\Execution\EvaluatorConfig as ExecutionEvaluatorConfig;
65use Deriver\Deriver\Evaluator\Execution\InstructionTransfer;
66use Deriver\Deriver\Evaluator\Execution\Memory\Location;
67use Deriver\Deriver\Evaluator\Execution\Memory\Materialization;
68use Deriver\Deriver\Evaluator\Execution\Memory\Memory;
69use Deriver\Deriver\Evaluator\Execution\Memory\Slots\SlotReference;
70use Deriver\Deriver\Evaluator\Execution\Memory\StorageCapture;
71use Deriver\Deriver\Evaluator\Execution\ObservationCollector;
72use Deriver\Deriver\Evaluator\Execution\Operation\Conversions;
73use Deriver\Deriver\Evaluator\Execution\Operation\ScalarErrors;
74use Deriver\Deriver\Evaluator\Execution\State;
75use Deriver\Deriver\Evaluator\Execution\Summary\Evaluation;
76use Deriver\Deriver\Evaluator\Execution\Summary\Isolation;
77use Deriver\Deriver\Evaluator\Execution\Trace\Derivation;
78use Deriver\Deriver\Evaluator\Execution\Transfer\IntrinsicTransfer;
79use Deriver\Deriver\Evaluator\Execution\Transfer\MemoryStep;
80use Deriver\Deriver\Evaluator\Execution\Transfer\PureStep;
81use Deriver\Deriver\Evaluator\Execution\Transfer\ReferenceAssignment;
82use Deriver\Deriver\Evaluator\Request\Budget;
83use Deriver\Deriver\Evaluator\Request\EntryPoint;
84use Deriver\Deriver\Evaluator\Request\QueryScope;
85use Deriver\Deriver\Evaluator\Request\ReturnQuery;
86use Deriver\Deriver\Evaluator\Scalar\Arrays;
87use Deriver\Deriver\Evaluator\Scalar\Identity;
88use Deriver\Deriver\Evaluator\Scalar\Operations;
89use Deriver\Deriver\Evaluator\TargetProfile;
90use Deriver\Deriver\Evaluator\Term;
91use Deriver\Deriver\Evaluator\TermKind;
92use Deriver\Deriver\Execution\DerivationResult;
93use Deriver\Deriver\Execution\QueryExecution;
94use Deriver\Deriver\Execution\ResultAssessment;
95use Deriver\Deriver\Identity\QueryEncoding;
96use Deriver\Deriver\Identity\ValueGraph;
97use Deriver\Deriver\Lowerer\AggregateLowering;
98use Deriver\Deriver\Lowerer\CallLowering;
99use Deriver\Deriver\Lowerer\Declaration\CallableIdentity;
100use Deriver\Deriver\Lowerer\Declaration\CallableSource;
101use Deriver\Deriver\Lowerer\Declaration\Index;
102use Deriver\Deriver\Lowerer\Declaration\Indexer;
103use Deriver\Deriver\Lowerer\Declaration\Traits\Composition;
104use Deriver\Deriver\Lowerer\ExpressionLowering;
105use Deriver\Deriver\Lowerer\Graph\Argument;
106use Deriver\Deriver\Lowerer\Graph\BasicBlock;
107use Deriver\Deriver\Lowerer\Graph\CallableGraph;
108use Deriver\Deriver\Lowerer\Graph\Instruction;
109use Deriver\Deriver\Lowerer\Graph\Parameter;
110use Deriver\Deriver\Lowerer\Graph\Terminator;
111use Deriver\Deriver\Lowerer\Hierarchy\Dispatch;
112use Deriver\Deriver\Lowerer\InstructionEmitter;
113use Deriver\Deriver\Lowerer\Lowerer;
114use Deriver\Deriver\Lowerer\LoweringScope;
115use Deriver\Deriver\Lowerer\StatementLowering;
116use Deriver\Deriver\MicroEvaluatorConversion;
117use Deriver\Deriver\Parser\Node\File;
118use Deriver\Deriver\Parser\Parser;
119use Deriver\Deriver\Parser\ParserConfig;
120use Deriver\Deriver\Parser\TargetSyntax;
121use Deriver\Deriver\Providers\ProviderExpansion;
122use Deriver\Deriver\Resolver\AssignmentPatterns;
123use Deriver\Deriver\Resolver\ClassScope;
124use Deriver\Deriver\Resolver\ExpressionValidation;
125use Deriver\Deriver\Resolver\MagicContext;
126use Deriver\Deriver\Resolver\Resolver;
127use Deriver\Deriver\Selection\QueryValidation;
128use Deriver\Deriver\SnapshotIdentity;
129use Deriver\Deriver\Sources\LineMap;
130use Deriver\Deriver\Sources\Path;
131use Deriver\Deriver\Sources\Source;
132use Deriver\Deriver\Sources\SourceRef;
133use Deriver\Deriver\Sources\Sources;
134use Deriver\Exception\ParserException;
135use Deriver\Exception\ResolutionException;
136use PHPUnit\Framework\Attributes\CoversClass;
137use PHPUnit\Framework\Attributes\DataProvider;
138use PHPUnit\Framework\Attributes\Small;
139use PHPUnit\Framework\Attributes\UsesClass;
140use PHPUnit\Framework\TestCase;
141
142#[UsesClass(Resolver::class)]
143#[UsesClass(ExpressionValidation::class)]
144#[UsesClass(ResolutionException::class)]
145#[UsesClass(ParserConfig::class)]
146#[UsesClass(ParserException::class)]
147#[CoversClass(Library::class)]
148#[UsesClass(QueryExecution::class)]
149#[UsesClass(QueryValidation::class)]
150#[UsesClass(ResultAssessment::class)]
151#[UsesClass(Deriver::class)]
152#[UsesClass(Argument::class)]
153#[UsesClass(BasicBlock::class)]
154#[UsesClass(CallableGraph::class)]
155#[UsesClass(CallableIdentity::class)]
156#[UsesClass(Instruction::class)]
157#[UsesClass(Parameter::class)]
158#[UsesClass(Terminator::class)]
159#[UsesClass(ArgumentBinding::class)]
160#[UsesClass(ArgumentOrder::class)]
161#[UsesClass(CallExecutor::class)]
162#[UsesClass(CallResolution::class)]
163#[UsesClass(CallableCheck::class)]
164#[UsesClass(Dispatch::class)]
165#[UsesClass(Inputs::class)]
166#[UsesClass(NativeArguments::class)]
167#[UsesClass(ParameterBinding::class)]
168#[UsesClass(PassedArgument::class)]
169#[UsesClass(Arguments::class)]
170#[UsesClass(Modes::class)]
171#[UsesClass(Resolution::class)]
172#[UsesClass(Target::class)]
173#[UsesClass(Transfer::class)]
174#[UsesClass(TypeBinding::class)]
175#[UsesClass(TypeCheck::class)]
176#[UsesClass(Completion::class)]
177#[UsesClass(Context::class)]
178#[UsesClass(ObservationLimit::class)]
179#[UsesClass(StateJoin::class)]
180#[UsesClass(Unwinding::class)]
181#[UsesClass(Discovery::class)]
182#[UsesClass(Dependencies::class)]
183#[UsesClass(InstructionTransfer::class)]
184#[UsesClass(Evaluator::class)]
185#[UsesClass(SlotReference::class)]
186#[UsesClass(ObservationCollector::class)]
187#[UsesClass(Conversions::class)]
188#[UsesClass(ScalarErrors::class)]
189#[UsesClass(State::class)]
190#[UsesClass(Evaluation::class)]
191#[UsesClass(Isolation::class)]
192#[UsesClass(IntrinsicTransfer::class)]
193#[UsesClass(MemoryStep::class)]
194#[UsesClass(PureStep::class)]
195#[UsesClass(ReferenceAssignment::class)]
196#[UsesClass(Location::class)]
197#[UsesClass(Materialization::class)]
198#[UsesClass(Memory::class)]
199#[UsesClass(StorageCapture::class)]
200#[UsesClass(ArgumentBindings::class)]
201#[UsesClass(BoundArgument::class)]
202#[UsesClass(ArrayFunctions::class)]
203#[UsesClass(FunctionEvaluator::class)]
204#[UsesClass(ScalarFunctions::class)]
205#[UsesClass(CallDescription::class)]
206#[UsesClass(PlanActions::class)]
207#[UsesClass(PlanCompiler::class)]
208#[UsesClass(PlanFootprints::class)]
209#[UsesClass(PlanValidation::class)]
210#[UsesClass(CallDecision::class)]
211#[UsesClass(CallDescriptor::class)]
212#[UsesClass(Action::class)]
213#[UsesClass(Expression::class)]
214#[UsesClass(SemanticPlan::class)]
215#[UsesClass(Extensions::class)]
216#[UsesClass(ProviderExpansion::class)]
217#[UsesClass(Evaluators::class)]
218#[UsesClass(StateRegistry::class)]
219#[UsesClass(DeclarationParameter::class)]
220#[UsesClass(Signature::class)]
221#[UsesClass(DeriverConfig::class)]
222#[UsesClass(EntryPoint::class)]
223#[UsesClass(Sources::class)]
224#[UsesClass(Path::class)]
225#[UsesClass(Snapshot::class)]
226#[UsesClass(Source::class)]
227#[UsesClass(TargetProfile::class)]
228#[UsesClass(Budget::class)]
229#[UsesClass(QueryScope::class)]
230#[UsesClass(ReturnQuery::class)]
231#[UsesClass(ResultRef::class)]
232#[UsesClass(SourceRef::class)]
233#[UsesClass(Alternative::class)]
234#[UsesClass(Assessment::class)]
235#[UsesClass(Derivation::class)]
236#[UsesClass(DerivationResult::class)]
237#[UsesClass(QueryEncoding::class)]
238#[UsesClass(ValueGraph::class)]
239#[UsesClass(Statistics::class)]
240#[UsesClass(StorageSnapshot::class)]
241#[UsesClass(Parser::class)]
242#[UsesClass(File::class)]
243#[UsesClass(AggregateLowering::class)]
244#[UsesClass(CallLowering::class)]
245#[UsesClass(Lowerer::class)]
246#[UsesClass(ExpressionLowering::class)]
247#[UsesClass(InstructionEmitter::class)]
248#[UsesClass(LoweringScope::class)]
249#[UsesClass(StatementLowering::class)]
250#[UsesClass(CallableSource::class)]
251#[UsesClass(Indexer::class)]
252#[UsesClass(Index::class)]
253#[UsesClass(Composition::class)]
254#[UsesClass(LineMap::class)]
255#[UsesClass(MagicContext::class)]
256#[UsesClass(AssignmentPatterns::class)]
257#[UsesClass(ClassScope::class)]
258#[UsesClass(TargetSyntax::class)]
259#[UsesClass(Arrays::class)]
260#[UsesClass(Identity::class)]
261#[UsesClass(Operations::class)]
262#[UsesClass(Term::class)]
263#[Small]
264final class LibraryTest extends TestCase
265{
266 public function testEvaluatorPreservesTheSemanticContract(): void
267 {
268 $source = '<?php function target(){return array_merge([2=>"left","x"=>1],[2=>"right","x"=>2]);}';
269 $symbol = 'target';
270 $input = new Sources([new Source('fixture.php', $source)]);
271 $configuration = new DeriverConfig();
272 $contributions = new Contributions();
273 $target = new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision);
274 $providers = new ProviderExpansion($input, array_map(Term::fromNative(...), $configuration->environment), $contributions);
275 $contributions1 = $providers->contributions;
276 $evaluators = new Evaluators(new TargetProfile($configuration->phpVersion, $configuration->integerBits, $configuration->floatPrecision), new Registrations($contributions1->evaluators, $contributions1->providers, $contributions1->domains, $contributions1->intrinsics, $contributions1->stateSlots), $configuration->standardEvaluators);
277 $settings = new EvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators, array_map((new MicroEvaluatorConversion())->convert(...), $configuration->evaluators));
278 $identity = new SnapshotIdentity($providers->input, $settings, $evaluators, $providers->manifest);
279 $snapshotId = $identity->id;
280 $input1 = $providers->input;
281 $parser = new Parser(new ParserConfig($target->version));
282 $trees = array_map(static fn (Source $source1): File => (new Resolver())->resolve($parser->parse(new Source(Path::normalize($source1->path), $source1->contents, $source1->declarationsOnly))), iterator_to_array($input1, false));
283 $program = new Index($snapshotId, $trees);
284 $result = (new QueryExecution($program, new ExecutionEvaluatorConfig($target, $providers->environment, $configuration->standardEvaluators), $evaluators, $identity->snapshot(array_map(Frontier::fromDiagnostic(...), $program->diagnostics()))))->derive(new ReturnQuery($symbol));
285 self::assertSame([0 => 'left', 'x' => 2, 1 => 'right'], $result->normalOutcomes[0]->values['return']->native());
286 self::assertSame([], $result->frontiers);
287 self::assertSame([], $result->exceptionalOutcomes);
288 }
289 public function testParametersKeepsRequiredRandomBoundsDistinctFromOmittedRandBounds(): void
290 {
291 $library = new Library();
292 $random = $library->parameters('random_int');
293 $rand = $library->parameters('rand');
294 self::assertNotNull($random);
295 self::assertNotNull($rand);
296 self::assertNull($random[0]->default);
297 self::assertSame(TermKind::Omitted, $rand[0]->default?->kind);
298 }
299 public function testArraysDeclaresSortWritesAndNamedCollectionArguments(): void
300 {
301 $library = new Library();
302 $sort = $library->arrays('sort');
303 self::assertNotNull($sort);
304 self::assertTrue($sort[0]->byReference);
305 self::assertSame(['array', 'callback', 'initial'], array_column($library->arrays('array_reduce') ?? [], 'name'));
306 self::assertNull($library->arrays('unknown'));
307 }
308 /**
309 * @param list<string> $types Accepted target type alternatives
310 * @param scalar|null $default Captured target default
311 */
312 #[DataProvider('providerNativeParameters')]
313 public function testParametersMatchCapturedPhp83Signatures(string $function, int $index, int $count, string $name, array $types, bool $reference, bool $variadic, string $defaultKind, int|float|string|bool|null $default): void
314 {
315 $parameters = (new Library())->parameters($function);
316 self::assertNotNull($parameters);
317 self::assertCount($count, $parameters);
318 $parameter = $parameters[$index];
319 self::assertSame($name, $parameter->name);
320 self::assertEqualsCanonicalizing($types, explode('|', $parameter->type));
321 self::assertSame($reference, $parameter->byReference);
322 self::assertSame($variadic, $parameter->variadic);
323 self::assertSame($defaultKind, $parameter->default->kind->value ?? 'required');
324 self::assertSame($default, $parameter->default?->literal);
325 }
326 /**
327 * @return array<string,array{string,int,int,string,list<string>,bool,bool,string,scalar|null}>
328 */
329 public static function providerNativeParameters(): array
330 {
331 return ['strlen:string' => ['strlen', 0, 1, 'string', ['string'], false, false, 'required', null], 'strtolower:string' => ['strtolower', 0, 1, 'string', ['string'], false, false, 'required', null], 'strtoupper:string' => ['strtoupper', 0, 1, 'string', ['string'], false, false, 'required', null], 'trim:string' => ['trim', 0, 2, 'string', ['string'], false, false, 'required', null], 'trim:characters' => ['trim', 1, 2, 'characters', ['string'], false, false, 'constant', " \n\r\t\v\x00"], 'implode:separator' => ['implode', 0, 2, 'separator', ['array', 'string'], false, false, 'required', null], 'implode:array' => ['implode', 1, 2, 'array', ['array', 'null'], false, false, 'constant', null], 'join:separator' => ['join', 0, 2, 'separator', ['array', 'string'], false, false, 'required', null], 'join:array' => ['join', 1, 2, 'array', ['array', 'null'], false, false, 'constant', null], 'explode:separator' => ['explode', 0, 3, 'separator', ['string'], false, false, 'required', null], 'explode:string' => ['explode', 1, 3, 'string', ['string'], false, false, 'required', null], 'explode:limit' => ['explode', 2, 3, 'limit', ['int'], false, false, 'constant', 9223372036854775807], 'substr:string' => ['substr', 0, 3, 'string', ['string'], false, false, 'required', null], 'substr:offset' => ['substr', 1, 3, 'offset', ['int'], false, false, 'required', null], 'substr:length' => ['substr', 2, 3, 'length', ['int', 'null'], false, false, 'constant', null], 'sprintf:format' => ['sprintf', 0, 2, 'format', ['string'], false, false, 'required', null], 'sprintf:values' => ['sprintf', 1, 2, 'values', ['mixed'], false, true, 'required', null], 'str_replace:search' => ['str_replace', 0, 4, 'search', ['array', 'string'], false, false, 'required', null], 'str_replace:replace' => ['str_replace', 1, 4, 'replace', ['array', 'string'], false, false, 'required', null], 'str_replace:subject' => ['str_replace', 2, 4, 'subject', ['array', 'string'], false, false, 'required', null], 'str_replace:count' => ['str_replace', 3, 4, 'count', ['mixed'], true, false, 'constant', null], 'getenv:name' => ['getenv', 0, 2, 'name', ['string', 'null'], false, false, 'constant', null], 'getenv:local_only' => ['getenv', 1, 2, 'local_only', ['bool'], false, false, 'constant', false], 'random_int:min' => ['random_int', 0, 2, 'min', ['int'], false, false, 'required', null], 'random_int:max' => ['random_int', 1, 2, 'max', ['int'], false, false, 'required', null], 'mt_rand:min' => ['mt_rand', 0, 2, 'min', ['int'], false, false, 'omitted', null], 'mt_rand:max' => ['mt_rand', 1, 2, 'max', ['int'], false, false, 'omitted', null], 'rand:min' => ['rand', 0, 2, 'min', ['int'], false, false, 'omitted', null], 'rand:max' => ['rand', 1, 2, 'max', ['int'], false, false, 'omitted', null], 'get_class:object' => ['get_class', 0, 1, 'object', ['object'], false, false, 'omitted', null], 'microtime:as_float' => ['microtime', 0, 1, 'as_float', ['bool'], false, false, 'constant', false], 'is_array:value' => ['is_array', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_string:value' => ['is_string', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_int:value' => ['is_int', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_integer:value' => ['is_integer', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_float:value' => ['is_float', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_double:value' => ['is_double', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_bool:value' => ['is_bool', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_null:value' => ['is_null', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_object:value' => ['is_object', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_numeric:value' => ['is_numeric', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_scalar:value' => ['is_scalar', 0, 1, 'value', ['mixed'], false, false, 'required', null], 'is_callable:value' => ['is_callable', 0, 3, 'value', ['mixed'], false, false, 'required', null], 'is_callable:syntax_only' => ['is_callable', 1, 3, 'syntax_only', ['bool'], false, false, 'constant', false], 'is_callable:callable_name' => ['is_callable', 2, 3, 'callable_name', ['mixed'], true, false, 'omitted', null], 'count:value' => ['count', 0, 2, 'value', ['Countable', 'array'], false, false, 'required', null], 'count:mode' => ['count', 1, 2, 'mode', ['int'], false, false, 'constant', 0], 'array_values:array' => ['array_values', 0, 1, 'array', ['array'], false, false, 'required', null], 'array_keys:array' => ['array_keys', 0, 3, 'array', ['array'], false, false, 'required', null], 'array_keys:filter_value' => ['array_keys', 1, 3, 'filter_value', ['mixed'], false, false, 'omitted', null], 'array_keys:strict' => ['array_keys', 2, 3, 'strict', ['bool'], false, false, 'constant', false], 'array_merge:arrays' => ['array_merge', 0, 1, 'arrays', ['array'], false, true, 'required', null], 'in_array:needle' => ['in_array', 0, 3, 'needle', ['mixed'], false, false, 'required', null], 'in_array:haystack' => ['in_array', 1, 3, 'haystack', ['array'], false, false, 'required', null], 'in_array:strict' => ['in_array', 2, 3, 'strict', ['bool'], false, false, 'constant', false], 'array_key_exists:key' => ['array_key_exists', 0, 2, 'key', ['mixed'], false, false, 'required', null], 'array_key_exists:array' => ['array_key_exists', 1, 2, 'array', ['array'], false, false, 'required', null], 'array_map:callback' => ['array_map', 0, 3, 'callback', ['callable', 'null'], false, false, 'required', null], 'array_map:array' => ['array_map', 1, 3, 'array', ['array'], false, false, 'required', null], 'array_map:arrays' => ['array_map', 2, 3, 'arrays', ['array'], false, true, 'required', null], 'array_filter:array' => ['array_filter', 0, 3, 'array', ['array'], false, false, 'required', null], 'array_filter:callback' => ['array_filter', 1, 3, 'callback', ['callable', 'null'], false, false, 'constant', null], 'array_filter:mode' => ['array_filter', 2, 3, 'mode', ['int'], false, false, 'constant', 0], 'array_reduce:array' => ['array_reduce', 0, 3, 'array', ['array'], false, false, 'required', null], 'array_reduce:callback' => ['array_reduce', 1, 3, 'callback', ['callable'], false, false, 'required', null], 'array_reduce:initial' => ['array_reduce', 2, 3, 'initial', ['mixed'], false, false, 'constant', null], 'sort:array' => ['sort', 0, 2, 'array', ['array'], true, false, 'required', null], 'sort:flags' => ['sort', 1, 2, 'flags', ['int'], false, false, 'constant', 0], 'array_shift:array' => ['array_shift', 0, 1, 'array', ['array'], true, false, 'required', null], 'array_pop:array' => ['array_pop', 0, 1, 'array', ['array'], true, false, 'required', null], 'array_push:array' => ['array_push', 0, 2, 'array', ['array'], true, false, 'required', null], 'array_push:values' => ['array_push', 1, 2, 'values', ['mixed'], false, true, 'required', null], 'array_unshift:array' => ['array_unshift', 0, 2, 'array', ['array'], true, false, 'required', null], 'array_unshift:values' => ['array_unshift', 1, 2, 'values', ['mixed'], false, true, 'required', null], 'array_key_first:array' => ['array_key_first', 0, 1, 'array', ['array'], false, false, 'required', null], 'array_key_last:array' => ['array_key_last', 0, 1, 'array', ['array'], false, false, 'required', null], 'array_slice:array' => ['array_slice', 0, 4, 'array', ['array'], false, false, 'required', null], 'array_slice:offset' => ['array_slice', 1, 4, 'offset', ['int'], false, false, 'required', null], 'array_slice:length' => ['array_slice', 2, 4, 'length', ['int', 'null'], false, false, 'constant', null], 'array_slice:preserve_keys' => ['array_slice', 3, 4, 'preserve_keys', ['bool'], false, false, 'constant', false]];
332 }
333 public function testEvaluatorNormalizesQualifiedCaseInsensitiveBuiltinsAndRejectsUnknownNames(): void
334 {
335 $library = new Library();
336 $evaluator = $library->evaluator('\STRLEN');
337 self::assertNotNull($evaluator);
338 self::assertSame('php.strlen', $evaluator->descriptor()->id);
339 self::assertNull($library->evaluator('unregistered_function'));
340 self::assertNull($library->parameters('unregistered_function'));
341 self::assertSame([], $library->parameters('time'));
342 }
343 public function testValueFunctionsUsePhpParameterNames(): void
344 {
345 $parameters = (new Library())->valueFunctions('str_repeat');
346 self::assertNotNull($parameters);
347 self::assertSame(['string', 'times'], array_column($parameters, 'name'));
348 }
349}
350