Skip to content

Commit

Permalink
Merge branch 'hotfix/#353-support-variadic-arguments-in-proxies'
Browse files Browse the repository at this point in the history
  • Loading branch information
Ocramius committed Mar 26, 2015
2 parents 23cdbfa + d6d0007 commit 51af551
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 3 deletions.
38 changes: 35 additions & 3 deletions lib/Doctrine/Common/Proxy/ProxyGenerator.php
Expand Up @@ -789,11 +789,12 @@ private function generateMethods(ClassMetadata $class)
$methods .= ' }' . "\n\n";
}

$callParamsString = implode(', ', $this->getParameterNames($method->getParameters()));
$invokeParamsString = implode(', ', $this->getParameterNamesForInvoke($method->getParameters()));
$callParamsString = implode(', ', $this->getParameterNamesForParentCall($method->getParameters()));

$methods .= "\n \$this->__initializer__ "
. "&& \$this->__initializer__->__invoke(\$this, " . var_export($name, true)
. ", array(" . $callParamsString . "));"
. ", array(" . $invokeParamsString . "));"
. "\n\n return parent::" . $name . '(' . $callParamsString . ');'
. "\n" . ' }' . "\n";
}
Expand Down Expand Up @@ -906,6 +907,12 @@ private function buildParametersString(ClassMetadata $class, \ReflectionMethod $
$parameterDefinition .= '&';
}

if (method_exists($param, 'isVariadic')) {
if ($param->isVariadic()) {
$parameterDefinition .= '...';
}
}

$parameters[] = '$' . $param->getName();
$parameterDefinition .= '$' . $param->getName();

Expand Down Expand Up @@ -961,7 +968,7 @@ private function getParameterType(ClassMetadata $class, \ReflectionMethod $metho
*
* @return string[]
*/
private function getParameterNames(array $parameters)
private function getParameterNamesForInvoke(array $parameters)
{
return array_map(
function (\ReflectionParameter $parameter) {
Expand All @@ -970,4 +977,29 @@ function (\ReflectionParameter $parameter) {
$parameters
);
}

/**
* @param \ReflectionParameter[] $parameters
*
* @return string[]
*/
private function getParameterNamesForParentCall(array $parameters)
{
return array_map(
function (\ReflectionParameter $parameter) {
$name = '';

if (method_exists($parameter, 'isVariadic')) {
if ($parameter->isVariadic()) {
$name .= '...';
}
}

$name .= '$' . $parameter->getName();

return $name;
},
$parameters
);
}
}
21 changes: 21 additions & 0 deletions tests/Doctrine/Tests/Common/Proxy/ProxyClassGeneratorTest.php
Expand Up @@ -163,6 +163,27 @@ public function testClassWithCallableTypeHintOnProxiedMethod()
$this->assertEquals(1, substr_count($classCode, 'call(callable $foo)'));
}

public function testClassWithVariadicArgumentOnProxiedMethod()
{
if (PHP_VERSION_ID < 50600) {
$this->markTestSkipped('`...` is only supported in PHP >=5.6.0');
}

if (!class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\VariadicTypeHintClass', false)) {
$className = 'Doctrine\Tests\Common\Proxy\VariadicTypeHintClass';
$metadata = $this->createClassMetadata($className, array('id'));

$proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true);
$this->generateAndRequire($proxyGenerator, $metadata);
}

$classCode = file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyVariadicTypeHintClass.php');

$this->assertEquals(1, substr_count($classCode, 'function addType(...$types)'));
$this->assertEquals(1, substr_count($classCode, '__invoke($this, \'addType\', array($types))'));
$this->assertEquals(1, substr_count($classCode, 'parent::addType(...$types)'));
}

public function testClassWithInvalidTypeHintOnProxiedMethod()
{
$className = 'Doctrine\Tests\Common\Proxy\InvalidTypeHintClass';
Expand Down
56 changes: 56 additions & 0 deletions tests/Doctrine/Tests/Common/Proxy/ProxyLogicTest.php
Expand Up @@ -591,6 +591,62 @@ function () use ($test) {
$this->assertSame('newPersistentFieldValue', $this->lazyObject->publicPersistentField);
}

public function testCallingVariadicMethodCausesLazyLoading()
{
if (PHP_VERSION_ID < 50600) {
$this->markTestSkipped('Test applies only to PHP 5.6+');
}

$proxyClassName = 'Doctrine\Tests\Common\ProxyProxy\__CG__\Doctrine\Tests\Common\Proxy\VariadicTypeHintClass';

/* @var $metadata \Doctrine\Common\Persistence\Mapping\ClassMetadata|\PHPUnit_Framework_MockObject_MockObject */
$metadata = $this->getMock('Doctrine\Common\Persistence\Mapping\ClassMetadata');

$metadata
->expects($this->any())
->method('getName')
->will($this->returnValue('Doctrine\Tests\Common\Proxy\VariadicTypeHintClass'));
$metadata
->expects($this->any())
->method('getReflectionClass')
->will($this->returnValue(new \ReflectionClass('Doctrine\Tests\Common\Proxy\VariadicTypeHintClass')));

// creating the proxy class
if (!class_exists($proxyClassName, false)) {
$proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy', true);
$proxyGenerator->generateProxyClass($metadata);
require_once $proxyGenerator->getProxyFileName($metadata->getName());
}

/* @var $invocationMock callable|\PHPUnit_Framework_MockObject_MockObject */
$invocationMock = $this->getMock('stdClass', array('__invoke'));

/* @var $lazyObject \Doctrine\Tests\Common\Proxy\VariadicTypeHintClass */
$lazyObject = new $proxyClassName(
function ($proxy, $method, $parameters) use ($invocationMock) {
$invocationMock($proxy, $method, $parameters);
},
function () {}
);

$invocationMock
->expects($this->at(0))
->method('__invoke')
->with($lazyObject, 'addType', array(array('type1', 'type2')));
$invocationMock
->expects($this->at(1))
->method('__invoke')
->with($lazyObject, 'addTypeWithMultipleParameters', array('foo', 'bar', array('baz1', 'baz2')));

$lazyObject->addType('type1', 'type2');
$this->assertSame(array('type1', 'type2'), $lazyObject->types);

$lazyObject->addTypeWithMultipleParameters('foo', 'bar', 'baz1', 'baz2');
$this->assertSame('foo', $lazyObject->foo);
$this->assertSame('bar', $lazyObject->bar);
$this->assertSame(array('baz1', 'baz2'), $lazyObject->baz);
}

/**
* Converts a given callable into a closure
*
Expand Down
29 changes: 29 additions & 0 deletions tests/Doctrine/Tests/Common/Proxy/VariadicTypeHintClass.php
@@ -0,0 +1,29 @@
<?php

namespace Doctrine\Tests\Common\Proxy;

/**
* Test asset class
*/
class VariadicTypeHintClass
{
public $types;
public $foo;
public $bar;
public $baz;

/**
* @param ...$types
*/
public function addType(...$types)
{
$this->types = $types;
}

public function addTypeWithMultipleParameters($foo, $bar, ...$baz)
{
$this->foo = $foo;
$this->bar = $bar;
$this->baz = $baz;
}
}

0 comments on commit 51af551

Please sign in to comment.