sanmai
sanmai

Reputation: 30881

Detect static function called non-statically

How can one detect that a static function called non-statically?

For example, in this case:

class Foo
{
    public static function bar()
    {
        // How can I tell here that bar() is called on an instance?

        //var_dump(debug_backtrace()[0]['type'] == '::');
        // at all times the above prints bool(true)
        return 1;
    }
}

// later in the code
$foo = new Foo();
$foo::bar(); // that's fine

// even later
$foo->bar(); // this should not happen, yet it's here and there

I want to debug and eradicate cases such as on the last line above: someone somewhere wrongly calls a function on an instance, expecting that it would return something related to the instance in subject; instead one gets a somewhat related constant. Subsequently I need to know when this happens. If hell breaks loose or an exception gets thrown is fine with me too.

So far I've found that non-static calls for static functions are internally translated into static calls, hence debug_backtrace()[0]['type'] tells us nothing (it's :: in both cases).

Upvotes: 2

Views: 379

Answers (2)

sanmai
sanmai

Reputation: 30881

If you don't want to turn off E_STRICT for eternity, there's a way.

If the class Foo is, by sheer luck or providence, instantiated only in a couple of places like so:

public function getFoo()
{
    return new Foo();
}

Then if we subclass it and redefine bar():

class Foo2 extends Foo
{
    public static function bar()
    {
         throw new Exception("You're calling Foo::bar() on an instance");
    }
}

Then if we replace Foo with Foo2 where it were instantiated...

public function getFoo()
{
    //return new Foo();
    return new Foo2();
}

Exception will be thrown upon calling the above new static method:

$q = Foo::bar(); // no error just as before

$foo = $other->getFoo();
$foo->bar(); // throws exception

If anyone ever calls $foo->bar(); on this kind-of-Foo, he would be quickly notified that he's making a mistake.

Upvotes: 0

Adam Plocher
Adam Plocher

Reputation: 14233

The solution is twofold:

  1. First one needs to disable E_STRICT reporting:

    error_reporting(error_reporting() ^ E_STRICT);
    
  2. Next one should remove static keyword from the function's declaration.

    public /* static */ function bar()
    

Now it is possible to see if a function being called statically or dynamically with help of debug_backtrace function:

class Foo
{
    public function bar()
    {
        $calledStatically = debug_backtrace()[0]['type'] == '::';

        if (!$calledStatically) {
            throw new Exception("Should not happen");
        }

        // ...
    }
}

From the documentation:

type (string): The current call type. If a method call, "->" is returned. If a static method call, "::" is returned. If a function call, nothing is returned.

Quick demo.

Foo::bar(); // normal result returned

$foo = new Foo();
$foo->bar(); // throws exception

Upvotes: 2

Related Questions