Skip to content

Make "prototype" the function which inheritance is checked against #3993

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

Closed
wants to merge 1 commit into from

Conversation

nikic
Copy link
Member

@nikic nikic commented Mar 27, 2019

Currently the function "prototype" has a very peculiar definition, where it is not the function against which the inheritance check is performed (as would be expected from the name), but rather the first occurrence of the method in the inheritance hierarchy.

For #3732 we'd like the "prototype" field to be the actual prototype, which is almost always the direct parent. The only exception are constructors, in which case it will be either NULL or an abstract constructor somewhere higher up in the hierarchy.

Add a new ZEND_ACC_HAS_ABSTRACT_PARENT flag which is used to determine whether a failing LSP check should error or warn, as this can no longer be determined from the prototype after this change.

@@ -326,6 +326,9 @@ typedef struct _zend_oparray_context {
/* function is a destructor | | | */
#define ZEND_ACC_DTOR (1 << 29) /* | X | | */
/* | | | */
/* function is a destructor | | | */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment is identical as the define above

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, if this change is safe.
"prototype" is also used to check overridden protected method visibility.
If you change "prototype" meaning, this also changes visibility.

@nikic
Copy link
Member Author

nikic commented Mar 28, 2019

@dstogov Thanks, I wasn't aware of that. I think now I finally understand what the prototype is about...

An example:

<?php

class A {
    protected function test() {}
}
class B extends A {
    public function test2($x) {
        $x->test();
    }
}
class C extends A {
    protected function test() {}
}
(new B)->test2(new C);

We allow this code, because when checking protected visibility for the C::test() call we use the prototype A::test() for the check instead. Because we also allow subclasses to call protected parents, this is then allowed. If we checked against C::test() instead, then B and C would not be in a subclass relationship, so it's not allowed.

The above example will still work after this patch because the prototype happens to stay the same, but the following example will error:

<?php
  
class A {
    protected function test() {}
}
class B extends A {
    public function test2($x) {
        $x->test(); // Uncaught Error: Call to protected method D::test() from context 'B'
    }
}
class C extends A {
    protected function test() {}
}
class D extends C {
    protected function test() {}
}
(new B)->test2(new D);

So yeah, looks like we cannot make this change.

@nikic nikic closed this Mar 28, 2019
php-pulls pushed a commit that referenced this pull request Mar 28, 2019
@nikic
Copy link
Member Author

nikic commented Mar 28, 2019

I think we also need to introduce the prototype concept for properties. This code currently fails with "Cannot access protected property B2::$foo", but it should work, same the method-based code above:

<?php
  
class A {
    protected $foo = 'A';
}

class B1 extends A {
    public function test($obj) {
        var_dump($obj->foo);
    }
}

class B2 extends A {
    protected $foo = 'B2';
}

(new B1)->test(new B2);

@dstogov
Copy link
Member

dstogov commented Mar 28, 2019

So yeah, looks like we cannot make this change.

We may change this, if we are going to break BC (requires RFC).

I'm not sure, if protected functions/properties should work this way, and how this case work in other languages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants