2

SCENARIO


I am implementing "command pattern"-like classes. Since they are so similar to each other in so many sense (e.g. functionally, their relation in domain problem, etc.), it is desirable to enforce them having similar structure.

Let us use an anecdotal example Merger.

class Merger {
    /* ... details are omitted ... */

    public function execute (self $rval): self
    {
        // ... merge $rval to $this
        return $this;
    }

    /* ... details are omitted ... */
}

Then we have another example Filter

class Filter {
    /* ... details are omitted ... */

    public function execute (self $rval): self
    {
        // apply filters from $rval to $this
        return $this;
    }

    /* ... details are omitted ... */
}

There are many other class that implements public function execute (self $param): self.


PROBLEM


I attempt to extract public function execute (self $param): self to its own structure then have other class which implements that function to somehow derive from such structure.

The self part from the method signature is slightly problematic: I want self to refer the class which implements the function execute(self $param).

ATTEMPTED SOLUTION

(a) Interface

My attempt of using interface did not work because self points to the interface e.g.

interface IExecute {
    public function execute (self $param): self;
}

class Merger implements IExecute {
    /* ... details are omitted ... */
    
    public function execute (self $param): self
    {
        // ... merge $param to $this
        return $this;
    }
    
    /* ... details are omitted ... */
}

$merger = new Merger();
/**
 * Output:
 * <b>Fatal error</b>:  Declaration of Merger::execute(Merger $param): Merger must
 * be compatible with IExecute::execute(IExecute $param): IExecute
 * in <b>[...][...]</b> on line <b>7</b><br />
 *
 * PHP complains that Merger::execute signature is incorrect because $param
 * is of type IExecute + its return type is Merger instead of IExecute.
 * 
 * I want $param to be a Merger
 */

(b) Inheritance

Using inheritance yields the same result as using interface i.e. PHP blasts error complaining how the method signature is incompatible.

class Executor {
    public function execute (self $param): self
    {}
}

class Merger extends Executor {
    /* ... details are omitted ... */
    
    public function execute (self $param): self
    {
        // ... merge $param to $this
        return $this;
    }
    
    /* ... details are omitted ... */
}

$merger = new Merger();

/**
 * Output:
 * <b>Fatal error</b>:  Declaration of Merger::execute(Merger $param): Merger must
 * be compatible with Executor::execute(Executor $param): Executor
 * in <b>[...][...]</b> on line <b>18</b><br />
 * 
 * $param in Merger::execute has type of Merger instead of Executor
 * 
 * I want $param in Merger::execute to be a Merger, not Executor
 */

(c) Trait

One approach that I come up with (and works as I intended to) is by using trait.

trait Executor {
    abstract public function execute (self $self): self;
}

Then have the classes to use that trait.

class Merger {
    use Executor;

    /* ... details are omitted ... */

    public function execute (self $rval): self
    {
        // ... merge $rval to $this
        return $this;
    }

    /* ... details are omitted ... */
}

This works perfectly well since self points to the class that uses the trait Executor.


GOAL


trait does what I want, though having to use use <trait name here> is a slight mental overhead i.e. the developer has to know that they have to use trait to enforce class structure (instead of, for example, have a base class which is more intuitive IMO).

This finally brings to my question: is there a better approach than using trait to handle this issue? Preferably the one that has more emphasis on enforcing a structure intuitively

