Skip to content
This repository was archived by the owner on Jul 8, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Feature/FeatureDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@ public function standardFeatures()
->checkInternalMethod('ReflectionParameter', 'isCallable');
},

'parameter.variadic' => function ($detector) {
return $detector
->checkStatement('function (...$a) {};');
},

'reflection.function.export.default.array' => function ($detector) {
$function =
new ReflectionFunction(function ($a0 = array('a')) {});
Expand Down
24 changes: 15 additions & 9 deletions src/Mock/Generator/MockGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ public static function __callStatic(
$parameter[1] .
'$a' .
++$index .
$parameter[2];
$parameter[3];
}

$source .= <<<'EOD'
Expand Down Expand Up @@ -408,19 +408,24 @@ protected function generateMethods(array $methods)
}

$parameterCount = count($signature);
$variadic = false;

if ($signature) {
$argumentPacking = "\n";
$index = -1;

foreach ($signature as $parameter) {
$argumentPacking .= "\n if (\$argumentCount > " .
++$index .
") {\n \$arguments[] = " .
$parameter[1] .
'$a' .
$index .
";\n }";
if ($parameter[2]) {
$parameterCount--;
} else {
$argumentPacking .= "\n if (\$argumentCount > " .
++$index .
") {\n \$arguments[] = " .
$parameter[1] .
'$a' .
$index .
";\n }";
}
}
} else {
$argumentPacking = '';
Expand Down Expand Up @@ -475,9 +480,10 @@ protected function generateMethods(array $methods)

$source .= $parameter[0] .
$parameter[1] .
$parameter[2] .
'$a' .
++$index .
$parameter[2];
$parameter[3];
}

