weird php behaviour with classes and static methods - php

I maintain an application that uses a (to me) surprising PHP quirk/bug/feature. Consider this code:
<?php
class Bar {
// called statically
public function doStuff() {
print_r($this);
}
}
class Foo {
public function main() {
Bar::doStuff();
}
}
$foo = new Foo();
$foo->main();
Running on PHP 5.2.x, the output is:
Foo Object ( )
That means, although Bar::doStuff() is called statically, it still has access to $this where $this is a reference to the object that called Bar::doStuff(). Never came across that behaviour until recently. Quite evil to rely on this in production code if you ask me.
If you add a static and change the method signature to public static function doStuff() it throws a E_NOTICE: Undefined variable: this - which seems right to me.
Anyone has an explanation for this behaviour?

In PHP 5.3 at least, you get a strict warning:
PHP Strict Standards: Non-static method Bar::doStuff() should not be called statically, assuming $this from incompatible context in /tmp/test.php on line 11
And quite rightfully so.

It might be best to create some type of Printable_Object class and simply inherit from that. Add the doStuff() method to that class, and use the inherited method properly.

Related

Invoking a non-static method via the scope resolution operator

I've found some strange (for me) behavior of the PHP interpreter and I'm not sure if it's safe to use it in production or not.
When we call Foo::bar() and the Foo class does not have the static bar method but it has non-static bar method, the interpreter will invoke non-static bar on null (yes, it sounds ridiculous). I expected the __callStatic to be invoked in this case. But it's not what is happening for some reason.
I've then found a handy usage for this behavior: to provide the class with static and non-static methods with the same name like this:
class Foo
{
public function bar(){
if (isset($this)) {
$this->nonStaticBar();
} else {
static::staticBar();
}
}
private function nonStaticBar() {
echo "Non-static\n";
}
private static function staticBar() {
echo "Static\n";
}
}
(new Foo())->bar(); // Output: "Non-static"
Foo::bar(); // Output: "Static"
Yes I know, that this approach is not elegant and architecturally wrong. The question is if it's safe (standard-compliant) to use this "feature" or not. Are there any other cases when isset($this) can equal false?
While your above example does work, it is not best practice.
This is recognized in the PHP documentation here and states that in PHP versions before version 7, if E_STRICT error reporting is enabled then it will emit the error:
Strict Standards: Non-static method Foo::bar() should not be called statically in /Path/to/file.php on line 22
Additionally in PHP versions 7 and above calling static functions statically is deprecated and will cause the following error upon execution:
Deprecated: Non-static method Foo::bar() should not be called statically in /Path/to/file.php on line 22

Statically calling error while updated php version

I have a web application that I just updated from 5.3 to 5.5 on my server and i get around 200-300 errors like below. I understand this is a deprecation error and is it possible to solve all in one go and not hide the errors?
Strict standards: Non-static method xyz() should not be called statically.
Calling non-static methods statically generates an E_STRICT level warning.
Function xys is not static, but is clled statically. See difference below.
<?php
// Calling non-static function, first create object by class
$foo = new Foo();
$foo->xyz();
// Calling static function (you are doing this)
Foo::xyz();
?>
You should define your function like this. Find where xyz is defined.
public static function xyz(){}
Then it wont throw strict standard error. Just add static to your function and all errors will be gone.
You don't have to change it in all 200 files. This function is just called in 200 files. That dosen't meen that you have bug in all of them.
NOTE: from docs
Because static methods are callable without an instance of the object created, the pseudo-variable $this is not available inside the method declared as static.
So, if your are using $this in function xyz, you have to use my first example. Then it means fixing all 200 files.
See docs for static keyword.
Just make the function static.
The fix is a simple one , but it has to done in all places where you called a static method that way. I think there is no other way.

PHP Calling self on a non-static method

Why is the 'self'-call to a non-satic method in this example working?
class A{
protected function aNonStaticMethod(){
return __class__;
}
public function aEcho(){
echo self::aNonStaticMethod();
}
}
Thanks for explanation.
In your simple example $this and self is interchangable. But be aware of the different method resolving when dealing with inheritance (i added static for completeness):
class A {
protected function aNonStaticMethod(){
return __class__;
}
public function selfEcho(){
echo self::aNonStaticMethod();
}
public function staticEcho(){
echo static::aNonStaticMethod();
}
public function thisEcho(){
echo $this->aNonStaticMethod();
}
}
class B extends A {
protected function aNonStaticMethod(){
return __class__;
}
}
$b = new B();
$b->selfEcho(); // A
$b->staticEcho(); // B
$b->thisEcho(); // B
Calling non-static method statically
Theoretically it should not work, but as this comment says:
There was no static keyword in php4 but php4 did allow for static
calls. To maintain backwards compatibility this was left in when the
static keyword was added in php5.
This comment is supported by this official php.net wiki:
This is already deprecated if the call occurs from an instance method.
Not annotating methods as static is an obsolete PHP4-ism.
You really should not call non-static method statically - it does not make sense (if there is a static keyword).
Avoid calling non-static methods statically completely!
...because a) it is a bad approach and b) the PHP docs say:
Caution
In PHP 5, calling non-static methods statically generates an E_STRICT level warning.
AND
Warning
In PHP 7, calling non-static methods statically is deprecated, and will generate an E_DEPRECATED warning. Support for calling non-static methods statically may be removed in the future.
Using :: operator for non-static calls - may be a good approach!
As #Kontrollfreak pointed out and as this docs say the :: operator is not limited to static calls:
the double colon, is a token that allows access to static, constant,
and overridden properties or methods of a class
So it is OK if you reference this way a method or properties from a parent class - which is not limited to a direct parent.
EDIT: do not mistake this for Fascade etc. software patterns!
During writing this answer I forgot to mention that there might be cases, when the call is static, but internally it is calling dynamic method - for more info see patterns like Facade or Singleton.
However do NOT mistake these with issue described above! (issue above is about using direct static call on dynamic thing that should be called dynamically, these patterns are about calling static methods statically, which then may dynamically invoke something dynamic (internally)).

