59

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
Machavity
  • 30,841
  • 27
  • 92
  • 100
NakaBr
  • 795
  • 2
  • 6
  • 9

11 Answers11

41

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.

davidtbernal
  • 13,434
  • 9
  • 44
  • 60
  • 3
    Thanks @notJim, I was debugging this kind of situation for hours. Actually it's pretty crazy this kind of assignment. – Gihan May 14 '14 at 17:58
  • It is not an error when C is a subclass of A ([example](https://3v4l.org/eDnNu)), and can actually be pretty useful in that situation, e.g. to access a method on the parent of the parent. – Tgr Aug 02 '18 at 21:31
26

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
?>
David Titarenco
  • 32,662
  • 13
  • 66
  • 111
18

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

hakre
  • 193,403
  • 52
  • 435
  • 836
16

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.

webbiedave
  • 48,414
  • 8
  • 88
  • 101
9

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

Jake N
  • 10,535
  • 11
  • 66
  • 112
9

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

Malus Jan
  • 1,860
  • 2
  • 22
  • 26
  • if it removes in future we could use __callStatic magic method to support it? am i right? – siddhesh Feb 13 '19 at 16:21
  • 1
    @siddhesh it's better to create an instance of the class and call the non static method. – Malus Jan Feb 13 '19 at 17:58
  • yes i agree with you but singletons like request or logger would like to access with classname rather than creating object and storing it into the container. eg Log::info($message) rather than $container->log->info("this is message") – siddhesh Feb 14 '19 at 12:34
  • 1
    @siddhesh In this case maybe the info function should be static, no? If it's not static as you said__calStatic. – Malus Jan Feb 15 '19 at 13:20
5

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.

yivi
  • 42,438
  • 18
  • 116
  • 138
  • But why?͏͏͏͏͏ – cachius Apr 13 '22 at 10:05
  • 1
    Because it never made sense, @cachius. It lead to confusing code, that could very easily fail. Calling a non-static method statically should fail in any case, because if the method is not static it means in needs access to the instance. And if one calls it statically, there is no instance and the application will crash. And if the method **didn't** need access to the instance, then there was no reason to make it an instance mehtod to begin with. – yivi Apr 13 '22 at 10:12
1

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.

Jacob Relkin
  • 161,348
  • 33
  • 346
  • 320
1

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

brian_d
  • 11,190
  • 5
  • 47
  • 72
0

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

bpile
  • 359
  • 3
  • 10
0

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:

Dan
  • 3,329
  • 2
  • 21
  • 28