$source .= "\n ) {\n";
Expand Down
16 changes: 13 additions & 3 deletions src/Reflection/FunctionSignatureInspector.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*/
class FunctionSignatureInspector implements FunctionSignatureInspectorInterface
{
const PARAMETER_PATTERN = '/^\s*Parameter #\d+ \[ <(required|optional)> (\S+ )?(?:or NULL )?(&)?\$(\S+)( = [^\]]+)? ]$/m';
const PARAMETER_PATTERN = '/^\s*Parameter #\d+ \[ <(required|optional)> (\S+ )?(?:or NULL )?(&)?(?:\.\.\.)?\$(\S+)( = [^\]]+)? ]$/m';

/**
* Get the static instance of this inspector.
Expand Down Expand Up @@ -63,6 +63,8 @@ public function __construct(
->isSupported('reflection.function.export.default.array');
$this->isExportReferenceSupported = $featureDetector
->isSupported('reflection.function.export.reference');
$this->isVariadicParameterSupported = $featureDetector
->isSupported('parameter.variadic');
$this->isHhvm = $featureDetector->isSupported('runtime.hhvm');
}

Expand Down Expand Up @@ -143,6 +145,14 @@ public function signature(ReflectionFunctionAbstract $function)
$byReference = $parameter->isPassedByReference() ? '&' : '';
} // @codeCoverageIgnoreEnd

if ($this->isVariadicParameterSupported && $parameter->isVariadic()) {
$variadic = '...';
$optional = false;
} else {
$variadic = '';
$optional = 'optional' === $match[1];
}

if (isset($match[5])) {
if (
!$this->isExportDefaultArraySupported &&
Expand All @@ -164,14 +174,14 @@ public function signature(ReflectionFunctionAbstract $function)
$defaultValue =
str_replace('array (', 'array(', $defaultValue);
}
} elseif ('optional' === $match[1]) {
} elseif ($optional) {
$defaultValue = ' = null';
} else {
$defaultValue = '';
}

$signature[$match[4]] =
array($typehint, $byReference, $defaultValue);
array($typehint, $byReference, $variadic, $defaultValue);
}

return $signature;
Expand Down
1 change: 1 addition & 0 deletions test/fixture/feature-detector/features.fixie.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ data:
Can yield no value: [generator.yield.nothing, '5.5.x', '999', [], '3.4.x', '999', [] ]
Can use a constant as a parameter default value: [parameter.default.constant, '5.4.6.x', '999', [], '999', '0.x', [] ]
Can type hint a parameter as callable: [parameter.type.callable, '5.4.x', '999', [], '0.x', '999', [] ]
Can use variadic parameters: [parameter.variadic, '5.6.x', '999', [], '3.3.x', '999', [] ]
Reflection function exports include full array default value: [reflection.function.export.default.array, '999', '0.x', [], '3.2.x', '999', [] ]
Reflection function exports include by-reference information: [reflection.function.export.reference, '0.x', '999', [], '3.4.x', '999', [] ]
HHVM runtime: [runtime.hhvm, '999', '0.x', [], '0.x', '999', [] ]
Expand Down
17 changes: 17 additions & 0 deletions test/src/Test/TestInterfaceWithVariadicParameter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/*
* This file is part of the Phony package.
*
* Copyright © 2015 Erin Millard
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

namespace Eloquent\Phony\Test;

interface TestInterfaceWithVariadicParameter
{
public function method(...$arguments);
}
19 changes: 19 additions & 0 deletions test/suite/FunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,25 @@ public function testMockMocking()
$this->assertNotInstanceOf(get_class($mockMock->mock()), $mock->mock());
}

public function testVariadicParameterMocking()
{
if (!$this->featureDetector->isSupported('parameter.variadic')) {
$this->markTestSkipped('Requires variadic parameters.');
}

$mock = x\mock('Eloquent\Phony\Test\TestInterfaceWithVariadicParameter');
$mock->method->does(
function () {
return func_get_args();
}
);

$this->assertSame(
array(1, 2, 3),
$mock->mock()->method(1, 2, 3)
);
}

public function testSpyStatic()
{
$spy = Phony::spy();
Expand Down
69 changes: 44 additions & 25 deletions test/suite/Reflection/FunctionSignatureInspectorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,22 @@ function (
);
$actual = $this->subject->signature($function);
$expected = array(
'a' => array('', '', ''),
'b' => array('', '&', ''),
'c' => array('array ', '', ''),
'd' => array('array ', '&', ''),
'e' => array('\Type ', '', ''),
'f' => array('\Type ', '&', ''),
'g' => array('\Namespaced\Type ', '', ''),
'h' => array('\Namespaced\Type ', '&', ''),
'i' => array('\Eloquent\Phony\Feature\FeatureDetector ', '', ''),
'j' => array('', '', " = 'string'"),
'k' => array('', '&', ' = 111'),
'm' => array('array ', '&', ' = null'),
'n' => array('\Type ', '', ' = null'),
'o' => array('\Type ', '&', ' = null'),
'p' => array('\Namespaced\Type ', '', ' = null'),
'q' => array('\Namespaced\Type ', '&', ' = null'),
'a' => array('', '', '', ''),
'b' => array('', '&', '', ''),
'c' => array('array ', '', '', ''),
'd' => array('array ', '&', '', ''),
'e' => array('\Type ', '', '', ''),
'f' => array('\Type ', '&', '', ''),
'g' => array('\Namespaced\Type ', '', '', ''),
'h' => array('\Namespaced\Type ', '&', '', ''),
'i' => array('\Eloquent\Phony\Feature\FeatureDetector ', '', '', ''),
'j' => array('', '', '', " = 'string'"),
'k' => array('', '&', '', ' = 111'),
'm' => array('array ', '&', '', ' = null'),
'n' => array('\Type ', '', '', ' = null'),
'o' => array('\Type ', '&', '', ' = null'),
'p' => array('\Namespaced\Type ', '', '', ' = null'),
'q' => array('\Namespaced\Type ', '&', '', ' = null'),
);

$this->assertEquals($expected, $actual);
Expand All @@ -105,15 +105,15 @@ public function testSignatureWithArrayDefault()
$actual = $this->subject->signature($function);

$this->assertArrayHasKey('a', $actual);
$this->assertSame(array('a', 'b', 'c' => 'd'), eval('return $r' . $actual['a'][2] . ';'));
$this->assertSame(array('a', 'b', 'c' => 'd'), eval('return $r' . $actual['a'][3] . ';'));
}

public function testSignatureWithUnavailableDefaultValue()
{
$function = new ReflectionMethod('ReflectionClass', 'getMethods');
$actual = $this->subject->signature($function);
$expected = array(
'filter' => array('', '', ' = null'),
'filter' => array('', '', '', ' = null'),
);

$this->assertEquals($expected, $actual);
Expand All @@ -131,8 +131,8 @@ public function testSignatureWithCallableTypeHint()
);
$actual = $this->subject->signature($function);
$expected = array(
'a' => array('callable ', '', ''),
'b' => array('callable ', '', ' = null'),
'a' => array('callable ', '', '', ''),
'b' => array('callable ', '', '', ' = null'),
);

$this->assertEquals($expected, $actual);
Expand All @@ -148,8 +148,8 @@ public function testSignatureWithConstantDefault()
$function = new ReflectionMethod($this, 'methodA');
$actual = $this->subject->signature($function);
$expected = array(
'a' => array('', '', ' = 4'),
'b' => array('', '', " = 'a'"),
'a' => array('', '', '', ' = 4'),
'b' => array('', '', '', " = 'a'"),
);

$this->assertEquals($expected, $actual);
Expand All @@ -161,7 +161,7 @@ public function testSignatureWithSelfTypeHint()
$function = new ReflectionMethod($this, 'methodB');
$actual = $this->subject->signature($function);
$expected = array(
'a' => array('\Eloquent\Phony\Reflection\FunctionSignatureInspectorTest ', '', ''),
'a' => array('\Eloquent\Phony\Reflection\FunctionSignatureInspectorTest ', '', '', ''),
);

$this->assertEquals($expected, $actual);
Expand All @@ -172,14 +172,33 @@ public function testCallbackSignature()
{
$callback = function ($a, array $b = null) {};
$expected = array(
'a' => array('', '', ''),
'b' => array('array ', '', ' = null'),
'a' => array('', '', '', ''),
'b' => array('array ', '', '', ' = null'),
);
$actual = $this->subject->callbackSignature($callback);

$this->assertSame($actual, $expected);
}

public function testSignatureWithVariadicParameter()
{
if (!$this->featureDetector->isSupported('parameter.variadic')) {
$this->markTestSkipped('Requires variadic parameters.');
}

$code = 'return function (...$a) { return $args; };';
$callback = eval($code);
$function = new ReflectionFunction($callback);

$actual = $this->subject->signature($function);
$expected = array(
'a' => array('', '', '...', ''),
);

$this->assertEquals($expected, $actual);
$this->assertSame($expected, $actual);
}

protected function methodA($a = ReflectionMethod::IS_FINAL, $b = self::CONSTANT_A)
{
}
Expand Down
18 changes: 18 additions & 0 deletions test/suite/Stub/StubTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1096,4 +1096,22 @@ public function testInvokeWithWithReferenceParameters()
$this->assertSame('c', $c);
$this->assertSame('d', $d);
}

public function testStubWithVariadicParameter()
{
if (!$this->featureDetector->isSupported('parameter.variadic')) {
$this->markTestSkipped('Requires variadic parameters.');
}

$code = 'return function (...$args) { return $args; };';
$callback = eval($code);

$this->subject = new Stub($callback);
$this->subject->forwards();

$this->assertSame(
array(1, 2, 3),
call_user_func($this->subject, 1, 2, 3)
);
}
}