Skip to content

Commit 581ab35

Browse files
Fix enums and new in initializers as default values on PHP 8.1
1 parent c828ced commit 581ab35

21 files changed

+278
-107
lines changed

src/ProxyManager/Factory/AbstractBaseFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace ProxyManager\Factory;
66

7-
use Laminas\Code\Generator\ClassGenerator;
87
use OutOfBoundsException;
98
use ProxyManager\Configuration;
9+
use ProxyManager\Generator\ClassGenerator;
1010
use ProxyManager\ProxyGenerator\ProxyGeneratorInterface;
1111
use ProxyManager\Signature\Exception\InvalidSignatureException;
1212
use ProxyManager\Signature\Exception\MissingSignatureException;

src/ProxyManager/Generator/ClassGenerator.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,48 @@
55
namespace ProxyManager\Generator;
66

77
use Laminas\Code\Generator\ClassGenerator as LaminasClassGenerator;
8+
use ReflectionParameter;
9+
10+
use function method_exists;
811

912
/**
1013
* Class generator that ensures that interfaces/classes that are implemented/extended are FQCNs
1114
*
1215
* @internal do not use this in your code: it is only here for internal use
13-
* @deprecated this class was in use due to parent implementation not receiving prompt bugfixes, but
14-
* `laminas/laminas-code` is actively maintained and receives quick release iterations.
1516
*/
1617
class ClassGenerator extends LaminasClassGenerator
1718
{
19+
public function generate(): string
20+
{
21+
$extendedClass = $this->getExtendedClass();
22+
23+
foreach ($this->getMethods() as $method) {
24+
$class = $extendedClass;
25+
26+
if ($class === null) {
27+
foreach ($this->getImplementedInterfaces() as $interface) {
28+
if (method_exists($interface, $method->getName())) {
29+
$class = $interface;
30+
break;
31+
}
32+
}
33+
}
34+
35+
if ($class === null || ! method_exists($class, $method->getName())) {
36+
continue;
37+
}
38+
39+
foreach ($method->getParameters() as $parameter) {
40+
$default = $parameter->getDefaultValue();
41+
42+
if ($default === null) {
43+
continue;
44+
}
45+
46+
$parameter->setDefaultValue(new ValueGenerator($default, new ReflectionParameter([$class, $method->getName()], $parameter->getName())));
47+
}
48+
}
49+
50+
return parent::generate();
51+
}
1852
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ProxyManager\Generator;
6+
7+
use Laminas\Code\Generator\Exception\RuntimeException;
8+
use Laminas\Code\Generator\ValueGenerator as LaminasValueGenerator;
9+
use ReflectionParameter;
10+
11+
use function explode;
12+
use function implode;
13+
use function preg_replace;
14+
use function preg_split;
15+
use function rtrim;
16+
use function substr;
17+
use function var_export;
18+
19+
use const PREG_SPLIT_DELIM_CAPTURE;
20+
21+
/**
22+
* @internal do not use this in your code: it is only here for internal use
23+
*/
24+
class ValueGenerator extends LaminasValueGenerator
25+
{
26+
private $reflection;
27+
28+
public function __construct($value, ?ReflectionParameter $reflection = null)
29+
{
30+
if ($value instanceof LaminasValueGenerator) {
31+
$this->value = $value->value;
32+
$this->type = $value->type;
33+
$this->arrayDepth = $value->arrayDepth;
34+
$this->outputMode = $value->outputMode;
35+
$this->allowedTypes = $value->allowedTypes;
36+
$this->constants = $value->constants;
37+
$this->isSourceDirty = $value->isSourceDirty;
38+
$this->indentation = $value->indentation;
39+
$this->sourceContent = $value->sourceContent;
40+
} else {
41+
parent::__construct($value, parent::TYPE_AUTO, parent::OUTPUT_SINGLE_LINE);
42+
}
43+
44+
$this->reflection = $reflection;
45+
}
46+
47+
public function generate(): string
48+
{
49+
try {
50+
return parent::generate();
51+
} catch (RuntimeException $e) {
52+
if ($this->reflection) {
53+
$value = rtrim(substr(explode('$' . $this->reflection->getName() . ' = ', (string) $this->reflection, 2)[1], 0, -2));
54+
} else {
55+
$value = var_export($this->value, true);
56+
}
57+
58+
return self::fixExport($value);
59+
}
60+
}
61+
62+
private static function fixExport(string $value): string
63+
{
64+
$parts = preg_split('{(\'(?:[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')}', $value, -1, PREG_SPLIT_DELIM_CAPTURE);
65+
66+
foreach ($parts as $i => &$part) {
67+
if ($part === '' || $i % 2 !== 0) {
68+
continue;
69+
}
70+
71+
$part = preg_replace('/(?(DEFINE)(?<V>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+))(?<!\\\\)(?&V)(?:\\\\(?&V))*+::/', '\\\\$0', $part);
72+
}
73+
74+
return implode('', $parts);
75+
}
76+
}