In PHP, why I am able to access non-static method in a static way?

In the following code, nonStatic() is not a static method. Even then I am able to access it without creating an object (in a static way). Could anyone please help me in understanding as this is not possible in other languages like Java?
<?php
class MyClass
{
function nonStatic() {
echo "This can be printed";
}
}
MyClass::nonStatic(); // This can be printed
It's allowed, but it generates an E_STRICT warning:
Error #: 2048, Error: Non-static method MyClass::nonStatic() should not be called statically, assuming $this from incompatible context
In the earlier OO implementations of PHP this was silently allowed, but better practices have since been adopted.
The opposite works without a hitch though:
class Test
{
function foo()
{
echo $this->bar();
}
static function bar()
{
return "Hello world\n";
}
}
$x = new Test;
$x->foo();
This prints Hello world.
It seems as though the developers of PHP didn't see any value in disallowing static access of non-static methods. This is just one of those idiosyncratic features of PHP that doesn't really serve a purpose. It certainly is bad programming practice to call a non-static method statically, but in PHP it is possible. Maybe in a future version of PHP they will disallow this, but for now, it's just part of the language.
Edit:
Thankfully, the opposite is not allowed - you cannot call a static method from an object context. As Jack pointed out below, you can call a static method from an object context - hardly a best practice in the OOP paradigm, but it's allowed.
Not sure, probably some PHP magic (it's a bit like that sometimes), but you shouldn't do it.
Read more here http://php.net/manual/en/language.oop5.static.php
They also show a similar example, but note:
Calling non-static methods statically generates an E_STRICT level warning meaning this magic ability may disappear in future versions. So don't do it :)

php static methods question

What is the difference between these two pieces of code?
class something {
static function doit() {
echo 'hello world';
}
}
something::doit();
and the same but without the static keyword
class something {
function doit() {
echo 'hello world';
}
}
something::doit();
They both work the same is it better to use the static keywords? Am i right in understanding that it doesn't instantiate the class if you use the static method?
The second example is technically incorrect - if you turn on E_STRICT error reporting you'll see that PHP is actually throwing an error.
PHP Strict Standards: Non-static
method something::doit() should not be
called statically in...
In other words, it's being nice and letting you call the function anyway.
In addition to the other valid answers, the reason for the 2nd example working is also due to a quirk in how PHP handles objects and calls (Besides PHP 4 compatibility). Calling a non-static declared method statically from within another instance will let you access class methods on other classes as if they were local. To understand, let's take an example:
class A {
public function foo() {
echo get_class($this) . "\n";
}
}
class B {
public function bar() {
A::foo();
}
}
$a = new a();
$a->foo(); // "A"
$b = new B();
$b->bar(); // "B"
Did you see what happened there? Because you called the A::foo() method from within another class's instance, PHP treated the call as if it was on the same instance. Note that there is no relationship between A and B other than the fact that B calls A. Within A->foo(), if we did $this instanceof A (or $this instanceof self), it would fail and return false! Quite unusual...
Now, I first thought it was a bug, but after reporting it, it's apparently as designed. It's even in the docs.
Note that this will not work with E_STRICT mode enabled. It also will not work if you declare a method as static.
The difference is that static functions can be used without having to create an instance of the class.
Have a look at this great PHP OOP beginner tutorial here. It explains in more detail with an example under the Static Properties and Methods section.
Second bit shouldn't work as you should call it by
$something = new something();
$something->doit();
Static functions allows you to call a function within a class without consturcting it.
So basically if you have a class to handle users, so a function that logs the user in should be a static function, as in the constructor of that class you will probably gather the user information and you cannot do so without logging him in.
Your second example is wrong. Using a static method does not create an instance of the class. Your second example should look like this:
$x = new something();
$x->doit();
Static methods should be declared static for minimum two reasons:
a) when using E_STRICT error_reporting, calling non static method as static will generate error:
Strict standards: Non-static method something::doit() should not be called statically
b) based on keyword static some IDE's filter method possible to run at auto-complete.

Categories