closedroad.320PHP skips some code

When PHP skips some code, it features code optimizations : it skips automatically an instruction, as soon as it detects useless job. No need for code cache, this is plain vanilla PHP.

Implied if

The classic case is when using logical operators && or ||.

When the first operand of an && is false, then the the result will be false, whatever the second operand. Whenever this is the case, PHP skips the second instruction.

<?php
function foo() { print __METHOD__. "\n" ; }
function bar() {print __METHOD__. "\n" ;}

foo() && bar() ;
?>

On the other hand, when calling the same set of functions with || operator, PHP will skip the second operand if the first is true. When the first is true, the result is true, whatever the value of the second operand.

<?php
foo() || bar() ;
?>

This is why those logical operators are also used as implied if then structure. You may find this in numerous , with structures like this :

<?php
defined('FRAMEWORK_IS_INITED') || die('No direct access') ;
!defined('FRAMEWORK_IS_INITED') && die('No direct access') ;
?>

Skip constructor’s argument

There is also another situation that has the same ‘skip’ effect : creating an object in a class without a constructor. This seems like a long list of argument. Here is how it is build :

<?php
function foo() { print __METHOD__. "\n"; }
class b {}
class e extends b {}
class c { function __construct($a) {} } 
class d extends c { } 

new c(foo()); 
new d(foo()); 

?>

This will display foo() as one may expect. Even if we don’t do anything with the argument (which would be bad in a real code), foo() is called as it is an argument. However, this will not display anything  :

<?php
new b(foo());
new e(foo());
?>

b and e have no constructor, nor their respective parents. PHP finds it useless to assign arguments to a non-existent function that can’t and won’t be executed, so it skips completely this operation. This is fast, and it also prevents foo() to be called.

It’s not a bug, it’s a feature

This was implemented as it is in PHP 5.0, and hasn’t changed since. It won’t be changed, so better know about it. It is still valid for PHP 7.

If you want to avoid this trap, you may consider two approaches :

  • Avoid evaluating code during instantiation : send variables or literals, but never call a method or function
  • Always put a constructor, even an empty one.

Many thanks to Reen from Sweetlake PHP for bringing this to me, and to Julien Pauli to confirm.