Skip to content

Commit

Permalink
Fix enums and new in initializers as default values on PHP 8.1
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Apr 28, 2022
1 parent c828ced commit 3661a2d
Show file tree
Hide file tree
Showing 21 changed files with 265 additions and 106 deletions.
2 changes: 1 addition & 1 deletion src/ProxyManager/Factory/AbstractBaseFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

namespace ProxyManager\Factory;

use Laminas\Code\Generator\ClassGenerator;
use OutOfBoundsException;
use ProxyManager\Configuration;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
use ProxyManager\Signature\Exception\InvalidSignatureException;
use ProxyManager\Signature\Exception\MissingSignatureException;
Expand Down
37 changes: 35 additions & 2 deletions src/ProxyManager/Generator/ClassGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,47 @@
namespace ProxyManager\Generator;

use Laminas\Code\Generator\ClassGenerator as LaminasClassGenerator;
use ProxyManager\Generator\ValueGenerator;
use ReflectionParameter;

use function method_exists;

/**
* Class generator that ensures that interfaces/classes that are implemented/extended are FQCNs
*
* @internal do not use this in your code: it is only here for internal use
* @deprecated this class was in use due to parent implementation not receiving prompt bugfixes, but
* `laminas/laminas-code` is actively maintained and receives quick release iterations.
*/
class ClassGenerator extends LaminasClassGenerator
{
public function generate(): string
{
$extendedClass = $this->getExtendedClass();

foreach ($this->getMethods() as $method) {
$class = $extendedClass;

if ($class === null) {
foreach ($this->getImplementedInterfaces() as $interface) {
if (method_exists($interface, $method->getName())) {
$class = $interface;
break;
}
}
}

if ($class === null || ! method_exists($class, $method->getName())) {
continue;
}

foreach ($method->getParameters() as $parameter) {
$default = $parameter->getDefaultValue();

if ($default !== null) {
$parameter->setDefaultValue(new ValueGenerator($default, new ReflectionParameter([$class, $method->getName()], $parameter->getName())));
}
}
}

return parent::generate();
}
}
65 changes: 65 additions & 0 deletions src/ProxyManager/Generator/ValueGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

declare(strict_types=1);

namespace ProxyManager\Generator;

use Laminas\Code\Generator\ValueGenerator as LaminasValueGenerator;
use Laminas\Code\Generator\Exception\RuntimeException;
use ReflectionParameter;

use function implode;
use function is_array;
use function preg_replace;
use function preg_split;

use const PREG_SPLIT_DELIM_CAPTURE;

