Skip to content

Promoted properties missing default value #182

Open
@n-valverde

Description

@n-valverde

Bug Report

Q A
Version(s) 4.11.0 (all versions affected since 4.5.0)

Summary

Promoted properties support was added by #114
For some reason, this is lacking support for promoted property default value, I am not sure if this was made on purpose or not, but adding this seems pretty straight forward

Full context of the issue: In a Symfony application, when configuring a service as lazy, this is generating a proxy under the hood (using friendsofphp/proxy-manager-lts). Hunting all deprecations on the road of upgrading PHP in my userland application, I've been faced with a notice about optional parameter being treated as required because of the parameter position. Problem is, this notice come from the generated proxy, but my userland code signature is correct. Let's consider this signature

class SomeService
{
    private LoggerInterface $logger;

    public function __construct(private SomeDependency $dependency, ?LoggerInterface $logger = null, private string $someOptionalConfig = "")
    {
        $this->logger = $logger ?? new NullLogger();
    }
}

That signature is a bit ugly but is technically correct, the problem is, when generating a proxy for this class, you end up with

class SomeService
{
    private LoggerInterface $logger;

    public function __construct(private SomeDependency $dependency, ?LoggerInterface $logger = null, private string $someOptionalConfig)
    {
        $this->logger = $logger ?? new NullLogger();
    }
}

$someOptionalConfig does not have its default value anymore, so the argument position becomes wrong because there is this optional $logger which is now treated as required.

Obviously I can change the userland code signature to fix that, but this behaviour feels unexpected

Current behavior

Promoted property default value is basically always ignored

How to reproduce

Slighlty changing the relevant test easily show the failure, see below

namespace LaminasTest\Code\Generator\TestAsset;

final class ClassWithPromotedParameter
{
    public function __construct(private string $promotedParameter = 'foobar') {
    }
}
namespace LaminasTest\Code\Generator;

class ClassGeneratorTest extends TestCase
{
    public function testClassWithPromotedParameterFromReflection(): void
    {
        $classGenerator = ClassGenerator::fromReflection(
            new ClassReflection(ClassWithPromotedParameter::class)
        );

        $expectedOutput = <<<EOS
namespace LaminasTest\Code\Generator\TestAsset;

final class ClassWithPromotedParameter
{
    public function __construct(private string \$promotedParameter = 'foobar')
    {
    }
}

EOS;

        self::assertEquals($expectedOutput, $classGenerator->generate()); // Fails
    }
}

Expected behavior

Generated class does include promoted property default value. Applying the below tiny patch makes the test pass, and ultimately fixes the root issue of generating proxy classes from Symfony config, but again I am not sure if this was initially made on purpose

Index: src/Generator/PromotedParameterGenerator.php
===================================================================
diff --git a/src/Generator/PromotedParameterGenerator.php b/src/Generator/PromotedParameterGenerator.php
--- a/src/Generator/PromotedParameterGenerator.php	(revision 169123b3ede20a9193480c53de2a8194f8c073ec)
+++ b/src/Generator/PromotedParameterGenerator.php	(date 1684241975636)
@@ -28,12 +28,13 @@
         ?string $type = null,
         string $visibility = self::VISIBILITY_PUBLIC,
         ?int $position = null,
-        bool $passByReference = false
+        bool $passByReference = false,
+        mixed $defaultValue = null
     ) {
         parent::__construct(
             $name,
             $type,
-            null,
+            $defaultValue,
             $position,
             $passByReference,
         );
@@ -92,7 +93,8 @@
             $type,
             $visibility,
             $generator->getPosition(),
-            $generator->getPassedByReference()
+            $generator->getPassedByReference(),
+            $generator->getDefaultValue()
         );
     }
 }

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions