I need a class that would let the user take advantage of both normal (non-static) methods and static methods. The actual implementation would reside in the static methods, which would be called by virtual non-static methods of the same name via the magic __call method. The problem however is that, when calling a non-static method in this way, PHP thinks that the programmer made a mistake and the static method was actually meant to be called:
class AClass
{
public static function method ()
{
// implementation
}
public function __call ($name, $arguments)
{
var_dump("Hello");
call_user_func_array("AClass::$name", $arguments);
}
}
$obj = new AClass;
$obj->method();
So "Hello" is no being output when invoking method method on the object but is being output if trying to invoke a method with any other name. Any way to achieve the correct behavior while preserving method naming?
I don't really see why you would try to do that but this seems to work :
class AClass
{
protected static function method ()
{
echo "method()\n";
}
public function __call ($name, $arguments)
{
echo("Hello normal\n");
call_user_func_array("self::$name", $arguments);
}
public static function __callStatic($name, $arguments)
{
echo("Hello static\n");
call_user_func_array("self::$name", $arguments);
}
}
$obj = new AClass;
// Normal call
$obj->method();
// Static call
AClass::method();
Output :
Hello normal
method()
Hello static
method()
The thing is, accordingly to its documentation, the __call method is only used if a function is inaccessible from the context you called it from.
So since in your case the function was public, there was no need to use it (even if with some settings it displays a warning).
If you make it "protected" or "private", it can't be called from the outside and then __call is used.
Because __call doesn't care about static calls, you also have to use __callStatic (available in PHP >=5.3) if you still need to call AClass::method from outside your class.
Related
class Test
{
private $flag;
public function __construct($flag)
{
$this->flag = $flag;
}
public function a()
{
if ($this->flag)
{
$this->b();
}
}
public function b()
{
$this->c();
}
public function c()
{
}
}
how an external method would like to use b() but he cant do it without creating Test. But Test requires a parameter, even though it wont be used. b() also cant be static.
Answer to original question:
Yep, the best way to access the method of a class without instantiating it (instantiation requires parameters) is to make the desired method static. Make sure that it makes sense to use the method even if the object has not been constructed yet. Note that you cannot refer to non-static content, e.g. non-static methods, within a static context e.g. a static method.
Edit (question now specifies b() cannot be static):
If b() cannot be static then it must be referring to a non-static member e.g. a non-static function or variable. In other words, it must be referring to an instance variable that requires the instantiation of the object. It is not possible to call a non-static method of a class without instantiating the class. I would recommend you analyze the class structure and determine why b() must be non-static (trace any functions or variables it refers to).
Looking at the body of b(), it calls the non-static method c(). Analyze the body of c() and find out why c() must be non-static. If you cannot find any references to an instance of the object, you can safely make c() static and therefore make b() static. On the other hand, if c() requires an instance then b() will also require an instance meaning it is not possible to make b() static.
If you need to create the class without specifying the flag, then you can default the value:
public function __construct($flag = null)
{
$this->flag = $flag;
}
You can use other simple values instead of null. If you need to default it to something other than a simple value (i.e. you need to call another function), then you can check to see if $flag is null or not null. Then you can use the other functions as necessary.
I have this method that I want to use $this in but all I get is: Fatal error: Using $this when not in object context.
How can I get this to work?
public static function userNameAvailibility()
{
$result = $this->getsomthin();
}
This is the correct way
public static function userNameAvailibility()
{
$result = self::getsomthin();
}
Use self:: instead of $this-> for static methods.
See: PHP Static Methods Tutorial for more info :)
You can't use $this inside a static function, because static functions are independent of any instantiated object.
Try making the function not static.
Edit:
By definition, static methods can be called without any instantiated object, and thus there is no meaningful use of $this inside a static method.
Only static functions can be called within the static function using self:: if your class contains non static function which you want to use then you can declare the instance of the same class and use it.
<?php
class some_class{
function nonStatic() {
//..... Some code ....
}
Static function isStatic(){
$someClassObject = new some_class;
$someClassObject->nonStatic();
}
}
?>
The accessor this refers to the current instance of the class. As static methods does not run off the instance, using this is barred. So one need to call the method directly here. The static method can not access anything in the scope of the instance, but access everything in the class scope outside instance scope.
It's a pity PHP doesn't show a descriptive enough error. You can not use $this-> inside a static function, but rather use self:: if you have to call a function inside the same class
In the static method,properties are for the class, not the object.
This is why access to static methods or features is possible without creating an object.
$this refers to an object made of a class, but $self only refers to the same class.
Here is an example of what happens when a method of a class is called in a wrong way. You will see some warnings when execute this code but it will work and will print: "I'm A: printing B property". (Executed in php5.6)
class A {
public function aMethod() {
echo "I'm A: ";
echo "printing " . $this->property;
}
}
class B {
public $property = "B property";
public function bMethod() {
A::aMethod();
}
}
$b = new B();
$b->bMethod();
It seams that the variable $this, used in a method which is called as a static method, points to the instance of the "caller" class. In the example above there is $this->property used in the A class which points to a property of the B.
EDIT:
The pseudo-variable $this is available when a method is called from within an object context. $this is a reference to the calling object (usually the object to which the method belongs, but possibly another object, if the method is called statically from the context of a secondary object).
PHP > The Basics
SO question Weird behaviour with triggering __callStatic() from non-static method is great because it explains the weird behaviour with the __callStatic not being called from within the class itself (Note that I don't see this behaviour in 5.3.3 but in 5.3.8 and 5.3.12). It seems that the __callStatic can only be invoked from outside the class. That's now a fact. But what do I do if I really want the __callStatic to be called from within my class? What syntax should I use to get over the issue?
It does not have to be from outside the class, just not from object context (i.e. where $this is an instance of the class). So you can wrap this call in a static method, for example:
class TestCallStatic
{
public function __call($func, $args)
{
echo "__call($func)";
}
public static function __callStatic($func, $args)
{
echo "__callStatic($func)";
}
public function test()
{
self::_test();
}
protected static function _test()
{
self::i_am_static();
}
}
$test = new TestCallStatic();
$test->test();
Output:
__callStatic(i_am_static)
You could abstract the functionality to another method like Class::magicCall($method, $args) and call that from within __callStatic(). That way you can also access that functionality by simply calling Class::magicCall() directly.
I know the following could potentially create problems elsewhere and is probably bad design, but I still would like to know why this fails (for my own edification):
class Test {
// singleton
private function __construct(){}
private static $i;
public static function instance(){
if(!self::$i){
self::$i = new Test();
}
return self::$i;
}
// pass static requests to the instance
public static function __callStatic($method, $parameters){
return call_user_func_array(array(self::instance(), $method), $parameters);
}
private $someVar = 1;
public function getSomeVar(){
return $this->someVar;
}
}
print Test::getSomeVar();
The error is Using $this when not in object context
Obviously $this is unavailable from a static method, but the static method is handing it off to an instance method invokation via call_user_func_array, which should make $this the instance...
/EDIT
I'm aware that $this is not available in static context. However, this works:
print call_user_func_array(array(Test::instance(), 'getSomeVar'), array());
Which is exactly what's happening in the __callStatic overload, so something's amiss...
/EDIT 2
scope is definitely getting handled strangely. if you pull the singleton instance to any other class, it works as expected:
class Test {
// singleton
private function __construct(){}
private static $i;
public static function instance(){
if(!self::$i){
self::$i = new Blah();
}
return self::$i;
}
// pass static requests to the instance
public static function __callStatic($method, $parameters){
return call_user_func_array(array(static::instance(), $method), $parameters);
}
}
class Blah {
private $someVar = 1;
public function getSomeVar(){
return $this->someVar;
}
}
print Test::getSomeVar();
You can not staticfy your object methods through __callStatic calls in PHP. It will only be invoked when the method so far does not exist (incl. not being visible from the calling context). In your case Test::getSomeVar() is already defined.
Because of backwards compatibility, PHP does not check if only a static method exists, but actually if generally a method exists.
In your case you are calling the non-static method statically, so $this is not defined because __callStatic has not been invoked. If you would have enabled warnings and notices to the highest level (recommended for development), PHP would have warned you about that.
The correct usage therefore is:
echo Test::instance()->getSomeVar();
As with any other implementation of a Singleton.
So you are just using __callStatic wrong, it only works for methods not yet defined. Choose another tool/design for the job, like the aggregation example you use with your Blah class.
A further note:
You should normally prevent to use any static context in PHP anyway, especially as you exploit a lot of magic functionality here, too, which is another smell. It looks like you have a pile of design issues before you even finished your code. You will only increase the likelihood to run into hard to debug and maintain code with all this. But that just so you do not say in some time you have not been warned.
See Who needs singletons? in case you want to learn more.
It doesn't work like that. In a static call you have no instance, no object and no $this. Call static only works with static methods in which $this is unavailable.
The documentation says:
__callStatic() is triggered when invoking inaccessible methods in a static context.
I know this is not the answer you were hoping for. As you have anticipated: you don't need of overloading in PHP, just create your app and leave this thing out.
I have this method that I want to use $this in but all I get is: Fatal error: Using $this when not in object context.
How can I get this to work?
public static function userNameAvailibility()
{
$result = $this->getsomthin();
}
This is the correct way
public static function userNameAvailibility()
{
$result = self::getsomthin();
}
Use self:: instead of $this-> for static methods.
See: PHP Static Methods Tutorial for more info :)
You can't use $this inside a static function, because static functions are independent of any instantiated object.
Try making the function not static.
Edit:
By definition, static methods can be called without any instantiated object, and thus there is no meaningful use of $this inside a static method.
Only static functions can be called within the static function using self:: if your class contains non static function which you want to use then you can declare the instance of the same class and use it.
<?php
class some_class{
function nonStatic() {
//..... Some code ....
}
Static function isStatic(){
$someClassObject = new some_class;
$someClassObject->nonStatic();
}
}
?>
The accessor this refers to the current instance of the class. As static methods does not run off the instance, using this is barred. So one need to call the method directly here. The static method can not access anything in the scope of the instance, but access everything in the class scope outside instance scope.
It's a pity PHP doesn't show a descriptive enough error. You can not use $this-> inside a static function, but rather use self:: if you have to call a function inside the same class
In the static method,properties are for the class, not the object.
This is why access to static methods or features is possible without creating an object.
$this refers to an object made of a class, but $self only refers to the same class.
Here is an example of what happens when a method of a class is called in a wrong way. You will see some warnings when execute this code but it will work and will print: "I'm A: printing B property". (Executed in php5.6)
class A {
public function aMethod() {
echo "I'm A: ";
echo "printing " . $this->property;
}
}
class B {
public $property = "B property";
public function bMethod() {
A::aMethod();
}
}
$b = new B();
$b->bMethod();
It seams that the variable $this, used in a method which is called as a static method, points to the instance of the "caller" class. In the example above there is $this->property used in the A class which points to a property of the B.
EDIT:
The pseudo-variable $this is available when a method is called from within an object context. $this is a reference to the calling object (usually the object to which the method belongs, but possibly another object, if the method is called statically from the context of a secondary object).
PHP > The Basics