Skip to content

Properties (hooks) and inheritance #15424

Open

Description

@SerafimArts

Description

Referenced RFC #13455

The following code:

<?php

interface ExampleInterface {} // parent
interface MutableExampleInterface extends ExampleInterface {} // child
final class Example implements MutableExampleInterface {} // impl

// parent provider
interface ExampleProviderInterface
{
    // Any type that implements ExampleInterface can be read
    public ExampleInterface $property { get; }
}

// child provider that "overrides" parent definition
interface MutableExampleProviderInterface extends ExampleProviderInterface
{
    // Any type that implements (MutableExampleInterface & ExampleInterface) can be read + write
    // We supplement the object with the set method
    public MutableExampleInterface $property { get; set; }
}

// An implementation that uses implementation
final class ExampleProvider implements MutableExampleProviderInterface
{
    public Example $property { // << implementation
        // We can return the implementation, because it implements (MutableExampleInterface & ExampleInterface)
        get => $this->property; 
        // Any instance of ExampleInterface is allowed for set, since it satisfies the covariance condition
        set (ExampleInterface $v) => $this->property = new Example($v);
    }
}

Resulted in this output:

Fatal error: Type of ExampleProvider::$property must be MutableExampleInterface (as in class MutableExampleProviderInterface)

But I expected this output instead:

*nothing*

Same code (logic) using getters and setters instead of properties: https://onlinephp.io/c/4e180

interface ExampleInterface {}
interface MutableExampleInterface extends ExampleInterface {}
final class Example implements MutableExampleInterface {}

interface ExampleProviderInterface
{
    public function getProperty(): ExampleInterface;
}

interface MutableExampleProviderInterface extends ExampleProviderInterface
{
    public function getProperty(): MutableExampleInterface;
    public function setProperty(MutableExampleInterface $value): void;
}

final class ExampleProvider implements MutableExampleProviderInterface
{
    public function getProperty(): Example {}
    public function setProperty(ExampleInterface $value): void {}
}

Ping @iluuu1994 (again) ^_^

PHP Version

PHP 8.4-beta1

Operating System

No response

Activity

  1. iluuu1994 commented on Aug 16, 2024

    @iluuu1994
    Member

    This is not really a bug but a feature request. We specifically considered this case but decided not to further complicate LSP rules. This may be considered in the future.

  2. SerafimArts commented on Aug 16, 2024

    @SerafimArts
    ContributorAuthor

    kk. Should I close this issue in that case?

  3. iluuu1994 commented on Aug 16, 2024

    @iluuu1994
    Member

    It's ok, it can remain as a feature request. Thanks for the report.

  4. vudaltsov commented on Aug 27, 2024

    @vudaltsov
    Contributor

    By the way, readonly properties could also become covariant. Initially Nikita stated in his RFC, that readonly properties cannot be covariant due to possible initialization from a different class in the hierarchy. But now readonly properties can only be initialized from the declaring class, so covariance can be enabled for them as well.

  5. iluuu1994 commented on Aug 27, 2024

    @iluuu1994
    Member

    From the RFC:

    Covariance would hold for reads from the property, but not for the initializing assignment, which is permitted from both A and B here. I believe that covariance would only be truly correct if the parent property were abstract, which is currently not supported. A relaxation to covariant semantics (either wholesale, or for abstract parents) would be possible in the future.

    This hasn't really changed. both A and B can write to the property. I think readonly with inheritance is inherently risky, given that changes to the parent can break the write in the child, or vice versa. The narrowing of the child type is only sound if the child can guarantee the parent doesn't attempt to write to it.

  6. vudaltsov commented on Aug 28, 2024

    @vudaltsov
    Contributor

    This hasn't really changed.

    Oh, yes, my bad. I was testing it like this https://3v4l.org/XFLc4 instead of https://3v4l.org/bafTR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions