PHP instanceof Operator Tips

The instanceof operator in PHP is great at making sure you are looking at a type of object before acting on it. Whilst it's straightforward to use on its own, using it can lead to some undesirable side effects.

As a basic example, let's create a couple of interfaces and classes to show the instanceof operator in action. Rather than just have some test names I am using object names that you might find in a system. This consists of User and Order that might form part of a commerce system.

interface UserInterface {}
class User implements UserInterface {}

interface OrderInterface {}
class Order implements OrderInterface {}

If we instantiate the User object we can detect if the user is an instance of the class User like this.

$user = new User();
var_dump($user instanceof User); // Returns true

As the User object implements an interface we can also detect if the User object is an instance of an interface like this.

var_dump($user instanceof UserInterface); // Returns true

Conversely, if we were to see if the $user variable we created is an instance of a different kind of object then it would return false, like this.

var_dump($user instanceof Order); // Returns false

If we aren't sure what kind of interface or object we are going to receive then we can pass in a variable that contains the name of the interface.

$interface = 'UserInterface';
var_dump($user instanceof $interface);

Note that just passing in the interface as a string, as in the following snippet, will produce a syntax error. The item you use to detect the type of object or interface must be either a variable or a fully qualified class or interface name.

var_dump($user instanceof 'UserInterface');
// PHP Parse error:  syntax error, unexpected ''UserInterface'' (T_CONSTANT_ENCAPSED_STRING) in index.php on line 2

Negating the instance of operator is a little less straightforward, but can be done by putting an exclamation mark at the beginning of the statement.

var_dump(!$user instanceof UserInterface); // Returns false

It is usually good practice to wrap the whole thing in brackets so that you can be sure of the outcome of the check.

var_dump(!($user instanceof UserInterface)); // Returns false

You can also compare the output of the instanceof operator to false, which essentially negates it. I find this a little more difficult to read, although you could also argue that you might miss the exclamation mark.

var_dump(FALSE === $user instanceof UserInterface); // Returns false

When namespaces are involved then care must be taken that the right syntax is used. When using the fully qualified namespace then the proceeding slash is required, otherwise the right instance won't be found. It's probably best practice to not use the fully qualified namespace at all though and just to rely on the interface name you are looking for. The following example is looking at a Drupal interface called FormStateInterface with three different ways of writing down the class name.

use Drupal\Core\Form\FormStateInterface;

var_dump($form_state instanceof Drupal\Core\Form\FormStateInterface); // returns false
var_dump($form_state instanceof \Drupal\Core\Form\FormStateInterface); // returns true
var_dump($form_state instanceof FormStateInterface); // returns true

A single instanceof check is easy enough to understand, but if you are trying to detect if an object is one of a series of types of objects then the code gets a little difficult to read. For example, let's say that you are trying to detect if an object was one of a series of the three types of interface defined at the beginning of the post; you might create an if statement like the following.

var_dump($object instanceof UserInterface || $object instanceof ProductInterface || $object instanceof OrderInterface); // returns true

This returns true, which indicates that the object is one of the instance types we are looking for.

I found a good function that allows this to be encapsulated neatly whilst reading stack overflow the other day (see the original stack overflow comment here). To detect if an object is one of a series of types you can use the following function that will take an object and an array of classnames.

function isInstanceOf($object, Array $classnames) {
    foreach($classnames as $classname) {
        if($object instanceof $classname){
            return true;
        }
    }
    return false;
}

This can be run in the following way. Passing in the previously created user object and an array that contains the instance types we are looking for as strings returns true.

var_dump(isInstanceOf($user, ['UserInterface', 'ProductInterface', 'OrderInterface'])); // Returns true

The instanceof operator will also work for child objects. Take the following code that creates an interface, defines a User class and then defines an Administrator class that extends the User class.

interface UserInterface {}
class User implements UserInterface {}
class Administrator extends User {}

We can instantiate the User and Administrator objects and try to detect them in different ways. 

$user = new User();
$administrator = new Administrator();

var_dump($user instanceof UserInterface); // Returns true
var_dump($user instanceof User); // Returns true
var_dump($user instanceof Administrator); // Returns false

var_dump($administrator instanceof UserInterface); // Returns true
var_dump($administrator instanceof User); // Returns true
var_dump($administrator instanceof Administrator); // Returns true

The thing to note from the above code is that a User object is not an instance of an Administrator object and so that comparison returns false. The Administrator object is, however, an instance of the User object and so that comparison is true.

What happens then, if we are trying to detect the instance of just an Administrator object, and not a User object. Unfortunately the instanceof operator can't be used here. Instead, in order to detect just the one type of class you are looking for you'll need to use a different comparison using the get_class() PHP method.

var_dump(get_class($administrator) === 'Administrator'); // returns true
var_dump(get_class($user) === 'Administrator'); // returns false

Note that the User object is not an Administrator object and so the comparison returns false. The above can also be written differently using the ::class magic constant to return the string of the class name. This is actually the way I normally print out class names and the way I would write this type of comparison.

var_dump(get_class($administrator) === Administrator::class); // returns true
var_dump(get_class($user) === Administrator::class); // returns false

Ultimately, the instanceof operator is useful, but you need to follow these rules to get the most out of it.

  • When detecting a type of object try as much as possible to detect the instance, rather than the class. This allows for good SOLID principles to be followed.
  • Don't use the fully qualified name of the interface. Using just the interface name is sufficient and follows best practice.
  • If looking for the absence of an instance then be sure to wrap the comparison in brackets to avoid any inconsistencies in the precedence of surrounding operators.
  • Be careful when using inheritance, all objects inheriting a parent class will be detected as being the same type as the parent.

Comments

Thanks for the article! Talking about rules, particularly in Drupal 10: Should "instanceof" be used in order to check if an object is not null or falsy, even though I'm sure that the previous load method will return null or an instance of that class?

So instead of

$entity = Entity::load(1)
if ($entity) {
	// go on with the entity...
}

always use

$entity = Entity::load(1)
if ($entity instanceof EntityInterface) {
	// go on with the entity...
}

What is best practice? Are there any downsides using "instanceof" in such a case?

Permalink

Hi Phillipp,

Thanks for reading!

Interesting question. My personal preference is to avoid all true/false assumptions and try to use comparisons that don't use casting or coercion to make the comparison. I know, the result of "Entity::load(1)" should always be the entity or null, but you shouldn't assume that this is the case. Making concrete assertions makes your code more robust.

In which case I would use the second example in your comment.

With one caveat. Try to use the principle of early return. So instead of wrapping the use of the entity in the if statement you instead want to wrap the failure of the entity load in the if statement. This means you can use the entity later on and be sure that the entity exists in the correct state.

$entity = Entity::load(1)
if (($entity instanceof EntityInterface) === FALSE) {
  // Throw an error of some kind.
}
// go on with the entity...

In terms of best practice, I would also avoid the use of Entity::load(1). Instead use dependency injection to inject the entity type manager and load the entity with $this->entityTypeManager->getStorage($entity_type)->load(1). This ensures that the code is properly decoupled and can be unit tested.

Name
Philip Norton
Permalink

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
1 + 0 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.