php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #75095 Interface inheritance not allowed properly
Submitted: 2017-08-19 12:52 UTC Modified: 2017-08-19 13:06 UTC
From: adaliszk at gmail dot com Assigned:
Status: Not a bug Package: Scripting Engine problem
PHP Version: 7.1.8 OS: Irrelevant
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: adaliszk at gmail dot com
New email:
PHP Version: OS:

 

 [2017-08-19 12:52 UTC] adaliszk at gmail dot com
Description:
------------
It seems that the engine not using inheritance properly with paramether types in abstract classes (which would be solved in 7.2) and in interfaces.

The engine seems not seeing that a sub-interface is compatible with the base interface it just may have more functionalities but the expected boundries are meet even if you override a function method with a sub-interface of the specified function paramether.

You can bypass this to write the same code without using inheritance or checking the entity type inside your class:

  - The first bypass solution is just not great, since if your collection base
    got new functionalities or refactored for some reason, then you have to
    copy-paste trough all of your collection interfaces, it's not efficient.

  - The second bypass solution is making a joke from the engine typecheck 
    features, since it has a power to check the input types, but you cannot use
    it and you have to write typecheck manually which way you have to write
    more code.

It would be great if the engine allow us to override with compatible types the method signitures like it will allow it with abstract classes and interfaces are abstract classes with only abstract methods in it.

Test script:
---------------
An example for this: Create a Collection interface and Entity interface where Entities could add/remove to it. Create sub-interfaces with more functionalities and override the collection add/remove so it would only allow a sub-interface instead a globaly used one.


// Base interfaces
interface Entity {
    public function getId(): int;
}

interface Collection {
    public function add(Entity $item): void;
    public function remove(Entity $item): void;
}

// just more functionality
interface ProductEntity extends Entity {
    public function getName(): string;
}

/// override the types with a subtype, which is compatible by LSP
interface ProductCollection extends Collection {
    public function add(ProductEntity $item): void;
    public function remove(ProductEntity $item): void;
}

Expected result:
----------------
No errors because the sub-interfaces are compatible with the base interface using Liskov Substitute Principle.

Actual result:
--------------
Declaration are not compatible error.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-08-19 13:06 UTC] nikic@php.net
-Status: Open +Status: Not a bug
 [2017-08-19 13:06 UTC] nikic@php.net
Under the Liskov substitution principle argument types are contravariant, not covariant. Please see https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) for more information.
 [2017-08-19 13:08 UTC] example at example dot org
the example you provided is wrong, but this can be done with generic types. they probably will land in php sooner or later
 [2017-08-19 13:40 UTC] adaliszk at gmail dot com
@nikic: So basically you say that I cannot use LSP in deeper level using the engine typecheck? I mean here the Type Entity could be replaced by any Entity and it's works perfectly in runtime. But I cannot specify in a sub-Collection that it's only allow to have a sub-Entity or any of that sub-type? (which is not breaking the LSP)

In this way why we have inheritance in first place? I have to copy-paste and create all of the functionalities in each Collection and each Entity to allow sub types?

Also then if this thinking is wrong, why do you guys will allow this whole stuff with abstract classes? I next release (7.2) you will allow to override to a sub-type and if I create abstract classes with only abstract methods I would able to specify this as an interface. If you allow this in abstract classes, you have to allow it in interfaces since we wont able to specify interfaces around our abstract classes.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Apr 25 21:01:36 2024 UTC