14

I have the following code (like, for real, this is my real code) :

<?php
class Foobar
{
  public static function foo()
  {
    exit('foo');
  }
}

When I run $foobar = new FooBar; $foobar->foo() it displays foo.

Why would PHP try to use a static method in an object context ? Is there a way to avoid this ?


Ok you guys didn't get my problem : I know the differences between static and non static methods and how to call them. That's my whole point, if I call $foobar->foo(), why does PHP tries to run a static method ?


Note : I run PHP 5.4.4, error reporting to E_ALL.

Yevgeniy Afanasyev
  • 37,872
  • 26
  • 173
  • 191
Maxime Fabre
  • 2,252
  • 3
  • 20
  • 24
  • 1
    Possibly relevant: [__call and __callStatic](http://php.net/manual/en/language.oop5.overloading.php#object.callstatic) – Waleed Khan Jan 17 '13 at 15:18
  • 2
    @Daniel It should be called `Foobar::foo`, though. – Waleed Khan Jan 17 '13 at 15:19
  • To clarify : I do _not_ want this static method to be found when I use it, this doesn't make sense to me. – Maxime Fabre Jan 17 '13 at 15:21
  • 1
    The method will always be called statically. There just happens to be two different syntaxes to call it. Try to avoid the syntax that does not make sense to you, and it will save confusion with those that read your code later too. However, it may sometimes make sense to call up static methods on instantiated objects that are injected into another object, since you will not necessarily know the fully qualified name of the class of the object passed in. It is just a convenience in that instance. – Jason Oct 02 '14 at 14:02

3 Answers3

19

To call a static method, you don't use:

$foobar = new FooBar;
$foobar->foo()

You call

FooBar::foo();

The PHP manual says...

Declaring class properties or methods as static makes them accessible without needing an instantiation of the class. A property declared as static can not be accessed with an instantiated class object (though a static method can).

This is why you are able to call the method on an instance, even though that is not what you intended to do.

Whether or not you call a static method statically or on an instance, you cannot access $this in a static method.

http://php.net/manual/en/language.oop5.static.php

You can check to see if you are in a static context, although I would question whether this is overkill...

class Foobar
{
  public static function foo()
  {
    $backtrace = debug_backtrace();
    if ($backtrace[1]['type'] == '::') {
      exit('foo');
    }
  }
}

One additional note - I believe that the method is always executed in a static context, even if it is called on an instance. I'm happy to be corrected on this if I'm wrong though.

Umbrella
  • 4,733
  • 2
  • 22
  • 31
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • 3
    He was probably expecting error when calling static function in object context so his question is why isn't there error, not how to do it. – Valdars Jan 17 '13 at 15:39
  • 2
    `Is there a way to avoid this ?` This is also part of the question, that was *not* answered. – phant0m Jan 17 '13 at 15:40
  • shadyyx - in the spirit of fairness, I edited the answer after the question was edited, so it didn't fully answer the question originally. Valdars - that is in my answer, it is allowed in PHP (and Java if I remember correctly). phant0m I will add that to my answer. – Fenton Jan 17 '13 at 15:58
  • @EM-Creations which bit? Bear in mind the edit history: http://stackoverflow.com/posts/14382206/revisions – Fenton Jan 17 '13 at 16:24
  • @SteveFenton The OP already seems to have grasped the difference between static and non-static and the fact that you "shouldn't" be able to do what they have done. – EM-Creations Jan 17 '13 at 16:25
  • @EM-Creations if you check the edit history the order of events will become clear. – Fenton Jan 17 '13 at 16:33
  • Even when calling the static method using the `$foobar->foo()` syntax, you cannot access `$this` without an error being raised. Whether this is because it makes a static call equivalent to `FooBar::foo()` or there are simply some additional checks in PHP to set a "static context" mode when accessing a static method in *any* way, is not clear. But the point is, you must assume all calls to a static method will be executed as static, with all the restrictions that are imposed on static methods. – Jason Oct 02 '14 at 13:51
9

Since PHP is quite a forgiving language you could prevent this default behavior by overloading __callStatic and maybe use reflections to validate the method scope.

http://php.net/manual/en/language.oop5.overloading.php#object.call

http://php.net/ReflectionClass

Daniel
  • 3,726
  • 4
  • 26
  • 49
2

We may need more information about the FooBar declaration. Obviously, you cannot declare two methods foo() even if one is a static method and the other one an instance method:

class FooBar
{
  public static function foo()
  {
    return 'I am FooBar::foo().';
  }
  public function foo()
  {
    return 'I am FooBar->foo().';
  }
}
// result to Fatal error: Cannot redeclare FooBar::foo()

So, I suppose that you want to reach a magic __call() method, like so:

class FooBar
{
  public $foo = 'I am FooBar->foo().'; 
  // yes we can have a property with the same name than a method

  // this is the static method that we want to avoid
  public static function foo()
  {
    return 'I am FooBar::foo().';
  }

  // this is the method that should be call
  public function __call( $method , $arguments = array() )
  {
    if( isset( $this->$method ) ) // or anything else
    {
      return $this->$method; // or anything else
    }
    else
    {
      // restore fatal error
      trigger_error( sprintf( 'Call to undefined method %s::%s()' , get_class( $this ) , $method ) , E_USER_ERROR );
    }
  }

}

To achieve that, look at this piece of code:

$foobar = new FooBar;

try
{
  // safe way to detect if a method is static
  // @see http://php.net/manual/en/class.reflectionmethod.php
  $rfx = new ReflectionMethod( get_class( $foobar ).'::foo' );
  if( $rfx->isStatic() )
  {
    // the method exists and is static
    // but we do not want to call it directly
    // why not involving the magic public method `__call()`?
    // sounds like a plan...
    echo $foobar->__call( 'foo' );
  }
  else
  {
    // the method exists and is not static
    echo $foobar->foo();
  }
}
catch( ReflectionException $e )
{
  // the method does not exist, let's do some kind of magic
  echo $foobar->foo();
}
P. Evrard
  • 31
  • 1