3

Consider the following code.

class C {}

/**
 * @throws \InvalidArgumentException
 */
function classCreateInstance($class) {
  if (!is_string($class)) {
    throw new \InvalidArgumentException("Class name must be a string.");
  }
  if (!class_exists($class)) {
    throw new \InvalidArgumentException("Class '$class' does not exist.");
  }
  return new $class();
}

/**
 * @return C
 */
function foo() {
  return classCreateInstance(C::class);
}

There is one function that may throw an exception, because it does not know anything about the $class argument.

On the other hand, the calling code knows that 'C' is a valid class name, so it would like to assume that the "InvalidArgumentException" will never occur. It would like to avoid verbose try/catch, and it would like to avoid having its own @throws tag. Especially if it is not "allowed" to have one, because it is implementing an interface that does not annotate the exception.

But, from an IDE / automatic code validation perspective, ignoring this exception is not safe.

So.. what is the correct way to deal with exceptions that are "almost impossible" from a calling code perspective?

donquixote
  • 4,877
  • 3
  • 31
  • 54

1 Answers1

10

In Java. there is a distinction between "checked" exception classes and "unchecked" exception classes. Only the checked exceptions are part of the interface contract, whereas throwing an "unchecked" exception is allowed even if the interface does not declare it.

In Java, in the "should never occur" case, one would throw an unchecked exception - e.g. a "RuntimeException".

In PHP, this is all convention-based. Methods can throw whichever exceptions they want. Adding a @throws tag in the doc comment is nice, but it is not enforced by the language.

However, an IDE, or possibly other code reviewing tools, can be configured to analyse code based on the Java model of checked vs unchecked exceptions.

E.g. PhpStorm has options to not require a @throws doc tag for RuntimeException and LogicException. So this would allow to treat these as "unchecked", but then write custom exception classes and treat them like "checked" exceptions in Java.

Native exception classes in PHP: http://php.net/manual/en/spl.exceptions.php#spl.exceptions.tree Not that these all inherit from LogicException or RuntimeException, so they would all be considered as "unchecked". Only the root class, Exception (and custom child classes) would be considered as "checked".

This distinction also means that if you call a method/function with no declared/annotated exception, you still need to consider that an exception could be thrown nevertheless. This could be covered e.g. by a try/catch at the top level of the application. (e.g. an index.php)

donquixote
  • 4,877
  • 3
  • 31
  • 54