FAQ
Hi everybody,
I hope this is the right place for asking such a question. If that's not
the case, please excuse me; I'd appreciate if you could redirect me to
the appropriate place.

The PHP manual says, regarding Interfaces, that the class implementing
the interface must use the exact same method signatures as are defined
in the interface. Not doing so will result in a fatal error.

I don't understand why this needs to be so. Consider for example the
following case: I have an interface

interface Foo
{
      public function foo(SpecialClass $object);
}

and a class

class Bar
{
      public function foo(BaseClass $object)
      {
          // do something with BaseClass $object
      }
}

that has the same signature of Foo except for the fact that the method
foo takes a BaseClass, where SpecialClass extends BaseClass.

  From a theoretical point of view, saying that a class satisfies the
interface Foo means that it has a method foo that is able to deal with a
SpecialClass object. So Bar satisfies the contract imposed by Foo since
it has a method foo that is able to deal with a BaseClass, which means
that it will also be able to deal with SpecialClass.

Hence it would seem natural to say that Bar implements Foo. But, as the
documentation says, this produces a fatal error.

What is the reason for this fatal error? Is there a specific reason to
do things this way? It this is the case, could you please provide an
example that proves why my approach creates problems.
Thank you

Search Discussions

  • Andreas Heigl at May 12, 2016 at 4:47 pm

    Am 12.05.16 um 18:37 schrieb Marco Perone:
    Hi everybody,
    I hope this is the right place for asking such a question. If that's not
    the case, please excuse me; I'd appreciate if you could redirect me to
    the appropriate place.

    The PHP manual says, regarding Interfaces, that the class implementing
    the interface must use the exact same method signatures as are defined
    in the interface. Not doing so will result in a fatal error.

    I don't understand why this needs to be so. Consider for example the
    following case: I have an interface

    interface Foo
    {
    public function foo(SpecialClass $object);
    }

    and a class

    class Bar
    {
    public function foo(BaseClass $object)
    {
    // do something with BaseClass $object
    }
    }

    that has the same signature of Foo except for the fact that the method
    foo takes a BaseClass, where SpecialClass extends BaseClass.

    From a theoretical point of view, saying that a class satisfies the
    interface Foo means that it has a method foo that is able to deal with a
    SpecialClass object. So Bar satisfies the contract imposed by Foo since
    it has a method foo that is able to deal with a BaseClass, which means
    that it will also be able to deal with SpecialClass.

    Hence it would seem natural to say that Bar implements Foo. But, as the
    documentation says, this produces a fatal error.

    What is the reason for this fatal error? Is there a specific reason to
    do things this way? It this is the case, could you please provide an
    example that proves why my approach creates problems.
    Thank you
    Hey Marco.

    It's the other way around.

    The interface creates a contract that ensures that you can use ALL
    methods available in your SpecialClass. But that might be more, never
    less methods than in BaseClass. So when you try to call methods of the
    SpecialClass while executing Bar::foo() (which you have a contract on)
    it might fail as BaseClass might not have those methods.

    class BaseClass {
         public function a(){}
    }

    class SpecialClass extends BaseClass{
         public function b(){}
    }

    Think about the implementation of your class Bar:

    class Bar implements Foo {
         public function foo($x){
      $x->b();
         }
    }

    So calling (new Bar())->foo(new BaseClass()) will fail as the method 'b'
    isn't implemented, but it should be according to the interface...

    Hope that helps ;)

    Cheers

    Andreas

    --
                                                                   ,,,
                                                                  (o o)
    +---------------------------------------------------------ooO-(_)-Ooo-+
    Andreas Heigl |
    mailto:andreas@heigl.org N 50°22'59.5" E 08°23'58" |
    http://andreas.heigl.org http://hei.gl/wiFKy7 |
    +---------------------------------------------------------------------+
    http://hei.gl/root-ca |
    +---------------------------------------------------------------------+
  • Rowan Collins at May 12, 2016 at 5:03 pm

    On 12/05/2016 17:47, Andreas Heigl wrote:
    It's the other way around.

    The interface creates a contract that ensures that you can use ALL
    methods available in your SpecialClass.
    I don't think that's what the interface in the example means:

    interface Foo
    {
          public function foo(SpecialClass $object);
    }


    What this says is "an object adhering to interface Foo has a method foo
    which accepts any SpecialClass instance".

    In other words, it says that the following is bound to succeed:

    if ( $thing instanceOf Foo ) {
          $thing->foo( new SpecialClass );
    }


    Marco is right that a class with a wider definition fulfils this contract:

    class Bar implements Foo
    {
          public function foo(BaseClass $object)
          {
              // do something with BaseClass $object
          }
    }

    $thing = new Bar;
    if ( $thing instanceOf Foo ) {
          $thing->foo( new SpecialClass );
    }


    This is called "contravariance of parameters", and has come up a couple
    of times on the list. In particular, when return types were added, it
    was discussed that they should technically be covariant (subtypes can
    "narrow" return types, and say they only return a specific sub-type of
    the original contract).

    So from a design point of view, there is no reason not to support it;
    unfortunately, there are some technical hurdles to implementing that
    within the PHP engine. Here is a nice summary from Anthony Ferrara:
    http://marc.info/?l=php-internals&m=142791636808421&w=2

    Regards,
    --
    Rowan Collins
    [IMSoP]
  • Marco Perone at May 12, 2016 at 9:14 pm

    On 12/05/2016 19:02, Rowan Collins wrote:
    On 12/05/2016 17:47, Andreas Heigl wrote:
    It's the other way around.

    The interface creates a contract that ensures that you can use ALL
    methods available in your SpecialClass.
    I don't think that's what the interface in the example means:

    interface Foo
    {
    public function foo(SpecialClass $object);
    }


    What this says is "an object adhering to interface Foo has a method
    foo which accepts any SpecialClass instance".

    In other words, it says that the following is bound to succeed:

    if ( $thing instanceOf Foo ) {
    $thing->foo( new SpecialClass );
    }


    Marco is right that a class with a wider definition fulfils this
    contract:

    class Bar implements Foo
    {
    public function foo(BaseClass $object)
    {
    // do something with BaseClass $object
    }
    }

    $thing = new Bar;
    if ( $thing instanceOf Foo ) {
    $thing->foo( new SpecialClass );
    }


    This is called "contravariance of parameters", and has come up a
    couple of times on the list. In particular, when return types were
    added, it was discussed that they should technically be covariant
    (subtypes can
    "narrow" return types, and say they only return a specific sub-type of
    the original contract).

    So from a design point of view, there is no reason not to support it;
    unfortunately, there are some technical hurdles to implementing that
    within the PHP engine. Here is a nice summary from Anthony Ferrara:
    http://marc.info/?l=php-internals&m=142791636808421&w=2

    Regards,
    Thanks Rowan for your response and for the link, very interesting.

    Adding a bit of context to my question, I asked it after following a
    discussion in the FIG mailing list.
    In PSR-7 you have a RequestInterface and a ServerRequestInterface with
    inherits from it (the BaseClass and SpecialClass of my example).
    Now there is a discussion on how to standardize a MiddlewareInterface.
    You would like to have an interface like

    interface ServerMiddlewareInterface
    {
          public function __invoke(ServerRequestInterface $request, ...)
    }

    which plays the role of Foo, and maybe an implementation

    class MyMiddleware
    {
          public function __invoke(RequestInterface $request, ...)
          {
              // do something with $request
          }
    }

    which plays the part of Bar.

    Since at the moment Bar can not implement Foo, the solution to this is,
    from a design point of view, a bit akward (see
    https://github.com/php-fig/fig-standards/blob/95aece5e68376e9a5d79d2385d755824dfac3ed8/proposed/middleware.md#21-server-vs-client-requests
    for details). It would be nice if "variant" parameters and return types
    were introduced, so the design could be improved (this is just an
    example, I guess there could be more aroud).

    Regards

    marco
  • Rasmus Schultz at May 15, 2016 at 10:05 am
    I know you're already voting on this, but I'd like to throw in a
    couple of late comments.

    My main problem with this RFC is the use of the interface keyword for
    something that almost by definition is not an interface.

    An interface is a set of methods, but an anonymous function is just
    one function, so it can't possibly implement an interface - this only
    works because of the single method constraint and some magical mapping
    of the closure as a method implementing that single-method interface.

    You can't, for example, add another method to one of these
    "interfaces", which is problematic - it's inconsistent with what
    interfaces actually are.

    In a sense, what you've really done, is you've overloaded the
    interface concept as a means of shoehorning typedefs into the
    language.

    Per the example in the RFC:

    interface IFoo {
         public function method() : int;
    }

    $cb = function () implements IFoo : int {
         return 42;
    };

    The coupling between $cb and IFoo is explicit - but the coupling
    between IFoo::method() and the closure is implied; it's something the
    interpreter, and the person reading the code, has to reason about.

    The one-method limitation isn't apparent from the "interface"
    declaration - that is, nothing about the interface declaration implies
    that it's not actually an interface, e.g. is not a set of methods.

    On the other hand, I can see the advantages of being able to treat and
    invoke the closure as if it were an object implementing an interface -
    it just seems like there's something missing. When a function
    "implements" an interface, it puts a constraint on that interface,
    preventing it from taking on another method - it imposes this
    constraint on the interface from the outside-in, which seems really
    counter intuitive to me.

    From the RFC:

    interface IFoo {
         public function method1();
         public function method2();
    }

    function () implements IFoo {};

    This is really surprising if you thought you understood what
    interfaces are: a set of methods.

    What suppose I write an interface that has a single method today - and
    consumers of my library begin to implement this interface using
    closures in this manner. But then tomorrow, I add another method to my
    interface? There is suddenly now direct upgrade path, no obvious way
    to fix the code - they can't simply implement that new method and move
    on, they now have to start refactoring everything from closures to
    actual classes.

    In that sense, this is worse than typedefs - at least typedefs
    consistently define precisely one function and not a set.

    Another disconnect from interfaces, is that objects can traditionally
    implement more than one interface - but an anonymous function can only
    implement a single interface. It's another deviation from actual
    interfaces, overloading the interface" keyword - it's inconsistent
    with interfaces, which is misleading and adds unnecessary learning
    curve.

    Another downside of this proposal, is that a single functional
    interface effectively has two names: the type-name and the
    method-name; but both names map to the same thing, the anonymous
    closure. Method-names are necessary in interfaces because they
    identify the individual methods in a set of methods. But functional
    interfaces are not a set of methods. The functional interface
    type-name already designates the type. There is no reason it should
    need to have two names.

    How about the following alternative syntax?

    callable IFoo = function () : int;

    $cb = function () implements IFoo {
         return 42;
    };

    With this syntactic sugar, functional interfaces are distinctly
    single-method interfaces - you can't add another method.

    You no longer need to come up with two names for one thing.

    You don't need to repeat the return-type when implementing it.

    And you don't need to specify that extra method-name when invoking it:

    if ($cb instanceof IFoo) {
         $number = $cb();
    }

    It also avoids the following conundrum:

    interface Foo {
         public function foo();
    }

    interface Bar {
         public function bar();
    }

    class Bleet implements Foo, Bar {
         // ...
    }

    function fluff(Bleet $bleet) {
         $bleet(); // ERROR!
    }

    In this example, the fluff() function thinks it's getting an anonymous
    function implementing Foo, but that's not the case. But it could be.
    You opened that door when you decided to overload interfaces - not
    only functions can implement these interfaces, classes can too.

    I think this last example demonstrates a huge disconnect - you've
    defined something that is apparently both an interface and a typedef
    of sorts, but the resulting types are not in fact interchangible. That
    is, you can't swap out an anonymous function with a class implementing
    that interface, or vice-versa, unless the consumer relies on invoking
    the method rather than using the object as a callable. The fact that
    it's a callable isn't even a fact you can rely on anymore.

    I don't believe this is good. I think that the number of compiler
    errors specified in the RFC alone is a flashing red light.

    I think that other languages have typedefs rather than a syntactic
    version of single-method interface implementations.

    Is there a source of reference for this feature? I mean another
    language implementing this pattern?

    I have a feeling other languages likely do not implement something like this.

    I'm concerned that this might be one of those "original PHP features"
    that turns out to be a little too original.

    I personally would prefer to have something that's more familiar, e.g.
    something close to typedefs.

    Something that doesn't overload the concept of interfaces and requires
    a big section in the manual explaining how interfaces in PHP are not
    like interfaces in other languages, all the conditions, warnings,
    acceptable use and limitations.

    I'd prefer something that's simpler and easier to explain.

    On Thu, May 12, 2016 at 11:14 PM, Marco Perone wrote:
    On 12/05/2016 19:02, Rowan Collins wrote:
    On 12/05/2016 17:47, Andreas Heigl wrote:

    It's the other way around.

    The interface creates a contract that ensures that you can use ALL
    methods available in your SpecialClass.

    I don't think that's what the interface in the example means:

    interface Foo
    {
    public function foo(SpecialClass $object);
    }


    What this says is "an object adhering to interface Foo has a method foo
    which accepts any SpecialClass instance".

    In other words, it says that the following is bound to succeed:

    if ( $thing instanceOf Foo ) {
    $thing->foo( new SpecialClass );
    }


    Marco is right that a class with a wider definition fulfils this contract:

    class Bar implements Foo
    {
    public function foo(BaseClass $object)
    {
    // do something with BaseClass $object
    }
    }

    $thing = new Bar;
    if ( $thing instanceOf Foo ) {
    $thing->foo( new SpecialClass );
    }


    This is called "contravariance of parameters", and has come up a couple of
    times on the list. In particular, when return types were added, it was
    discussed that they should technically be covariant (subtypes can
    "narrow" return types, and say they only return a specific sub-type of the
    original contract).

    So from a design point of view, there is no reason not to support it;
    unfortunately, there are some technical hurdles to implementing that within
    the PHP engine. Here is a nice summary from Anthony Ferrara:
    http://marc.info/?l=php-internals&m=142791636808421&w=2

    Regards,

    Thanks Rowan for your response and for the link, very interesting.

    Adding a bit of context to my question, I asked it after following a
    discussion in the FIG mailing list.
    In PSR-7 you have a RequestInterface and a ServerRequestInterface with
    inherits from it (the BaseClass and SpecialClass of my example).
    Now there is a discussion on how to standardize a MiddlewareInterface.
    You would like to have an interface like

    interface ServerMiddlewareInterface
    {
    public function __invoke(ServerRequestInterface $request, ...)
    }

    which plays the role of Foo, and maybe an implementation

    class MyMiddleware
    {
    public function __invoke(RequestInterface $request, ...)
    {
    // do something with $request
    }
    }

    which plays the part of Bar.

    Since at the moment Bar can not implement Foo, the solution to this is, from
    a design point of view, a bit akward (see
    https://github.com/php-fig/fig-standards/blob/95aece5e68376e9a5d79d2385d755824dfac3ed8/proposed/middleware.md#21-server-vs-client-requests
    for details). It would be nice if "variant" parameters and return types were
    introduced, so the design could be improved (this is just an example, I
    guess there could be more aroud).

    Regards

    marco


    --
    PHP Internals - PHP Runtime Development Mailing List
    To unsubscribe, visit: http://www.php.net/unsub.php
  • Niklas Keller at May 15, 2016 at 1:28 pm

    2016-05-15 12:05 GMT+02:00 Rasmus Schultz <rasmus@mindplay.dk>:

    I know you're already voting on this, but I'd like to throw in a
    couple of late comments.

    My main problem with this RFC is the use of the interface keyword for
    something that almost by definition is not an interface.

    An interface is a set of methods, but an anonymous function is just
    one function, so it can't possibly implement an interface - this only
    works because of the single method constraint and some magical mapping
    of the closure as a method implementing that single-method interface.

    You can't, for example, add another method to one of these
    "interfaces", which is problematic - it's inconsistent with what
    interfaces actually are.

    In a sense, what you've really done, is you've overloaded the
    interface concept as a means of shoehorning typedefs into the
    language.

    Per the example in the RFC:

    interface IFoo {
    public function method() : int;
    }

    $cb = function () implements IFoo : int {
    return 42;
    };

    The coupling between $cb and IFoo is explicit - but the coupling
    between IFoo::method() and the closure is implied; it's something the
    interpreter, and the person reading the code, has to reason about.

    The one-method limitation isn't apparent from the "interface"
    declaration - that is, nothing about the interface declaration implies
    that it's not actually an interface, e.g. is not a set of methods.

    On the other hand, I can see the advantages of being able to treat and
    invoke the closure as if it were an object implementing an interface -
    it just seems like there's something missing. When a function
    "implements" an interface, it puts a constraint on that interface,
    preventing it from taking on another method - it imposes this
    constraint on the interface from the outside-in, which seems really
    counter intuitive to me.

    From the RFC:

    interface IFoo {
    public function method1();
    public function method2();
    }

    function () implements IFoo {};

    This is really surprising if you thought you understood what
    interfaces are: a set of methods.

    What suppose I write an interface that has a single method today - and
    consumers of my library begin to implement this interface using
    closures in this manner. But then tomorrow, I add another method to my
    interface? There is suddenly now direct upgrade path, no obvious way
    to fix the code - they can't simply implement that new method and move
    on, they now have to start refactoring everything from closures to
    actual classes.
    You can just convert the function to an anonymous class. If you add methods
    to an interface,
    that's a BC break for a major version, since it's not compatible anymore,
    everything needs
    an update either way.

    In that sense, this is worse than typedefs - at least typedefs
    consistently define precisely one function and not a set.

    Another disconnect from interfaces, is that objects can traditionally
    implement more than one interface - but an anonymous function can only
    implement a single interface. It's another deviation from actual
    interfaces, overloading the interface" keyword - it's inconsistent
    with interfaces, which is misleading and adds unnecessary learning
    curve.
    Literally, an interface is something you interact with. It's important for
    the consumer
    not the class implementing it. If you separate your concerns, you often
    have just one interface.

    Another downside of this proposal, is that a single functional
    interface effectively has two names: the type-name and the
    method-name; but both names map to the same thing, the anonymous
    closure. Method-names are necessary in interfaces because they
    identify the individual methods in a set of methods. But functional
    interfaces are not a set of methods. The functional interface
    type-name already designates the type. There is no reason it should
    need to have two names.

    How about the following alternative syntax?

    callable IFoo = function () : int;

    $cb = function () implements IFoo {
    return 42;
    };

    With this syntactic sugar, functional interfaces are distinctly
    single-method interfaces - you can't add another method.

    You no longer need to come up with two names for one thing.

    You don't need to repeat the return-type when implementing it.
    I like that syntax, but it has its own disadvantages.

    And you don't need to specify that extra method-name when invoking it:
    As a downside, you can't implement it in a normal class anymore and just use
    an instance of that class.

    if ($cb instanceof IFoo) {
    $number = $cb();
    }

    It also avoids the following conundrum:

    interface Foo {
    public function foo();
    }

    interface Bar {
    public function bar();
    }

    class Bleet implements Foo, Bar {
    // ...
    }

    function fluff(Bleet $bleet) {
    $bleet(); // ERROR!
    }

    In this example, the fluff() function thinks it's getting an anonymous
    function implementing Foo, but that's not the case. But it could be.
    You opened that door when you decided to overload interfaces - not
    only functions can implement these interfaces, classes can too.
    That's why you call it with the method name of the interface.

    I think this last example demonstrates a huge disconnect - you've
    defined something that is apparently both an interface and a typedef
    of sorts, but the resulting types are not in fact interchangible. That
    is, you can't swap out an anonymous function with a class implementing
    that interface, or vice-versa, unless the consumer relies on invoking
    the method rather than using the object as a callable. The fact that
    it's a callable isn't even a fact you can rely on anymore.

    I don't believe this is good. I think that the number of compiler
    errors specified in the RFC alone is a flashing red light.

    I think that other languages have typedefs rather than a syntactic
    version of single-method interface implementations.

    Is there a source of reference for this feature? I mean another
    language implementing this pattern?

    I have a feeling other languages likely do not implement something like
    this.

    I'm concerned that this might be one of those "original PHP features"
    that turns out to be a little too original.

    I personally would prefer to have something that's more familiar, e.g.
    something close to typedefs.

    Something that doesn't overload the concept of interfaces and requires
    a big section in the manual explaining how interfaces in PHP are not
    like interfaces in other languages, all the conditions, warnings,
    acceptable use and limitations.

    I'd prefer something that's simpler and easier to explain.

    On Thu, May 12, 2016 at 11:14 PM, Marco Perone wrote:
    On 12/05/2016 19:02, Rowan Collins wrote:
    On 12/05/2016 17:47, Andreas Heigl wrote:

    It's the other way around.

    The interface creates a contract that ensures that you can use ALL
    methods available in your SpecialClass.

    I don't think that's what the interface in the example means:

    interface Foo
    {
    public function foo(SpecialClass $object);
    }


    What this says is "an object adhering to interface Foo has a method foo
    which accepts any SpecialClass instance".

    In other words, it says that the following is bound to succeed:

    if ( $thing instanceOf Foo ) {
    $thing->foo( new SpecialClass );
    }


    Marco is right that a class with a wider definition fulfils this
    contract:
    class Bar implements Foo
    {
    public function foo(BaseClass $object)
    {
    // do something with BaseClass $object
    }
    }

    $thing = new Bar;
    if ( $thing instanceOf Foo ) {
    $thing->foo( new SpecialClass );
    }


    This is called "contravariance of parameters", and has come up a couple
    of
    times on the list. In particular, when return types were added, it was
    discussed that they should technically be covariant (subtypes can
    "narrow" return types, and say they only return a specific sub-type of
    the
    original contract).

    So from a design point of view, there is no reason not to support it;
    unfortunately, there are some technical hurdles to implementing that
    within
    the PHP engine. Here is a nice summary from Anthony Ferrara:
    http://marc.info/?l=php-internals&m=142791636808421&w=2

    Regards,

    Thanks Rowan for your response and for the link, very interesting.

    Adding a bit of context to my question, I asked it after following a
    discussion in the FIG mailing list.
    In PSR-7 you have a RequestInterface and a ServerRequestInterface with
    inherits from it (the BaseClass and SpecialClass of my example).
    Now there is a discussion on how to standardize a MiddlewareInterface.
    You would like to have an interface like

    interface ServerMiddlewareInterface
    {
    public function __invoke(ServerRequestInterface $request, ...)
    }

    which plays the role of Foo, and maybe an implementation

    class MyMiddleware
    {
    public function __invoke(RequestInterface $request, ...)
    {
    // do something with $request
    }
    }

    which plays the part of Bar.

    Since at the moment Bar can not implement Foo, the solution to this is, from
    a design point of view, a bit akward (see
    https://github.com/php-fig/fig-standards/blob/95aece5e68376e9a5d79d2385d755824dfac3ed8/proposed/middleware.md#21-server-vs-client-requests
    for details). It would be nice if "variant" parameters and return types were
    introduced, so the design could be improved (this is just an example, I
    guess there could be more aroud).

    Regards

    marco


    --
    PHP Internals - PHP Runtime Development Mailing List
    To unsubscribe, visit: http://www.php.net/unsub.php
    --
    PHP Internals - PHP Runtime Development Mailing List
    To unsubscribe, visit: http://www.php.net/unsub.php
  • Rasmus Schultz at May 15, 2016 at 2:21 pm

    As a downside, you can't implement it in a normal class anymore and just use
    an instance of that class.
    I see that as a plus, because, as a you said:
    That's why you call it with the method name of the interface
    Your logic is circular - you also said:
    As a downside, you can't implement it in a normal class anymore and just use
    an instance of that class.
    So you want it both ways, class and callable.

    But if I'm not supposed to invoke it as a callable, why implement it
    as a callable in the first place?

    In other words, you can't really have both - it doesn't really work: a
    callable can be treated as a class, but a class can't be treated as
    callable. If I have to treat everything as classes anyhow, why not
    just use classes?

    The only advantage seems to be shorter syntax than anonymous class
    declarations - the resulting objects have to be treated the same way
    in practice, e.g. you can't safely treat them as callables. That's
    where the chain falls off for me: you end up with callables, but you
    shouldn't depend on them being callables, so then why make them
    callables in the first place?

    The net result seems to be syntactic sugar for what is really just
    anonymous classes.

    I'd like to see an example of how this is really useful in practice -
    the example in the RFC seems rather hypothetical, and actually shows
    that the same thing is already possible with anonymous classes. Beyond
    syntax, where's the real advantage over actual classes or anonymous
    classes?

    On Sun, May 15, 2016 at 3:28 PM, Niklas Keller wrote:
    2016-05-15 12:05 GMT+02:00 Rasmus Schultz <rasmus@mindplay.dk>:
    I know you're already voting on this, but I'd like to throw in a
    couple of late comments.

    My main problem with this RFC is the use of the interface keyword for
    something that almost by definition is not an interface.

    An interface is a set of methods, but an anonymous function is just
    one function, so it can't possibly implement an interface - this only
    works because of the single method constraint and some magical mapping
    of the closure as a method implementing that single-method interface.

    You can't, for example, add another method to one of these
    "interfaces", which is problematic - it's inconsistent with what
    interfaces actually are.

    In a sense, what you've really done, is you've overloaded the
    interface concept as a means of shoehorning typedefs into the
    language.

    Per the example in the RFC:

    interface IFoo {
    public function method() : int;
    }

    $cb = function () implements IFoo : int {
    return 42;
    };

    The coupling between $cb and IFoo is explicit - but the coupling
    between IFoo::method() and the closure is implied; it's something the
    interpreter, and the person reading the code, has to reason about.

    The one-method limitation isn't apparent from the "interface"
    declaration - that is, nothing about the interface declaration implies
    that it's not actually an interface, e.g. is not a set of methods.

    On the other hand, I can see the advantages of being able to treat and
    invoke the closure as if it were an object implementing an interface -
    it just seems like there's something missing. When a function
    "implements" an interface, it puts a constraint on that interface,
    preventing it from taking on another method - it imposes this
    constraint on the interface from the outside-in, which seems really
    counter intuitive to me.

    From the RFC:

    interface IFoo {
    public function method1();
    public function method2();
    }

    function () implements IFoo {};

    This is really surprising if you thought you understood what
    interfaces are: a set of methods.

    What suppose I write an interface that has a single method today - and
    consumers of my library begin to implement this interface using
    closures in this manner. But then tomorrow, I add another method to my
    interface? There is suddenly now direct upgrade path, no obvious way
    to fix the code - they can't simply implement that new method and move
    on, they now have to start refactoring everything from closures to
    actual classes.

    You can just convert the function to an anonymous class. If you add methods
    to an interface,
    that's a BC break for a major version, since it's not compatible anymore,
    everything needs
    an update either way.
    In that sense, this is worse than typedefs - at least typedefs
    consistently define precisely one function and not a set.

    Another disconnect from interfaces, is that objects can traditionally
    implement more than one interface - but an anonymous function can only
    implement a single interface. It's another deviation from actual
    interfaces, overloading the interface" keyword - it's inconsistent
    with interfaces, which is misleading and adds unnecessary learning
    curve.

    Literally, an interface is something you interact with. It's important for
    the consumer
    not the class implementing it. If you separate your concerns, you often have
    just one interface.
    Another downside of this proposal, is that a single functional
    interface effectively has two names: the type-name and the
    method-name; but both names map to the same thing, the anonymous
    closure. Method-names are necessary in interfaces because they
    identify the individual methods in a set of methods. But functional
    interfaces are not a set of methods. The functional interface
    type-name already designates the type. There is no reason it should
    need to have two names.

    How about the following alternative syntax?

    callable IFoo = function () : int;

    $cb = function () implements IFoo {
    return 42;
    };

    With this syntactic sugar, functional interfaces are distinctly
    single-method interfaces - you can't add another method.

    You no longer need to come up with two names for one thing.

    You don't need to repeat the return-type when implementing it.

    I like that syntax, but it has its own disadvantages.
    And you don't need to specify that extra method-name when invoking it:

    As a downside, you can't implement it in a normal class anymore and just use
    an instance of that class.
    if ($cb instanceof IFoo) {
    $number = $cb();
    }

    It also avoids the following conundrum:

    interface Foo {
    public function foo();
    }

    interface Bar {
    public function bar();
    }

    class Bleet implements Foo, Bar {
    // ...
    }

    function fluff(Bleet $bleet) {
    $bleet(); // ERROR!
    }

    In this example, the fluff() function thinks it's getting an anonymous
    function implementing Foo, but that's not the case. But it could be.
    You opened that door when you decided to overload interfaces - not
    only functions can implement these interfaces, classes can too.

    That's why you call it with the method name of the interface.
    I think this last example demonstrates a huge disconnect - you've
    defined something that is apparently both an interface and a typedef
    of sorts, but the resulting types are not in fact interchangible. That
    is, you can't swap out an anonymous function with a class implementing
    that interface, or vice-versa, unless the consumer relies on invoking
    the method rather than using the object as a callable. The fact that
    it's a callable isn't even a fact you can rely on anymore.

    I don't believe this is good. I think that the number of compiler
    errors specified in the RFC alone is a flashing red light.

    I think that other languages have typedefs rather than a syntactic
    version of single-method interface implementations.

    Is there a source of reference for this feature? I mean another
    language implementing this pattern?

    I have a feeling other languages likely do not implement something like
    this.

    I'm concerned that this might be one of those "original PHP features"
    that turns out to be a little too original.

    I personally would prefer to have something that's more familiar, e.g.
    something close to typedefs.

    Something that doesn't overload the concept of interfaces and requires
    a big section in the manual explaining how interfaces in PHP are not
    like interfaces in other languages, all the conditions, warnings,
    acceptable use and limitations.

    I'd prefer something that's simpler and easier to explain.

    On Thu, May 12, 2016 at 11:14 PM, Marco Perone wrote:
    On 12/05/2016 19:02, Rowan Collins wrote:
    On 12/05/2016 17:47, Andreas Heigl wrote:

    It's the other way around.

    The interface creates a contract that ensures that you can use ALL
    methods available in your SpecialClass.

    I don't think that's what the interface in the example means:

    interface Foo
    {
    public function foo(SpecialClass $object);
    }


    What this says is "an object adhering to interface Foo has a method foo
    which accepts any SpecialClass instance".

    In other words, it says that the following is bound to succeed:

    if ( $thing instanceOf Foo ) {
    $thing->foo( new SpecialClass );
    }


    Marco is right that a class with a wider definition fulfils this
    contract:

    class Bar implements Foo
    {
    public function foo(BaseClass $object)
    {
    // do something with BaseClass $object
    }
    }

    $thing = new Bar;
    if ( $thing instanceOf Foo ) {
    $thing->foo( new SpecialClass );
    }


    This is called "contravariance of parameters", and has come up a couple
    of
    times on the list. In particular, when return types were added, it was
    discussed that they should technically be covariant (subtypes can
    "narrow" return types, and say they only return a specific sub-type of
    the
    original contract).

    So from a design point of view, there is no reason not to support it;
    unfortunately, there are some technical hurdles to implementing that
    within
    the PHP engine. Here is a nice summary from Anthony Ferrara:
    http://marc.info/?l=php-internals&m=142791636808421&w=2

    Regards,

    Thanks Rowan for your response and for the link, very interesting.

    Adding a bit of context to my question, I asked it after following a
    discussion in the FIG mailing list.
    In PSR-7 you have a RequestInterface and a ServerRequestInterface with
    inherits from it (the BaseClass and SpecialClass of my example).
    Now there is a discussion on how to standardize a MiddlewareInterface.
    You would like to have an interface like

    interface ServerMiddlewareInterface
    {
    public function __invoke(ServerRequestInterface $request, ...)
    }

    which plays the role of Foo, and maybe an implementation

    class MyMiddleware
    {
    public function __invoke(RequestInterface $request, ...)
    {
    // do something with $request
    }
    }

    which plays the part of Bar.

    Since at the moment Bar can not implement Foo, the solution to this is,
    from
    a design point of view, a bit akward (see

    https://github.com/php-fig/fig-standards/blob/95aece5e68376e9a5d79d2385d755824dfac3ed8/proposed/middleware.md#21-server-vs-client-requests
    for details). It would be nice if "variant" parameters and return types
    were
    introduced, so the design could be improved (this is just an example, I
    guess there could be more aroud).

    Regards

    marco


    --
    PHP Internals - PHP Runtime Development Mailing List
    To unsubscribe, visit: http://www.php.net/unsub.php
    --
    PHP Internals - PHP Runtime Development Mailing List
    To unsubscribe, visit: http://www.php.net/unsub.php
  • Niklas Keller at May 15, 2016 at 2:45 pm

    2016-05-15 16:21 GMT+02:00 Rasmus Schultz <rasmus@mindplay.dk>:

    As a downside, you can't implement it in a normal class anymore and just use
    an instance of that class.
    I see that as a plus, because, as a you said:
    That's why you call it with the method name of the interface
    Your logic is circular - you also said:
    As a downside, you can't implement it in a normal class anymore and just use
    an instance of that class.
    So you want it both ways, class and callable.

    But if I'm not supposed to invoke it as a callable, why implement it
    as a callable in the first place?

    In other words, you can't really have both - it doesn't really work: a
    callable can be treated as a class, but a class can't be treated as
    callable. If I have to treat everything as classes anyhow, why not
    just use classes?

    The only advantage seems to be shorter syntax than anonymous class
    declarations - the resulting objects have to be treated the same way
    in practice,

    From the RFC:

    The anonymous class version:

    - must use referencing, or fetch a new Countable object on each iteration,
    - is extremely verbose
    - must set dependencies in the constructor
    - has no support for lexical scope


    The functional interface version:

    - is sparse
    - is easier to reason about
    - does not require the use of references
    - supports lexical scope
    - Functional interface support does not change the definition of an
    interface, and only reuse the definition of a Closure.

    e.g. you can't safely treat them as callables. That's
    where the chain falls off for me: you end up with callables, but you
    shouldn't depend on them being callables, so then why make them
    callables in the first place?
  • Stanislav Malyshev at May 13, 2016 at 2:29 am
    Hi!
    The PHP manual says, regarding Interfaces, that the class implementing
    the interface must use the exact same method signatures as are defined
    in the interface. Not doing so will result in a fatal error.
    I think this is mainly for practical reasons - when you compile class
    Bar, you may not know what SpecialClass and BaseClass are. This is not a
    problem for checking the calls - if by the time you get to the call they
    are still not known, you can be sure whatever you've got as a parameter
    is *not* instance of them. However, if you wanted to check if Bar
    properly implements Foo, you'd need to know relation between
    SpecialClass and BaseClass. So that makes the deal way more complex,
    especially given that you'd need to load SpecialClass and BaseClass in
    the middle of defining Bar, so what if they depend on Bar?

    --
    Stas Malyshev
    smalyshev@gmail.com

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupphp-internals @
categoriesphp
postedMay 12, '16 at 4:37p
activeMay 15, '16 at 2:45p
posts9
users6
websitephp.net

People

Translate

site design / logo © 2018 Grokbase