Skip to content

Commit 77359d0

Browse files
authored
Add support for variadic parameters and null values (#1849)
1 parent 04dd53f commit 77359d0

2 files changed

Lines changed: 123 additions & 1 deletion

File tree

src/FrameBuilder.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,18 @@ private function getFunctionArgumentValues(\ReflectionFunctionAbstract $reflecti
219219
foreach ($reflectionFunction->getParameters() as $reflectionParameter) {
220220
$parameterPosition = $reflectionParameter->getPosition();
221221

222-
if (!isset($backtraceFrameArgs[$parameterPosition])) {
222+
if ($reflectionParameter->isVariadic()) {
223+
// For variadic parameters, collect all remaining arguments into an array
224+
$variadicArgs = [];
225+
for ($i = $parameterPosition; $i < \count($backtraceFrameArgs); ++$i) {
226+
$variadicArgs[] = $backtraceFrameArgs[$i];
227+
}
228+
$argumentValues[$reflectionParameter->getName()] = $variadicArgs;
229+
// Variadic parameter is always the last one, so we can break
230+
break;
231+
}
232+
233+
if (!\array_key_exists($parameterPosition, $backtraceFrameArgs)) {
223234
continue;
224235
}
225236

tests/FrameBuilderTest.php

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,4 +284,115 @@ public static function addFrameSetsInAppFlagCorrectlyDataProvider(): \Generator
284284
false,
285285
];
286286
}
287+
288+
public function testGetFunctionArgumentsWithVariadicParameters(): void
289+
{
290+
$options = new Options([]);
291+
$frameBuilder = new FrameBuilder($options, new RepresentationSerializer($options));
292+
293+
$testFunction = function (string $first, int $second, ...$rest) {
294+
};
295+
296+
$backtraceFrame = [
297+
'function' => 'testVariadicFunction',
298+
'args' => ['hello', 42, 'extra1', 'extra2', 'extra3'],
299+
];
300+
301+
$reflectionClass = new \ReflectionClass($frameBuilder);
302+
$getFunctionArgumentsMethod = $reflectionClass->getMethod('getFunctionArguments');
303+
$getFunctionArgumentsMethod->setAccessible(true);
304+
305+
$reflectionFunction = new \ReflectionFunction($testFunction);
306+
307+
$getFunctionArgumentValuesMethod = $reflectionClass->getMethod('getFunctionArgumentValues');
308+
$getFunctionArgumentValuesMethod->setAccessible(true);
309+
310+
$result = $getFunctionArgumentValuesMethod->invoke($frameBuilder, $reflectionFunction, $backtraceFrame['args']);
311+
312+
$this->assertSame('hello', $result['first']);
313+
$this->assertSame(42, $result['second']);
314+
315+
$this->assertArrayHasKey('rest', $result);
316+
$this->assertSame(['extra1', 'extra2', 'extra3'], $result['rest']);
317+
}
318+
319+
public function testGetFunctionArgumentsWithOnlyVariadicParameters(): void
320+
{
321+
$options = new Options([]);
322+
$frameBuilder = new FrameBuilder($options, new RepresentationSerializer($options));
323+
324+
$testFunction = function (...$args) {
325+
};
326+
327+
$backtraceFrame = [
328+
'function' => 'testOnlyVariadicFunction',
329+
'args' => ['arg1', 'arg2', 'arg3'],
330+
];
331+
332+
$reflectionClass = new \ReflectionClass($frameBuilder);
333+
$getFunctionArgumentValuesMethod = $reflectionClass->getMethod('getFunctionArgumentValues');
334+
$getFunctionArgumentValuesMethod->setAccessible(true);
335+
336+
$reflectionFunction = new \ReflectionFunction($testFunction);
337+
338+
$result = $getFunctionArgumentValuesMethod->invoke($frameBuilder, $reflectionFunction, $backtraceFrame['args']);
339+
340+
$this->assertArrayHasKey('args', $result);
341+
$this->assertSame(['arg1', 'arg2', 'arg3'], $result['args']);
342+
}
343+
344+
public function testGetFunctionArgumentsWithEmptyVariadicParameters(): void
345+
{
346+
$options = new Options([]);
347+
$frameBuilder = new FrameBuilder($options, new RepresentationSerializer($options));
348+
349+
$testFunction = function (string $first, ...$rest) {
350+
};
351+
352+
$backtraceFrame = [
353+
'function' => 'testEmptyVariadicFunction',
354+
'args' => ['hello'],
355+
];
356+
357+
$reflectionClass = new \ReflectionClass($frameBuilder);
358+
$getFunctionArgumentValuesMethod = $reflectionClass->getMethod('getFunctionArgumentValues');
359+
$getFunctionArgumentValuesMethod->setAccessible(true);
360+
361+
$reflectionFunction = new \ReflectionFunction($testFunction);
362+
363+
$result = $getFunctionArgumentValuesMethod->invoke($frameBuilder, $reflectionFunction, $backtraceFrame['args']);
364+
365+
$this->assertSame('hello', $result['first']);
366+
367+
$this->assertArrayHasKey('rest', $result);
368+
$this->assertSame([], $result['rest']);
369+
}
370+
371+
public function testGetFunctionArgumentsWithNullValues(): void
372+
{
373+
$options = new Options([]);
374+
$frameBuilder = new FrameBuilder($options, new RepresentationSerializer($options));
375+
376+
$testFunction = function (string $first, $second, ...$rest) {
377+
};
378+
379+
$backtraceFrame = [
380+
'function' => 'testNullFunction',
381+
'args' => ['hello', null, 'extra1', null, 'extra3'],
382+
];
383+
384+
$reflectionClass = new \ReflectionClass($frameBuilder);
385+
$getFunctionArgumentValuesMethod = $reflectionClass->getMethod('getFunctionArgumentValues');
386+
$getFunctionArgumentValuesMethod->setAccessible(true);
387+
388+
$reflectionFunction = new \ReflectionFunction($testFunction);
389+
390+
$result = $getFunctionArgumentValuesMethod->invoke($frameBuilder, $reflectionFunction, $backtraceFrame['args']);
391+
392+
$this->assertSame('hello', $result['first']);
393+
$this->assertNull($result['second']);
394+
395+
$this->assertArrayHasKey('rest', $result);
396+
$this->assertSame(['extra1', null, 'extra3'], $result['rest']);
397+
}
287398
}

0 commit comments

Comments
 (0)