Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix enums and new in initializers as default values on PHP 8.1 #17

Merged
merged 1 commit into from
Apr 28, 2022
Merged
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
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
38 changes: 36 additions & 2 deletions src/ProxyManager/Generator/ClassGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,48 @@
namespace ProxyManager\Generator;

use Laminas\Code\Generator\ClassGenerator as LaminasClassGenerator;
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) {
continue;
}

$parameter->setDefaultValue(new ValueGenerator($default, new ReflectionParameter([$class, $method->getName()], $parameter->getName())));
}
}

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

declare(strict_types=1);

namespace ProxyManager\Generator;

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

use function explode;
use function implode;
use function preg_replace;
use function preg_split;
use function rtrim;
use function substr;
use function var_export;

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) {
$this->value = $value->value;
$this->type = $value->type;
$this->arrayDepth = $value->arrayDepth;
$this->outputMode = $value->outputMode;
$this->allowedTypes = $value->allowedTypes;
$this->constants = $value->constants;
$this->isSourceDirty = $value->isSourceDirty;
$this->indentation = $value->indentation;
$this->sourceContent = $value->sourceContent;
} 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->getName() . ' = ', (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) {
continue;
}

$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,9 +8,10 @@
use Laminas\Code\Reflection\MethodReflection;
use ProxyManager\Generator\MethodGenerator;
use ProxyManager\Generator\Util\ProxiedMethodReturnExpression;
use ProxyManager\Generator\ValueGenerator;
use ReflectionClass;

use function count;
use function sprintf;
use function strtr;
use function var_export;

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 %d: \$args[] = %s;", $i, $default->generate());
continue;
}

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

$defaultValues[] = null;
$defaultValues .= sprintf("\n case %d: \$args[] = null;", $i);
}

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