/**
* @internal do not use this in your code: it is only here for internal use
*/
class ValueGenerator extends LaminasValueGenerator
{
private $reflection;

public function __construct($value, ?ReflectionParameter $reflection = null)
{
if ($value instanceof LaminasValueGenerator) {
foreach ($value as $k => $v) {
$this->$k = $v;
}
} else {
parent::__construct($value, parent::TYPE_AUTO, parent::OUTPUT_SINGLE_LINE);
}

$this->reflection = $reflection;
}

public function generate(): string
{
try {
return parent::generate();
} catch (RuntimeException $e) {
if ($this->reflection) {
$value = rtrim(substr(explode('$' . $this->reflection->name . ' = ', (string) $this->reflection, 2)[1], 0, -2));
} else {
$value = var_export($this->value, true);
}

return self::fixExport($value);
}
}

private static function fixExport(string $value): string
{
$parts = preg_split('{(\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')}', $value, -1, PREG_SPLIT_DELIM_CAPTURE);

foreach ($parts as $i => &$part) {
if ($part !== '' && $i % 2 === 0) {
$part = preg_replace('/(?(DEFINE)(?<V>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+))(?<!\\\\)(?&V)(?:\\\\(?&V))*+::/', '\\\\$0', $part);
}
}

return implode('', $parts);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Laminas\Code\Generator\PropertyGenerator;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\Util\IdentifierSuffixer;
use ProxyManager\Generator\ValueGenerator;
use ProxyManager\ProxyGenerator\Util\Properties;
use ReflectionProperty;

Expand Down Expand Up @@ -170,6 +171,6 @@ private function getExportedPropertyDefaultValue(ReflectionProperty $property):
$name = $property->getName();
$defaults = $property->getDeclaringClass()->getDefaultProperties();

return var_export($defaults[$name] ?? null, true);
return (new ValueGenerator($defaults[$name] ?? null))->generate();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Laminas\Code\Reflection\MethodReflection;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\Util\ProxiedMethodReturnExpression;
use ProxyManager\Generator\ValueGenerator;
use ReflectionClass;

use function count;
Expand All @@ -21,10 +22,10 @@ class RemoteObjectMethod extends MethodGenerator
{
private const TEMPLATE
= <<<'PHP'
$defaultValues = #DEFAULT_VALUES#;
$declaredParameterCount = #PARAMETER_COUNT#;
$args = \func_get_args();
$args = \func_get_args() + $defaultValues;
switch (\func_num_args()) {#DEFAULT_VALUES#
}
#PROXIED_RETURN#
PHP;
Expand All @@ -41,39 +42,36 @@ public static function generateMethod(
. ', ' . var_export($originalMethod->getName(), true) . ', $args);' . "\n\n"
. ProxiedMethodReturnExpression::generate('$return', $originalMethod);

$defaultValues = self::getDefaultValuesForMethod($originalMethod);
$declaredParameterCount = count($originalMethod->getParameters());
$defaultValues = self::getDefaultValuesForMethod($originalMethod);

$method->setBody(
strtr(
self::TEMPLATE,
[
'#PROXIED_RETURN#' => $proxiedReturn,
'#DEFAULT_VALUES#' => var_export($defaultValues, true),
'#PARAMETER_COUNT#' => var_export($declaredParameterCount, true),
'#DEFAULT_VALUES#' => $defaultValues,
]
)
);

return $method;
}

/** @psalm-return list<int|float|bool|array|string|null> */
private static function getDefaultValuesForMethod(MethodReflection $originalMethod): array
private static function getDefaultValuesForMethod(MethodReflection $originalMethod): string
{
$defaultValues = [];
foreach ($originalMethod->getParameters() as $parameter) {
$defaultValues = '';
foreach ($originalMethod->getParameters() as $i => $parameter) {
if ($parameter->isOptional() && $parameter->isDefaultValueAvailable()) {
/** @psalm-var int|float|bool|array|string|null */
$defaultValues[] = $parameter->getDefaultValue();
$default = new ValueGenerator($parameter->getDefaultValue(), $parameter);
$defaultValues .= sprintf("\n case $i: \$args[] = %s;", $default->generate());
continue;
}

if ($parameter->isVariadic()) {
continue;
}

$defaultValues[] = null;
$defaultValues .= "\n case $i: \$args[] = null;";
}

return $defaultValues;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

namespace ProxyManagerTest\Functional;

use Laminas\Code\Generator\ClassGenerator;
use PHPUnit\Framework\TestCase;
use ProxyManager\Exception\ExceptionInterface;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\Proxy\ProxyInterface;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizerGenerator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use ProxyManagerTestAsset\ClassWithMixedTypedProperties;
use ProxyManagerTestAsset\ClassWithParentHint;
use ProxyManagerTestAsset\ClassWithPhp80TypedMethods;
use ProxyManagerTestAsset\ClassWithPhp81Defaults;
use ProxyManagerTestAsset\ClassWithPrivateProperties;
use ProxyManagerTestAsset\ClassWithProtectedProperties;
use ProxyManagerTestAsset\ClassWithPublicProperties;
Expand Down Expand Up @@ -138,6 +139,10 @@ public function getTestedClasses(): array
$objects[] = [new ClassWithPhp80TypedMethods()];
}

if (PHP_VERSION_ID >= 80100) {
$objects['php81defaults'] = [new ClassWithPhp81Defaults()];
}

return $objects;
}
}
22 changes: 21 additions & 1 deletion tests/ProxyManagerTest/Functional/RemoteObjectFunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@
use ProxyManagerTestAsset\OtherObjectAccessClass;
use ProxyManagerTestAsset\RemoteProxy\BazServiceInterface;
use ProxyManagerTestAsset\RemoteProxy\Foo;
use ProxyManagerTestAsset\RemoteProxy\FooEnum;
use ProxyManagerTestAsset\RemoteProxy\FooServiceInterface;
use ProxyManagerTestAsset\RemoteProxy\RemoteServiceWithDefaultsAndVariadicArguments;
use ProxyManagerTestAsset\RemoteProxy\RemoteServiceWithDefaultsInterface;
use ProxyManagerTestAsset\RemoteProxy\RemoteServiceWithPhp81DefaultsInterface;
use ProxyManagerTestAsset\RemoteProxy\VariadicArgumentsServiceInterface;
use ProxyManagerTestAsset\VoidCounter;
use ReflectionClass;
use stdClass;

use function assert;
use function get_class;
Expand All @@ -32,6 +35,8 @@
use function ucfirst;
use function uniqid;

use const PHP_VERSION_ID;

/**
* Tests for {@see \ProxyManager\ProxyGenerator\RemoteObjectGenerator} produced objects
*
Expand Down Expand Up @@ -151,7 +156,7 @@ public function getProxyMethods(): array
{
$selfHintParam = new ClassWithSelfHint();

return [
$methods = [
[
FooServiceInterface::class,
'foo',
Expand Down Expand Up @@ -273,6 +278,21 @@ public function getProxyMethods(): array
200,
],
];

if (PHP_VERSION_ID >= 80100) {
$methods['when using php8.1 defaults'] = [
RemoteServiceWithPhp81DefaultsInterface::class,
'php81Defaults',
[],
[
FooEnum::bar,
new stdClass(),
],
200,
];
}

return $methods;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

namespace ProxyManagerTest\GeneratorStrategy;

use Laminas\Code\Generator\ClassGenerator;
use PHPUnit\Framework\TestCase;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\GeneratorStrategy\BaseGeneratorStrategy;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

namespace ProxyManagerTest\GeneratorStrategy;

use Laminas\Code\Generator\ClassGenerator;
use PHPUnit\Framework\TestCase;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

namespace ProxyManagerTest\GeneratorStrategy;

use Laminas\Code\Generator\ClassGenerator;
use PHPUnit\Framework\TestCase;
use ProxyManager\Exception\FileNotWritableException;
use ProxyManager\FileLocator\FileLocatorInterface;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\GeneratorStrategy\FileWriterGeneratorStrategy;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

namespace ProxyManagerTest\ProxyGenerator;

use Laminas\Code\Generator\ClassGenerator;
use PHPUnit\Framework\TestCase;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
Expand All @@ -18,6 +18,7 @@
use ProxyManagerTestAsset\ClassWithMixedReferenceableTypedProperties;
use ProxyManagerTestAsset\ClassWithMixedTypedProperties;
use ProxyManagerTestAsset\ClassWithPhp80TypedMethods;
use ProxyManagerTestAsset\ClassWithPhp81Defaults;
use ProxyManagerTestAsset\IterableMethodTypeHintedInterface;
use ProxyManagerTestAsset\ObjectMethodTypeHintedInterface;
use ProxyManagerTestAsset\ReturnTypeHintedClass;
Expand Down Expand Up @@ -112,6 +113,10 @@ public function getTestedImplementations(): array
$implementations[] = [ClassWithPhp80TypedMethods::class];
}

if (PHP_VERSION_ID >= 80100) {
$implementations['php81defaults'] = [ClassWithPhp81Defaults::class];
}

return $implementations;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

namespace ProxyManagerTest\ProxyGenerator;

use Laminas\Code\Generator\ClassGenerator;
use ProxyManager\Exception\InvalidProxiedClassException;
use ProxyManager\Exception\UnsupportedProxiedClassException;
use ProxyManager\Generator\ClassGenerator;
use ProxyManager\Proxy\AccessInterceptorInterface;
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizerGenerator;
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
Expand Down
Loading

0 comments on commit 3661a2d

Please sign in to comment.