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:
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:
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:
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:
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.