Don't call instance methods statically

There are quite a few things in PHP 4 that were a bit strange. One example is that PHP 4 allowed static calling of instance methods:

1 2 3 4 5 6 7 8 9 10 11 12
<?php
class Something
{
function doWork ( )
{
var_dump ( 'doing work' ) ;
}
}

$something = new Something ;

Something :: doWork ( ) ;

To keep backwards compatibility with PHP 4, this code works up to PHP 5, even though doWork() is not declared static, and called statically using :: rather than the object operator ->. PHP 5 at least complains with the E_DEPRECATED error:

Deprecated: Non-static method Something::doWork()
should not be called statically in ...

Now things will get really weird. When calling an instance method of another class statically, the $this context would carry over from the caller to the called class. In other words, $this suddenly refers to another object instance:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
<?php
class Something
{
public function doWork ( )
{
var_dump ( $this ) ;
}
}

class Another
{
public function run ( )
{
return Something :: doWork ( ) ;
}
}

$something = new Something ;

$something -> doWork ( ) ;

$another = new Another ;

$another -> run ( ) ;

While in PHP 5, this used to be an E_STRICT error, PHP 7 will emit an E_DEPRECATED error.

There are two problems with E_STRICT errors. First, E_ALL does not include E_STRICT before PHP 5.4, so in older versions you have to set error reporting to E_ALL & ~E_DEPRECATED (both error levels are bit fields, so you need a bitwise OR (&) to combine both). Second, you have to enable this error reporting level in php.ini, setting it at runtime by calling the error_reporting() function will not work, because E_STRICT error messages are already generated at compile-time.

When the above example gets executed with PHP 5, $this inside Something referred to the instance of Another, which is a really unexpected behavior. PHP 7, on the other hand, will complain about an undefined variable:

Notice: Undefined variable: this in ...

Since $this is undefined, trying to call a method will lead to another error, which in our example turns into a fatal error, since it is not caught:

Fatal error: Uncaught Error:
Using $this when not in object context in ...

One might wonder why such a weird behavior still has not been removed from PHP after more than 10 years. The reasoning is that before something can be removed from PHP, it has to be marked as deprecated first, only then it can be removed in the next major version. PHP upholds backwards compatibility, that is for sure. Even if it feels ludicrous, we will have to wait until PHP 8 to finally get rid of this PHP 4 relic.

Should your application call non-static methods statically, you can fix this by injecting an instance of the class. You can then modify the call to use the object operator -> instead of the :: operator:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
<?php
class Something
{
public function doWork ( )
{
var_dump ( $this ) ;
}
}

class Another
{
private $something ;

public function __construct ( Something $something )
{
$this -> something = $something ;
}

public function run ( )
{
return $this -> something -> doWork ( ) ;
}
}

$something = new Something ;

$something -> doWork ( ) ;

$another = new Another ( $something ) ;

$another -> run ( ) ;

First, we call the doWork() method directly on the instance of Something, then we call it from the instance of Another. The result is

object(Something)#1 (0) {
}
object(Something)#1 (0) {
}

In both cases, $this refers to the Something instance.

For the sake of completeness, we should mention that PHP also allows calling of static methods dynamically:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<?php
class Something
{
public static function doWork ( )
{
var_dump ( 'do work' ) ;
}

public function __construct ( )
{
$this -> doWork ( ) ;
}
}

$something = new Something ;

Fortunately, in this case, there is no problem with $this context propagation in the first place. This, however, does not argue away the fact that one should not write code like this.


This article is an excerpt from our eBook PHP 7 explained.

We keep PHP 7 explained up to date with each PHP 7 release, including the upcoming PHP 7.2. For more insights into PHP 7, get your copy now, it includes life-long free updates.

About the authors

Sebastian Bergmann
Sebastian Bergmann
Twitter LinkedIn Xing

Sebastian Bergmann, creator of PHPUnit, is an internationally sought-after expert who has played a vital role in professionalizing the PHP community.

Arne Blankerts
Arne Blankerts
Twitter LinkedIn Xing

Arne Blankerts has created solutions far ahead of the times already years ago, and finds security issues with magic intuition.

Stefan Priebsch
Stefan Priebsch
Twitter LinkedIn Xing

For over 20 years, Stefan Priebsch has been finding sustainable solutions using a unique blend of new ideas and proven approaches.

Share this article
Why Developers Should Not Code Conferences: for Fun or Profit?