PHP be all like: that nonexistent class be cool

Blog post
by Tim MacDonald on the

I was recently helping out on an open source issue where the developer was having trouble catching exceptions the library was throwing. Having a look at their code snippets I realised they had not imported the exception classes, but also that PHP was not checking that the exception classes they were attempting to catch actually existed.

I've noticed this kind of thing before in other scenarios, so I thought I would collate a list of them in hope that it could help others in the future. It really boils down to: if you don't check yourself, attempt to instantiate the class, or attempt to access something on the class (such as a static method), PHP may not tell you anything is wrong.

References using the ::class constant

One of these cases is when using the special ::class constant, PHP does not check that the class you are referencing actually exists. It is not until you manually check with class_exists($class), or try to instantiate an instance of the class, that PHP will complain. This is probably the least troublesome of the lot, as if you try to work with the class you are going to know if something is wrong.

// PHP does not complain about this
$class = NonExistentClass::class;

// will return false
class_exists($class);

// Fatal error: Uncaught Error: Class 'NonExistentClass' not found
new $class();

Check out a demo on 3v4l.

instanceof checks

This one could trip you up if you are not careful. When performing an instanceof check PHP does not ensure that the class you are checking against actually exists. Checks against nonexistent classes will just evaluate to false and continue on their merry way.

class ClassThatExists
{
    //
}

$class = new ClassThatExists();

if ($class instanceof NonExistentClass) {
    // does *not* evaluate to true OR cause an error
}

if ($class instanceof ClassThatExists) {
    // *does* evaluate to true.
}

Check out a demo on 3v4l.

try / catch blocks

Chances are this one is less of an issue as the exception will bubble up and hopefully you will be notified of the error if you have error tracking setup.

class ExceptionThatExists extends Exception
{
    //
}

try {
    throw new ExceptionThatExists();
} catch (NonExistentException $exception) {
    // does *not* execute this catch block or cause an error
} catch (ExceptionThatExists $exception) {
    // *does* execute this catch block.
}

Check out a demo on 3v4l.

As you can see it is possible to reference classes in PHP that do not exist. This can be a problem in different scenarios, such as if you think you are catching an exception but have not imported the class properly, or perhaps misspelt the class name. I guess this comes from the dynamic nature of PHP, and if PHP were to check that each class you reference existed at runtime, this would no doubt have a performance impact. There is plenty you can do to avoid this happening, but it is something you should keep in mind, especially if you are newer to PHP.

The remedy

  1. Make sure you always import the classes you are working with. If you are using an editor / IDE where you can have PHP analysis available as you type (such as VIM or PHPStorm), it will yell at you about the nonexistent classes.
  2. Write tests that will help detect these issues before they become a problem. However, this issue can also happen on your tests!
  3. Use static analysis tools that will help catch these types of errors. Checkout Psalm and PHPStan to get started.
  4. All of the above.

Not always the case

There are times when PHP will check that the class exists without you attempting to interact with it. As an example, if you are declaring a class that extends another class. If you attempt to extend a class that does not exist PHP will yell at you.

class BaseClass
{
    //
}

class ThisIsOkay extends BaseClass
{
    //
}

class PhpWillYellAtYou extends NonExistentClass
{
    //
}

Check out a demo on 3v4l.

If you know of any other scenarios where this can happen, I would love to hear about them. Hit me up on Twitter and I will update the post.