src/ProxyManager/ProxyGenerator/LazyLoadingGhost/MethodGenerator/CallInitializer.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Laminas\Code\Generator\PropertyGenerator;
99
use ProxyManager\Generator\MethodGenerator;
1010
use ProxyManager\Generator\Util\IdentifierSuffixer;
11+
use ProxyManager\Generator\ValueGenerator;
1112
use ProxyManager\ProxyGenerator\Util\Properties;
1213
use ReflectionProperty;
1314

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

173-
return var_export($defaults[$name] ?? null, true);
174+
return (new ValueGenerator($defaults[$name] ?? null))->generate();
174175
}
175176
}

src/ProxyManager/ProxyGenerator/RemoteObject/MethodGenerator/RemoteObjectMethod.php

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
use Laminas\Code\Reflection\MethodReflection;
99
use ProxyManager\Generator\MethodGenerator;
1010
use ProxyManager\Generator\Util\ProxiedMethodReturnExpression;
11+
use ProxyManager\Generator\ValueGenerator;
1112
use ReflectionClass;
1213

13-
use function count;
14+
use function sprintf;
1415
use function strtr;
1516
use function var_export;
1617

@@ -21,10 +22,10 @@ class RemoteObjectMethod extends MethodGenerator
2122
{
2223
private const TEMPLATE
2324
= <<<'PHP'
24-
$defaultValues = #DEFAULT_VALUES#;
25-
$declaredParameterCount = #PARAMETER_COUNT#;
25+
$args = \func_get_args();
2626
27-
$args = \func_get_args() + $defaultValues;
27+
switch (\func_num_args()) {#DEFAULT_VALUES#
28+
}
2829
2930
#PROXIED_RETURN#
3031
PHP;
@@ -41,39 +42,36 @@ public static function generateMethod(
4142
. ', ' . var_export($originalMethod->getName(), true) . ', $args);' . "\n\n"
4243
. ProxiedMethodReturnExpression::generate('$return', $originalMethod);
4344

44-
$defaultValues = self::getDefaultValuesForMethod($originalMethod);
45-
$declaredParameterCount = count($originalMethod->getParameters());
45+
$defaultValues = self::getDefaultValuesForMethod($originalMethod);
4646

4747
$method->setBody(
4848
strtr(
4949
self::TEMPLATE,
5050
[
5151
'#PROXIED_RETURN#' => $proxiedReturn,
52-
'#DEFAULT_VALUES#' => var_export($defaultValues, true),
53-
'#PARAMETER_COUNT#' => var_export($declaredParameterCount, true),
52+
'#DEFAULT_VALUES#' => $defaultValues,
5453
]
5554
)
5655
);
5756

5857
return $method;
5958
}
6059

61-
/** @psalm-return list<int|float|bool|array|string|null> */
62-
private static function getDefaultValuesForMethod(MethodReflection $originalMethod): array
60+
private static function getDefaultValuesForMethod(MethodReflection $originalMethod): string
6361
{
64-
$defaultValues = [];
65-
foreach ($originalMethod->getParameters() as $parameter) {
62+
$defaultValues = '';
63+
foreach ($originalMethod->getParameters() as $i => $parameter) {
6664
if ($parameter->isOptional() && $parameter->isDefaultValueAvailable()) {
67-
/** @psalm-var int|float|bool|array|string|null */
68-
$defaultValues[] = $parameter->getDefaultValue();
65+
$default = new ValueGenerator($parameter->getDefaultValue(), $parameter);
66+
$defaultValues .= sprintf("\n case %d: \$args[] = %s;", $i, $default->generate());
6967
continue;
7068
}
7169

7270
if ($parameter->isVariadic()) {
7371
continue;
7472
}
7573

76-
$defaultValues[] = null;
74+
$defaultValues .= sprintf("\n case %d: \$args[] = null;", $i);
7775
}
7876

7977
return $defaultValues;

tests/ProxyManagerTest/Functional/FatalPreventionFunctionalTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace ProxyManagerTest\Functional;
66

7-
use Laminas\Code\Generator\ClassGenerator;
87
use PHPUnit\Framework\TestCase;
98
use ProxyManager\Exception\ExceptionInterface;
9+
use ProxyManager\Generator\ClassGenerator;
1010
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
1111
use ProxyManager\Proxy\ProxyInterface;
1212
use ProxyManager\ProxyGenerator\AccessInterceptorScopeLocalizerGenerator;

tests/ProxyManagerTest/Functional/MultipleProxyGenerationTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use ProxyManagerTestAsset\ClassWithMixedTypedProperties;
2323
use ProxyManagerTestAsset\ClassWithParentHint;
2424
use ProxyManagerTestAsset\ClassWithPhp80TypedMethods;
25+
use ProxyManagerTestAsset\ClassWithPhp81Defaults;
2526
use ProxyManagerTestAsset\ClassWithPrivateProperties;
2627
use ProxyManagerTestAsset\ClassWithProtectedProperties;
2728
use ProxyManagerTestAsset\ClassWithPublicProperties;
@@ -138,6 +139,10 @@ public function getTestedClasses(): array
138139
$objects[] = [new ClassWithPhp80TypedMethods()];
139140
}
140141

142+
if (PHP_VERSION_ID >= 80100) {
143+
$objects['php81defaults'] = [new ClassWithPhp81Defaults()];
144+
}
145+
141146
return $objects;
142147
}
143148
}