I am attracted to the solution provided from this SO post (solution is on poster's question), though it is on C#. Kind of pity that one has to use a work around to do generic typing in PHP


EDIT 16/12/2019: I decided to mark Andy's answer as the correct one, though I'm still hoping another answerer will post another solution (or rather, another take on explaining why no other solution exists at the time of answering)

5
  • 1
    In more advanced languages, generic types are used for what you want. PHP does not offer that, so you gotta work with what you gotta work, either change your abstraction to have abstract methods and push implementation detail into override implementations of the methods, or keep your code completely dynamic with and cast your objects to proper instances.
    – Andy
    Dec 4, 2019 at 9:59
  • 1
    Can you clarify why exactly why you want to enforce the methods to have 'self' type parameters and returns? Is there calling code that would rely on that when using the interface?
    – bdsl
    Dec 8, 2019 at 0:36
  • I would probably delete the return $this; statements, and change those return types to void. You don't need to return the $this reference to the calling function because the caller already has it. The interface can't specify that the returned value will reference the same object that was called, even if it specifies the exact class.
    – bdsl
    Dec 8, 2019 at 0:38
  • @bdsl What driven me to have self type is that the class will act as a builder. Plus the point that many classes share common interface (I'm not saying that the only common thing the classes have is the interface, though. That's why I want to take out the common interface to its own structure) You have a good point, I don't really need to return $this. In fact I can just stash my plan extracting the common interface (which I have considered greatly), however for the sake of the question let us assume that this requirement cannot be compromised.
    – mghazian
    Dec 8, 2019 at 4:19
  • @Andy Thanks for your input! I think your comment suits as an answer. If you can add that as an answer that would be great
    – mghazian
    Dec 8, 2019 at 4:23

2 Answers 2

1

As I have mentioned in a comment, in several languages generic types may be utilised to achieve exactly what you're after.

E.g. in Java, the code could look like this:

interface IExecute<T> {
    @NotNull T execute(@NotNull T param);
}

class Merger implements IExecute<Merger> {

    @Override
    public @NotNull Merger execute(@NotNull Merger param) {
        return this;
    }
}

By defining this, the correct implementation is checked and enforced at compile-time. Unfortunately, PHP does not support such construct and if you want to make sure your code is not completely dynamic, a different approach has to be taken.

I don't know how you actually plan using the IExecute interface, however the dynamic code could be mitigated to some degree by defining an abstract class for Merger, Filter,... and instead of implementing the IExecute interface on the implementations of Mergers, Filters,... you would extend the correct abstract class.

In PHP this could look like this:

interface IExecute
{
    public function execute(IExecute $self): IExecute;
}

abstract class MergerExecute implements IExecute
{
    final public function execute(IExecute $self): IExecute
    {
        if (!($self instanceof MergerExecute)) {
            throw new RuntimeException("...");
        }

        $this->doExecute($self);

        return $self;
    }

    protected abstract function doExecute(MergerExecute $self): void;
}

abstract class FilterExecute implements IExecute
{
    final public function execute(IExecute $self): IExecute
    {
        if (!($self instanceof FilterExecute)) {
            throw new RuntimeException("...");
        }

        $this->doExecute($self);

        return $self;
    }

    protected abstract function doExecute(FilterExecute $self): void;
}

class Merger extends MergerExecute
{
    protected function doExecute(MergerExecute $self): void
    {

    }
}

class Filter extends FilterExecute
{
    protected function doExecute(FilterExecute $self): void
    {

    }
}

which still allows you to write a method like the following one:

function foo(IExecute $applyTo, IExecute ...$executors)
{
    foreach ($executors as $executor) {
        $applyTo = $executor->execute($applyTo);
    }
}

but inevitably failing during runtime if incorrect arguments are supplied. For a more advanced support and checks, in PHP one needs to wait until generic types are approved and added to the language.

2

The feature you are looking for is sometimes called MyType, and unfortunately, it doesn't exist in any mainstream programming language (including PHP).

The idea of MyType is that you have a special type annotation that means "whatever my current type is". It is actually similar to self in PHP, but it always refers to the type of the current class / interface, not the type where it appears. With a MyType feature, your code would look pretty much like your interface example:

interface IExecute {
    public function execute (this.type $param): this.type; // syntax I just made up
}

Note: Scala has a slightly related concept, which is the singleton type of this. However, singleton types (as the name implies) only have one value. I.e. the one and only value that is of type foo.type is foo. Likewise, the only value of type this.type is this. So, this wouldn't work in general. In your example, it looks like you are actually mutating this and returning it, so this would "half" work in Scala:

trait Executor {
  def execute(param: WhatToPutHere): this.type
}

class Merger extends Executor {
  override def execute(param: Merger) = ???
}

You can declare the method to return this, but you cannot declare the parameter type to be "the same type as this, but not the same object as this".

Another solution that is used in programming languages with Parametric Polymorphism is F-bounded Polymorphism.

It works something like this (example in Scala):

trait Executor[T] {
  def execute(param: T): T
}

class Merger extends Executor[Merger] {
  override def execute(param: Merger) = ???
}

Note, however, that this is not as safe as a MyType solution, since there is nothing stopping you from passing a different class as the type argument, something like this:

class EvilMerger extends Executor[SomethingCompletelyUnrelated] {
  override def execute(param: SomethingCompletelyUnrelated): SomethingCompletelyUnrelated = ???
}

Also, there is the obvious problem that PHP doesn't support Parametric Polymorphism, although there is an RFC for that, which would allow you to write something like this:

interface IExecute<T> {
    public function execute (T $param): T;
}

class Merger implements IExecute<Merger> {
    public function execute (Merger $param): Merger
    {
        // … merge $param to $this
        return $this;
    }
}

Scala also has the ability of annotating the type of this. This is useful specifically in traits, where you can thus restrict what the types of the classes are that the trait is mixed into. With a combination of self-type annotations, F-bounded Polymorphism, and singleton types in Scala, we can actually achieve what you want:

trait Executor[T] {
  this: T =>
  def execute(param: T): this.type
}

class Merger extends Executor[Merger] {
  override def execute(param: Merger) = ???
}

However, none of the features required to implement this exist in PHP, so the options that you already identified, are all you got.

2
  • What you call F-bounded Polymorphism in C++ world is called CRTP. Curiously returning template pattern.
    – jaskij
    Dec 3, 2019 at 20:04
  • It's actually Curiously Recurring Template Pattern, AFAIK. Yes, the C++ community likes to invent new names for established concepts (e.g. virtual function for method or CRTP for F-bounded Polymorphism) and re-use existing names for different contexts (e.g. functor which is actually a function object and has very little to do with functors). Dec 3, 2019 at 20:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.