Calling non-static method with double-colon(::)

PhpStatic

Php Problem Overview


Why can't I use a method non-static with the syntax of the methods static(class::method) ? Is it some kind of configuration issue?

class Teste {

	public function fun1() {
		echo 'fun1';
	}
	public static function fun2() {
		echo "static fun2" ;
	}
}

Teste::fun1(); // why?
Teste::fun2(); //ok - is a static method

Php Solutions


Solution 1 - Php

PHP is very loose with static vs. non-static methods. One thing I don't see noted here is that if you call a non-static method, ns statically from within a non-static method of class C, $this inside ns will refer to your instance of C.

class A 
{
    public function test()
    {
        echo $this->name;
    }
}

class C 
{
     public function q()
     {
         $this->name = 'hello';
         A::test();
     }
}

$c = new C;
$c->q();// prints hello

This is actually an error of some kind if you have strict error reporting on, but not otherwise.

Solution 2 - Php

This is a known "quirk" of PHP. It's by design to prevent back-propagation for figuring out if some time ago we actually instantiated an object or not (remember, PHP is interpreted, not compiled). However, accessing any non-static member the via scope resolution operator if the object is not instantiated will issue a fatal error.

Courtesy of PHP.net:

class User {
    const GIVEN = 1;  // class constants can't be labeled static nor assigned visibility
    public $a=2;
    public static $b=3;
   
    public function me(){
        echo "print me";
    }
     public static function you() {
        echo "print you";
    }
}

class myUser extends User {
}

// Are properties and methods instantiated to an object of a class, & are they accessible?
//$object1= new User();        // uncomment this line with each of the following lines individually
//echo $object1->GIVEN . "</br>";        // yields nothing
//echo $object1->GIVE . "</br>";        //  deliberately misnamed, still yields nothing
//echo $object1->User::GIVEN . "</br>";    // yields nothing
//echo $object1->a . "</br>";        // yields 2
//echo $object1->b . "</br>";        // yields nothing
//echo $object1->me() . "</br>";        // yields print me
//echo $object1->you() . "</br>";        // yields print you

// Are  properties and methods instantiated to an object of a child class,  & are accessible?
//$object2= new myUser();        // uncomment this line with each of the following lines individually
//echo $object2->GIVEN . "</br>";        // yields nothing
//echo $object2->a . "</br>";        // yields 2
//echo $object2->b . "</br>";        // yields nothing
//echo $object2->me() . "</br>";        // yields print me
//echo $object2->you() . "</br>";        // yields print you

// Are the properties and methods accessible directly in the class?
//echo User::GIVEN . "</br>";        // yields 1
//echo User::$a . "</br>";            // yields fatal error since it is not static
//echo User::$b . "</br>";            // yields 3
//echo User::me() . "</br>";        // yields print me
//echo User::you() . "</br>";        // yields print you

// Are the properties and methods copied to the child class and are they accessible?
//echo myUser::GIVEN . "</br>";        // yields 1
//echo myUser::$a . "</br>";        // yields fatal error since it is not static
//echo myUser::$b . "</br>";        // yields 3
//echo myUser::me() . "</br>";        // yields print me
//echo myUser::you() . "</br>";        // yields print you
?>

Solution 3 - Php

This is PHP 4 backwards compatibility. In PHP 4 you could not differ between an object method and the global function written as a static class method. Therefore both did work.

However with the changes in the object model with PHP 5 - http://php.net/oop5 - the static keyword has been introduced.

And then since PHP 5.1.3 you get proper strict standard warnings about those like:

> Strict Standards: Non-static method Foo::bar() should not be called statically

And/Or:

> Strict Standards: Non-static method Foo::bar() should not be called statically, assuming $this from incompatible context

which you should have enabled for your development setup. So it's merely backwards compatibility to a time where the language couldn't differ enough so this was "defined" at run-time.

Nowadays you can define it already in the code, however the code will not break if you still call it "wrong".

Some Demo to trigger the error messages and to show the changed behavior over different PHP versions: http://3v4l.org/8WRQH

Solution 4 - Php

PHP 4 did not have a static keyword (in function declaration context) but still allowed methods to be called statically with ::. This continued in PHP 5 for backwards compatibility purposes.

Solution 5 - Php

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.

Link

Solution 6 - Php

You can do this, but your code will error if you use $this in the function called fun1()

Solution 7 - Php

Starting on PHP8, this no longer works.

The ability to call non-static methods statically was finally removed in PHP 8.

> The ability to call non-static methods statically has been removed. Thus is_callable() will fail when checking for a non-static method with a classname (must check with an object instance).

It was originally deprecated on PHP 7.

> Static calls to methods that are not declared static are deprecated, and may be removed in the future.

Solution 8 - Php

In most languages you will need to have an instance of the class in order to perform instance methods. It appears that PHP will create a temporary instance when you call an instance method with the scope resolution operator.

Solution 9 - Php

Not sure why PHP allows this, but you do not want to get into the habit of doing it. Your example only works because it does not try to access non-static properties of the class.

Something as simple as:

<?php
class Foo {

    private $color;

    public function bar() {
        echo 'before';
        $this->color = "blue";
        echo 'after';
    }
}

Foo::bar();

would result in a fatal error

Solution 10 - Php

I have noticed that if you call non-static method self::test() from within a class, no warning for strict standard will be issued, like when you call Class::test(). I believe that this is not related to LSB, since my class was not extended (tested on php 5.5)?

Solution 11 - Php

One way for calling the same method both statically and non-statically is using the magic methods __call and __callStatic.

The FluentMath class (code down below) is an example where you can invoke the methods add or subtract both statically and not:

$res1 = FluentMath::add(5)    // add method called statically
    ->add(3)                  // add method called non-statically
    ->subtract(2)
    ->result();

$res2 = FluentMath::subtract(1)->add(10)->result();

FluentMath class

class FluentMath
{
    private $result = 0;

    public function __call($method, $args)
    {
        return $this->call($method, $args);
    }

    public static function __callStatic($method, $args)
    {
        return (new static())->call($method, $args);
    }

    private function call($method, $args)
    {
        if (! method_exists($this , '_' . $method)) {
            throw new Exception('Call undefined method ' . $method);
        }

        return $this->{'_' . $method}(...$args);
    }

    private function _add($num)
    {
        $this->result += $num;

        return $this;
    }

    private function _subtract($num)
    {
        $this->result -= $num;

        return $this;
    }

    public function result()
    {
        return $this->result;
    }
}

If you want the full explanation of how that class works please check:

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNakaBrView Question on Stackoverflow
Solution 1 - PhpdavidtbernalView Answer on Stackoverflow
Solution 2 - PhpDavid TitarencoView Answer on Stackoverflow
Solution 3 - PhphakreView Answer on Stackoverflow
Solution 4 - PhpwebbiedaveView Answer on Stackoverflow
Solution 5 - PhpMalus JanView Answer on Stackoverflow
Solution 6 - PhpJake NView Answer on Stackoverflow
Solution 7 - PhpyiviView Answer on Stackoverflow
Solution 8 - PhpJacob RelkinView Answer on Stackoverflow
Solution 9 - Phpbrian_dView Answer on Stackoverflow
Solution 10 - PhpbpileView Answer on Stackoverflow
Solution 11 - PhpDanView Answer on Stackoverflow