Skip to content

Bypassing readonly properties without visibilty can change behavior #49

@ghost

Description

Reproduction example:

  1. composer require dg/bypass-finals --dev
  2. index.php:
<?php

require_once __DIR__ . '/vendor/autoload.php';

\DG\BypassFinals::enable();

require_once __DIR__ . '/include.php';

new A('A');
new B('B');
  1. include.php:
<?php

class A {
    public function __construct(
        readonly string $a
    ) {
        var_dump('A: ' . $this->a);
    }
}

class B {
    public function __construct(
        public readonly string $a
    ) {
        var_dump('B: ' . $this->a);
    }
}

Result:

php index.php
PHP Warning:  Undefined property: A::$a in /…/test-bypassfinals/include.php on line 7

Warning: Undefined property: A::$a in /…/test-bypassfinals/include.php on line 7
string(3) "A: "
string(4) "B: B"

The problem stems from the fact that readonly is just removed from the code thus changing the promoted property to a simple constructor argument and thus changing behavior.

Context: We use a third-party library that does uses this kind of code.


I am unsure whether this package can really fix this issue because at the moment, a "simple" replace logic without considering the context is used. Here, the context of being a promoted property would have to be known and readonly replaced with public.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions