PHP type hints: self and parent

Back-end

PHP type hints: self and parent

Hannes Van De Vreken

Hannes Van De Vreken

In PHP we can type hint function arguments since version 5.0. Over the years and with newer versions of PHP the number of possible type hints have increased. Here’s an overview:

<?php

public function foo(
    // Since 5.0
    self $self,
    parent $parent,
    FooInterface $foo,
    // Since 5.1
    array $array,
    // Since 5.4
    callable $callable,
    // Since 7.0
    bool $bool,
    float $float,
    int $int,
    string $string
) {
    ...
}

Since PHP 5.0 we’ve been able to type hint function arguments and since PHP 7.0 we’re aso able to type hint scalar values. The entire set of type hints can be used for function return type hinting since version 7.0 as well. Let’s take a closer look at self and parent. They’ve been around for a long time
but we don’t see them very often. Why is that?

self

Self means: an object of the same type (the current class or a subclass). Every variable passed must be an instanceof the same class.

Function argument type

<?php

interface Person {
    public function addSibling(self $sibling)
    {
        // ...
    }
}

When modelling relations between nodes/objects/…, it happens that a relationship needs to be modelled between two objects of the same type. In this case it’s useful to use the self type hint.

When you create an implementation of this interface (or when overwriting this in case of less SOLID designed code) it will be necessary to replace the self type hint with the original class/interface name.

<?php

use Person as PersonContract;

class Person implements PersonContract {
    public function addSibling(PersonContract $sibling)
    {
        // ...
    }
}

Return type

Let’s take a look in what situations we can use self as a return type.

Mutators

One of the more obvious use case is setters (or mutators) that allow method chaining.

<?php

$foo->setBar($bar)->setBaz($baz);

If you use self return type it doesn’t matter if the method returns a cloned version of the object (this is the case when you’re dealing with immutable objects). The object that is returned is of the same type as the object on which the method is called.

<?php

interface Foo {
    /**
     * @param Bar $bar
     * @return self
     */
    public function setBar(Bar $bar) : self;
}

When extending or implementing a method in a subclass you have to make type hint explicit so that declaration is compatible.

<?php

use Foo as FooContract;

class Foo implements FooContract
{
    /**
     * {@inheritdoc}
     */
    public function setBar(Bar $bar) : FooContract
    {
        // ...
        return $this;
    }
}

If you use this object it will tell your IDE that whatever is returned will be of type Foo (the interface). So practically it won’t/shouldn’t autocomplete method names of the Foo implementing class anymore.

<?php

/* @var Foo $foo */
$foo->setBar(new Bar())->setBaz(new Baz());

In the example above, if setBaz is not a method of the interface Foo, it will not be recognized by the IDE. Thus for chaining methods, the return type self isn’t so useful.

Prior to PHP 7.0 we usually only declared return types in the docblocks with @return $this, @return self or @return static.
For chained methods I still use @return static and @return $this in the docblocks.

Factory methods

It’s not easy to come up with examples of non-setter methods that still return objects of the same class. Here’s one:

<?php

interface Foo
{
    public function wrapWithLogDecorator(PsrLogLoggerInterface $logger) : self;
}

An implementation would be:

<?php

trait LoggerWrapping
{
    public function wrapWithLogDecorator(PsrLogLoggerInterface $logger) : Foo
    {
         return new LogWrapper($this);
    }
}

I’m not saying this is good design, though. This method clearly doesn’t belong in this interface. There are better factory-like methods that can have a self return type hint. I just can’t come up with a practical one at this moment. Feel free to suggest an example in the comments below.

parent

I noticed from the PHP docs that parent is a valid type hint. Let me explain what parent means in the context of type hinting:

<?php

class Foo extends Bar {
    public function setBar(parent $bar)
    {
        // ...
    }
}

parent always refers to the class that you are extending. Thus in the example above it means: Bar, or one of its subclasses. The object that gets passed here could be of a sibling class of Foo, for example.

The type hint can not refer to an interface. It’s exactly the same as when you would call a method on a parent class (parent::__construct for example). You can only use that when the current class actually extends a parent class.

There are a couple of reasons why the parent type hint isn’t used much:

When your code’s design respects the SOLID principles, it should strike you that almost everything is either an interface or an implementing class (leaf node in inheritance tree). You’ll see that the number of abstract classes will be very small and almost no classes get extended. So using the parent type hint is not that useful, except in some very rare cases.

Secondly, it is generally better to type hint interfaces. This again is part of the SOLID design principles: Liskov Substitution Principle (objects should be replaceable with objects of subtypes), Interface Segregation Principle and Dependency Inversion Principle (Depend upon abstractions(interfaces)). Thus, again: the amount of type hints that are class type hints and not interface type hints is very small.

If you’re having questions or would like to add something, feel free to leave a comment below.

Cheers!

our blog Related blog articles

Do you have a question? Leave a comment