On Tue, Nov 25, 2014 at 11:42 PM, Nikita Popov <nikita....@gmail.com> wrote:
> On Tue, Nov 25, 2014 at 11:13 PM, Marc Bennewitz <dev@mabe.berlin> wrote:
>
>>
>> Am 25.11.2014 um 22:43 schrieb Levi Morrison:
>>
>>  On Tue, Nov 25, 2014 at 2:07 PM, Marc Bennewitz <dev@mabe.berlin> wrote:
>>>
>>>> I think it's required to do the type check on runtime (Option 2) because
>>>> one of the use cases for return type-hint are factories and such often do
>>>> instantiation in base of unknown string values:
>>>>
>>>> class MyFactory {
>>>>      public static function factory($name) : AdapterInterface {
>>>>          $class = 'MyNamespace\Adapter\' . $name;
>>>>          return $class();
>>>>      }
>>>> }
>>>>
>>> It seems that I did not explain this clearly enough; I apologize. The
>>> variance has to do with the declared type in the function signature
>>> when inheritance is involved, not the type of the value returned by
>>> the function.
>>>
>>> For instance, under any of the three options this code will work just
>>> fine:
>>>
>>> class Foo {}
>>> class Goo  extends Foo {}
>>>
>>> class FooFactory {
>>>      function create(): Foo { return new Goo(); }
>>> }
>>>
>>> As long as the return value from FooFactory::create returns Foo or a
>>> subtype of Foo (such as Goo), then it will work.
>>>
>>> The variance that is under discussion in this thread is about the
>>> declared return type in the signature:
>>>
>>> class GooFactory extends FooFactory {
>>>      function create(): Goo {}
>>> }
>>>
>>> In this case, GooFactory::create() declares a return type of Goo,
>>> which is a subtype of Foo [the return type of the inherited method
>>> FooFactory::create()]. This is a covariant return type.
>>>
>>> If we choose option 3, the only possible return type for
>>> GooFactory::create is Foo.
>>>
>>> Hopefully this clarifies the issue.
>>>
>> Yes it does - thank you for explanation - my mistake :/
>>
>> Option 3 is a no go not from OOP perspective and from consistency pov as
>> we already allow this in type-hint:
>>
>> class FooFactory {
>>     function create(Foo $foo): Foo { return $foo; }
>> }
>>
>> class GooFactory extends FooFactory {
>>     function create(Goo $goo): Goo { return $goo; }
>>
>> }
>>
>
> This is not correct. Parameter typehints in PHP are invariant, so you are
> not allowed to change them during inheritance. However LSP violations
> during inheritance of *non-abstract* methods currently uses a very low
> error level (E_STRICT), so you probably didn't notice. If you try the same
> thing with an interface method or an explicitly abstract method, you will
> receive a fatal error:
>
> interface I1 {
>     function foo(A $a);
> }
> class C1 implements I1 {
>     function foo(B $b) { ... }
> }
>
> This code snippet will result in a fatal error, because it violates type
> invariance.
>
> Nikita

Hi, all

As in case of compatibility, I would think about the following:

A return type should be the same, or an instance of a class, extending
the the required class. This way, you have all methods and properties
you would expect.

But as of the parameters, it should be (if we allow anything different
than the class/interface expected), a class or an interface the
parent-one extends or implements. I know, that this one sounds strange
and I can't come up with a practical way. Let me put that one into an
example:

See this example:

class Bar {}
class Foo extends Bar {}
class Goo extends Foo {}

interface ITester {
    function create(Goo $foo) : Bar;
}

class BarTester implements ITester {
    function create(Bar $foo) : Bar { return $foo; }
}

class FooTester extends BarTester {
    function create(Foo $foo) : Foo { return $foo; }
}

class GooTester extends FooTester {
    function create(Goo $goo)  : Goo { return $goo; }
}

$testers = array( new BarTester(), new FooTester(), new GooTester() );

/** @var $testers ITesters[] */
foreach($testers as $tester) {

    $res = $tester->create(new Goo());
    /** @var $res Bar (or some instance extending it) */
}

If you have an instance of ITester, you expect it to accept an
instance of Goo. As FooTester is implemented, it will always accept
Goo as instance here - yea - even more. It also accepts Foo instances
as parameter.

But as I expect the create method to return an instance of Foo (and to
make usage of any method, Foo has), the method could also return an
instance of Goo, because it has all the functionality, Foo has, and
even more.

In this example it may sounds weird and confusing, but if you take
these two things apart, it makes actually sense - each for itself.

Is there something I didn't think about, or that would get it to crash?

Bye
Simon

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to