tests/ProxyManagerTest/Functional/RemoteObjectFunctionalTest.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
use ProxyManagerTestAsset\OtherObjectAccessClass;
1919
use ProxyManagerTestAsset\RemoteProxy\BazServiceInterface;
2020
use ProxyManagerTestAsset\RemoteProxy\Foo;
21+
use ProxyManagerTestAsset\RemoteProxy\FooEnum;
2122
use ProxyManagerTestAsset\RemoteProxy\FooServiceInterface;
2223
use ProxyManagerTestAsset\RemoteProxy\RemoteServiceWithDefaultsAndVariadicArguments;
2324
use ProxyManagerTestAsset\RemoteProxy\RemoteServiceWithDefaultsInterface;
25+
use ProxyManagerTestAsset\RemoteProxy\RemoteServiceWithPhp81DefaultsInterface;
2426
use ProxyManagerTestAsset\RemoteProxy\VariadicArgumentsServiceInterface;
2527
use ProxyManagerTestAsset\VoidCounter;
2628
use ReflectionClass;
29+
use stdClass;
2730

2831
use function assert;
2932
use function get_class;
@@ -32,6 +35,8 @@
3235
use function ucfirst;
3336
use function uniqid;
3437

38+
use const PHP_VERSION_ID;
39+
3540
/**
3641
* Tests for {@see \ProxyManager\ProxyGenerator\RemoteObjectGenerator} produced objects
3742
*
@@ -151,7 +156,7 @@ public function getProxyMethods(): array
151156
{
152157
$selfHintParam = new ClassWithSelfHint();
153158

154-
return [
159+
$methods = [
155160
[
156161
FooServiceInterface::class,
157162
'foo',
@@ -273,6 +278,21 @@ public function getProxyMethods(): array
273278
200,
274279
],
275280
];
281+
282+
if (PHP_VERSION_ID >= 80100) {
283+
$methods['when using php8.1 defaults'] = [
284+
RemoteServiceWithPhp81DefaultsInterface::class,
285+
'php81Defaults',
286+
[],
287+
[
288+
FooEnum::bar,
289+
new stdClass(),
290+
],
291+
200,
292+
];
293+
}
294+
295+
return $methods;
276296
}
277297

278298
/**

tests/ProxyManagerTest/GeneratorStrategy/BaseGeneratorStrategyTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
namespace ProxyManagerTest\GeneratorStrategy;
66

7-
use Laminas\Code\Generator\ClassGenerator;
87
use PHPUnit\Framework\TestCase;
8+
use ProxyManager\Generator\ClassGenerator;
99
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
1010
use ProxyManager\GeneratorStrategy\BaseGeneratorStrategy;
1111

tests/ProxyManagerTest/GeneratorStrategy/EvaluatingGeneratorStrategyTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
namespace ProxyManagerTest\GeneratorStrategy;
66

7-
use Laminas\Code\Generator\ClassGenerator;
87
use PHPUnit\Framework\TestCase;
8+
use ProxyManager\Generator\ClassGenerator;
99
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
1010
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
1111

0 commit comments

Comments
 (0)