Invoking a non-static method via the scope resolution operator - php

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

Related

How does the non-static method of a class is getting called statically from another different class' method without creating the object of former?

I'm using PHP 7.1.11
Consider below code :
<?php
class A {
function foo() {
if (isset($this)) {
echo '$this is defined (';
echo get_class($this);
echo ")\n";
} else {
echo "\$this is not defined.\n";
}
}
}
class B {
function bar() {
A::foo();
}
}
$a = new A();
$a->foo();
A::foo();
$b = new B();
$b->bar();
B::bar();
?>
Output of above code :
$this is defined (A)
$this is not defined.
$this is not defined.
$this is not defined.
Except the first line in the output the next three lines of output have been generated by calling the non-static method foo() which is present in class A statically(i.e. without creating an object of class A).
Someone please explain me how is this happening?
How does the non-static method from another class is getting called statically from the class/ object of class under consideration(i.e. class B here)?
Thank You.
Note: PHP is very loose with static vs. non-static methods
But: Methods which are not static should NOT be called statically (even if PHP is tolerant). Why?
If a method is not static, this usually means that it is depending on the state of an instance, because otherwise it could be made static.
Sometimes a non-static method is not dependent on an instance and therefore a program still works, because this method could be static. But you never should do this.
Furthermore - if you turn on error reporting, PHP will also tell you this:
$this is defined (A) Deprecated: Non-static method
A::foo() should not be called statically in [...][...] on line
25 $this is not defined. Deprecated:
Non-static method A::foo() should not be called statically in
[...][...] on line 18 $this is not defined.
Deprecated: Non-static method B::bar() should not be called
statically in [...][...] on line 29
Deprecated: Non-static method A::foo() should not be called
statically in [...][...] on line 18 $this is not
defined.
The Deprecated also means: Just because PHP still allows this, it will most probably be removed in future PHP updates.

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

PHP, OOP, Static

I am studying PHP,OOP and i am at Static,
At this php.net/static i didnt understand this sentence
Calling non-static methods statically generates an E_STRICT level warning.
I did understand it's Valid for methods only (not for Properties) by the sentence above,
but i didn't succeed to understand It practically,
I'm glad if anything could please show me code that explains the sentence above,
Wishing you a pleasant week.
class Foo
{
public static $my_static = 'foo';
public $my_non_static = 'bar';
public function staticValue() {
return self::$my_static;
}
public function nonStaticValue() {
return self::$my_non_static;
}
}
print Foo::$my_static . "\n"; // OK
print Foo::staticValue(). "\n"; // E_STRICT
print Foo::$my_non_static . "\n"; // Fatal
print Foo::nonStaticValue(). "\n"; // Fatal
print Foo::$my_static . "\n"; is OK - static property accessed statically.
print Foo::staticValue(). "\n"; gives E_STRICT - non-static method accessed statically, but not Fatal error, because this method doesn't access non-static properties.
Other two give Fatal error because non-static field cannot be accessed statically.
Here is an example of what they mean with the sentence you are asking about.
Consider the following class with one method (it is not static).
class Test
{
function method()
{
echo "Hello from method";
}
}
Test::method(); // attempt to statically call a non-static method
This is the output:
Strict Standards: Non-static method Test::method() should not be
called statically in /obj.php on line 12
Hello from method
As you can see, it did execute the method when called static even though it is not a static method, however a strict error message was displayed.
If the method method() referenced the keyword $this, then you would encounter a fatal error because $this does not exist in the context of a static method call. So while it is technically possible to call a non-static class method statically, it should not be done.
EDIT:
The reason you are even allowed to call a non-static class member statically is because the static keyword did not exist in PHP4 in the context of class methods so if you were designing a static class or method in PHP4, there was no keyword to indicate it, you would simply call it in the static fashion. Now PHP5 emits the warning if the method is called statically but doesn't have the static keyword in the declaration.
It's because even if you can call non-static methods statically, you shouldn't and it will be logged.
class Foo {
function bar(){
print "you should not do that";
}
}
Foo::bar(); would actually works, but you will get a E_STRICT warning because you can do that, but you shouln't.
If a method is non-static, it means that it belongs to an instance of a class. For example, if we have a class Car with a method called getDamage() (which computes how much damaged the car is), then you should not call this method in a static way.
You should only create an instance of the Car class and call getDamage() on that instance. This makes sense because a particular car can be damaged for 25% while another car can be damaged for 70%.
But calling getDamage() in a static way makes no sense: a static method does not belong to a particular instance of the class but to the class itself. And a Car class has no useful way of giving a result for getDamage(). You could still compute a value (perhaps 0) but it does not make sense.

PHP: call to an instance method via ClassName::method syntax, results in a static call?

Her is my code:
class MyClass
{
public $prop;
public function method ()
{
echo $this->prop;
}
}
Then somewhere in the code, accidently:
MyClass::method();
I would expect to have an interpretation error about the above line, because the called method is not static. Instead, the method was called, and I received an exception about $prop not existing. So i understand that the method was called as a static method, even though it's not.
Does it work this way? (Why the hell? )
Calling non-static methods statically generates an E_STRICT level warning.
http://php.net/manual/en/language.oop5.static.php
I suppose you have E_STRICT warnings suppressed. It works (likely for legacy reasons), but it's not recommended.
For legacy reasons, any class method could be called statically even if it wasn't declared static, because you previously couldn't declare them as such. In those cases, $this would simply refer to nothing because it's not an object-context variable.
In PHP 5 you get an E_STRICT warning for calling non-static methods statically (as you just did).

weird php behaviour with classes and static methods

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.

Categories