I'm using both the magic methods _call and _callStatic for my own implementation of something like an ORM/Activerow. They're mainly meant for catching certain function calls: __call is responsible for getters and setters, and __callStatic for findBy methods (e.g. findById).
To map foreign keys, i'm trying to convert calls to e.g. getArticle to return the value of Article::findById(). To do that, i'm using this case inside my __call:
if (strstr($property, "_id")) {
return $foreignClass::findById($this->getId());
}
where $property is the substring after set or get in __call, and $foreignClass the rest of the string. So, in the case of the call getArticle, $property would be get and $foreignClass would be Article.
I've placed some echoes to ensure that the values are correct. However, my __call method gets called instead of my __callStatic. If i make an implicit static method findById, it does get called (so it does recognize it as a static call). If i specifically call Article::findById(), __call also catches it.
Is this an error with the relatively new __callStatic, or am i doing something wrong?
EDIT:
The problem seems to reside in this part:
_call() is triggered when invoking inaccessible methods in an object context.
__callStatic() is triggered when invoking inaccessible methods in a static context.
Though i am calling it on a class, i am calling it from an object context. Is there a way to get into the static context in this case?
Since the code you give runs in the context of an Activity object and since the value of $foreignClas is Article, which is an ancestor of Activity, PHP assumes that you are intending to call an ancestor's implementation of the method.
To break out of the object context there is AFAIK no option other than this absolutely hideous technique:
$id = $this->getById();
return call_user_func(
function() use($foreignClass, $id) {
return call_user_func("$foreignClass::findById", $id);
}
);
The __callStatic magic method was only introduced in PHP 5.3. Prior to that, I believe static calls were routed through __call just like normal method calls. My guess would be that you are using a PHP version that is < 5.3. What is the output of php -v on the command line?
Related
I understand that static methods have no access to state of instance objects of their class types and hence referencing $this inside them results in an error.But objects can reference static methods using object to member operator ->
$obj->staticMethod();
and can even pass it their state via paramaters.
$para1 = $obj->para1;
$para2 = $obj->para2;
$obj->staticMethod($para1, $para2);
How is this last example possible when statics are resolved in static context. If someone can explain to me the general behaviour of statics in php code. you can even talk about C related concepts if it will help.
Since you state that you already understand what static means, I'll skip over that.
However, it may still be good to reference PHP's documentation on the static keyword. In particular the following two alerts are important (and hard to glance over, really).
Caution In PHP 5, calling non-static methods statically generates an E_STRICT level warning.
And this one (italic emphasis mine).
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.
So, to cut a long story short: yes, your example will run (for now), because the PHP interpreter will try to fix up your mistake for you. You should however never do this. What the PHP interpreter will do is:
Say your $obj is of type Foo. Then it will read
$obj->staticMethod($para1, $para2);
conclude that staticMethod is static and instead execute
Foo::staticMethod($para1, $para2);
It is of course perfectly fine to pass parameters that are properties of an instance of Foo. It doesn't matter to staticMethod where the parameters come from.
To elaborate a bit more on why this works, while using $this in a static method is not allowed.
You can think of normal methods as static functions that have one extra: they receive an implicit parameter $this. The value of $this is simply the object on which the method is called. Thus, $obj->do($a, $b, $c) is equivalent to calling Foo::do($obj, $a, $b, $c) and naming the first argument of do, $this. This is convenient, because we can now easily define methods that work on an instance of an object without having to explicitly state over and over again that this instance is a parameter of our methods. Great.
Now back to static functions. The only difference with normal methods is that they do not receive this implicit $this parameter. Thus, using $this inside of them is invalid. Not because it is forbidden, but because it does not reference anything. PHP does not (and cannot) have a clue what $this should refer to.
Another way to look at it. Say that our Foo class has two properties: $para1 and $para2, both numbers. Say that you write a method that returns the sum of these numbers. One way is to do this:
public static function sum($para1, $para2) {
return $para1 + $para2;
}
Great. Works. However, it is annoying to have to call it like this
$sum = Foo::sum($obj->para1, $obj->para2);
So, this is what methods are for!
public function sum(/* implicit $this parameter */) {
// write looking up the properties once inside the function, instead
// of having to write it every time we call the function!
return $this->para1 + $this->para2;
}
// ...
$sum = $obj->sum(); // $obj is passed implicitly as $this
Because static functions do not receive an implicit $this parameter, using $this inside of them is like trying to use $undefined when you have never defined it. Thus, invalid.
Static means class members in simple terms , A static data member is accessible within a class regardless object is created or not . The static function are also functions dedicated to whole class . Static function works with static data only bit it can sometimes vary . Though statics are class dedicated, you can access them using object. It is allowed in all languages. Why ? Because of feasibility . If an object is not being able to access static members , that is a limitation.
I am confused about how "static" and "dynamic" functions and objects in PHP work together especially with regards to __callStatic().
How __callStatic() works:
You can have a normal class MyClass, where within the class you can
put a static function called __callStatic(), which gets called only
when MyClass doesn't have a static function by the name you want.
i.e. I call MyClass::newFunction();
newFunction() is called statically but MyClass does not
have it declared. So, then __callStatic() gets called and
inside you can say
$myObject=new SomeOtherClass();
$myObject->newFunction();
which calls the function you wanted but on some other object.
Short Version:
In other words, __callStatic() does this:
MyClass::newFunction();
which is hiding this:
(new SomeOtherClass())->newFunction();
Say what now? What looks like code calling a static function from a class, turns out to be calling that function from some other class and calling it via instantiation, and not statically.
Explain this, please!
Why was it done? Can you do anything like this elsewhere, like C++ or Java? I am looking for short & concise, but informative explanation on static and dynamic functions in languages, and in this case whether __callStatic() violates or conforms to the big picture of Language constructs. Or is it a new language construct entirely.
__callStatic() provides developers with possibility to react on static method calls even if that methods don't exist or aren't accessible from outside of the class ( being protected). This is useful for dynamic, generic code generation.
Example: You have this class:
class Test {
protected static function myProtected($test) {
var_dump(__METHOD__, $test);
}
public static function __callStatic($method, $args) {
switch($method) {
case 'foo' :
echo 'You have called foo()';
var_dump($args);
break;
case 'helloWorld':
echo 'Hello ' . $args[0];
break;
case 'myProtected':
return call_user_func_array(
array(get_called_class(), 'myProtected'),
$args
);
break;
}
}
}
Try to call:
// these ones does not *really* exist
Test::foo('bar');
Test::helloWorld('hek2mgl');
// this one wouldn't be accessible
Test::myProtected('foo');
Why was it done?
This is an existential question, but I think that the answer is "because you can". PHP is a dynamic language and these constructs __call and __callStatic() show off its power, and this power can be useful in some situations.
Can you do anything like this elsewhere, like C++ or Java?
No. They have no similar magic methods.
I am looking for short & concise, but informative explanation on static and dynamic functions in languages
Static functions encapsulate code. They exist even when no class has been instantiated. You can call them using scope resolution operator.
Dynamic functions are well, dynamic
.. does __callStatic() violate or conform to the big picture of Language constructs. Or is it a new language construct entirely.
It is a construct of a dynamic language. Not sure if this functionality exists in all dynamic languages, but it does in PHP. It does not violate anything, just introduces new paradigm of fall-through catch-all functions when the function you do call does not exist/not accessible in current scope.
I'm not entirely sure why __callStatic() is relevant here?
I don't quite see the difference between:
class Foo {
static function bar() {
$obj = new Foo();
$obj->quux();
}
function quux() {
return "whatever";
}
}
and your example? In both scenarios you're calling a static method which is calling another method on an object of the same class.
Yeah, that's possible. Actually doing it suggests you might want to refactor your code though. In the example you're instantiating an object with its default state, executing a method on it and then throwing the object away. This suggests whatever the method is doing doesn't actually need the objects state. That means it either doesn't belong in this class or could simply be rewritten to be a static method itself.
And are you aware of __call? It does the same thing as __callStatic but for objects rather than classes. E.g. $foo->myMissingMethod(); would go to __call if such a method exists for the class of which $foo is an instance.
I have TestClass and a public method in it.
I am able to call the same method using :: operator like static method and using an object.
What are the advantages or use of static functions in PHP, if we able to call public functions without creating object of the same class?
<?php
class TestClass {
public function testMethod() {
echo 'Method called';
}
}
TestClass::testMethod();
$classObj = new TestClass();
$classObj->testMethod();
?>
In this case, there is no difference.
However, the point of static functions is to say that some functions don't need an instance of the class in order to be executed. It is possible to call functions statically even if they are not marked as static, but it is technically incorrect to do so. If you have error_reporting(E_ALL) set, it will give you a strict standards error.
This is not because the code won't work, but because it might not.
class TestClass {
private $name = 'Rakesh';
public function doSomething() {
echo "Hi there";
}
public function doSomethingElse() {
echo "Hi there " . $this->name;
}
}
You can call the first function statically and it will work fine. But if you call doSomethingElse statically, it won't work, because it tries to access $this, which is only possible if you have an object.
So we apply the static keyword to doSomething to let (a) PHP and (b) the programmer using the class know that it is possible to call it statically. It's a promise that it will work.
The assumption should be that, if it is not marked as static, you shouldn't call it statically.
PHP's strict standards errors are meant to make your code better, even if it already works. The documentation for the E_STRICT constant says:
Enable to have PHP suggest changes to your code which will ensure the best interoperability and forward compatibility of your code.
In low-level terms, a static function in PHP isn't much different than a member function. The only real difference is that $this isn't provided to a static function.
That means the use of the static keyword is mostly of semantic benefit, as it helps you define the architecture and intended behaviour of your classes.
With that said, you shouldn't abuse the semantics. PHP can (optionally) warn you about those kinds of mistakes, and you should always pay attention to such warnings. The language specification is there for a reason, and it's designed to work in certain ways. If you use static (or any other language feature) incorrectly, then it may not always work as you expect. Future PHP updates or configuration changes could break your code unexpectedly.
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).
For compatibility with PHP 4, if no visibility declaration is used, then the property or method will be treated as if it was declared as public.
Advantages are ...
1>Hash memory will not create ,hence no wastage of Memory (no memory leak problem)
2>
// This makes little sense
Math m = new Math();
int answer = m.sin(45);
// This would make more sense
int answer = Math.sin(45);
It's like a shortchut, one feature more of php. But, to access to their properties you must declare them like constants. For example:
<?php
class Math{
const pi=3.1416;
}
echo Math::pi;
?>
I have a child class that extends a class with only static methods. I would like to make this child class a singleton rather than static because the original developer really wanted a singleton but used static instead (obvious because every method in the static class calls the Init() function (basically a constructor)).
Most of the methods in the parent don't need to be overwritten in the child, but I would like to avoid having to write methods like this:
public function Load($id)
{
return parent::Load($id);
}
when I would prefer not to overwrite the method at all and just use:
$child->Load($id);
Is it possible to call a static method non-statically? Is it possible to extend a static object with an instance object? I know I can try it and it will likely work (PHP is very forgiving), but I don't know if there is anything I should be concerned about.
Can you inherit static methods?
Yes
Can you override static methods?
Yes, but only as of PHP 5.3 do they work as you would expect: http://www.php.net/manual/en/language.oop5.static.php (ie. self binds to the actual class and not the class it's defined in).
Is it possible to call a static method non-statically?
Yes, but will lose $this. You don't get a warning (yet) but there also isn't really a reason to call it the wrong way.
Two part answer.
First, about the titular question: calling a static method non-statically is perfectly fine; #SamDark's comment is correct. It does not produce a warning, nor does it cause any kitten murdering. Try it:
<?php
class test {
public static function staticwarnings(){
echo "YOU ARE (statically) WARNED!\n";
}
}
error_reporting(E_ALL);
$test = new test();
echo "\n\ncalling static non-statically\n";
$test->staticwarnings();
If you had an instance reference, $this, in that static method, then you would get a fatal error. But that is true regardless of how you call it.
Once again, there isn't a warning, nor any kitten killed.
Second part of the answer:
Calling an overridden parent function from an overriding child class requires something called "scope resolution". What the OP is doing in their method is NOT calling a static method. (Or at least, it doesn't have to be; we can't see the parent implementation). The point is, using the parent keyword is not a static call. Using the :: operator on an explicit parent class name is also not a static call, if it is used from an extending class.
Why is that documentation link so strangely named? It's literally Hebrew. If you've ever run into an error related to it, you might have observed the delightfully-named parser error code T_PAAMAYIM_NEKUDOTAYIM.
This manages to create a new property on the object. But, can someone explain, with supporting links, why setAttrib behaves in two different ways? Why doesn't it cause a... wait for it... stack overflow!!??
class Test
{
public function setAttrib( $key, $value ) {
echo "setAttrib\n";
// first time: calls $this->__set($key, $value)
// second time: just sets a public property (but, when exactly was it created?)
$this->$key = $value;
}
public function __set( $key, $value ) {
echo "__set\n";
$this->setAttrib($key, $value);
}
}
$test = new Test();
$test->setAttrib('hey', 'It works');
var_dump($test);
produces...
setAttrib
__set
setAttrib
object(Test)#1 (1) {
["hey"]=>
string(8) "It works"
}
Edit: I'm not looking for an alternative. I'm looking for the reason why this works.
You are not the only one who seems to have notice that non-recursive behaviour : this comment on the manual's page states :
2 - PHP will not recursively call one
magic method from within itself (at
least for the same $name).
And, a bit later on the same page, there is this one, which states :
The recursion detection feature can
prove especially perilous when using
__set. When PHP comes across a
statement that would usually call
__set but would lead to recursion,
rather than firing off a warning or
simply not executing the statement it
will act as though there is no __set
method defined at all. The default
behaviour in this instance is to
dynamically add the specified property
to the object thus breaking the
desired functionality of all further
calls to __set or __get for that
property.
And, on PHP's bugtracker, there is #47215 : magic method __set() is bypassed on recursive call, which says :
Magic method __set() is bypassed on
recursive call. PHP automatically
creates a property on instance instead
of recursively calling __set() or
instead of throwing a recursivity
error
And it has been closed as :
Thank you for taking the time to write
to us, but this is not a bug.
That bug-report, itself, points to this blog-post, which ends by this sentence (quoting, emphasis mine) :
After all I think it may not be a bug
but expected behaviour, otherwise we
could not be able to define object
properties from within __set()
method.
__set is only used when writing to inaccessible properties. That is, those who are not accessible (private or protected) or those that aren't set at all. Therefore, __set will only be called once.
Here's what happens:
setAttrib: Attempt to write
class: inaccessible property
__set: Do whatever __set is told to do, which is call setAttrib again.
setAttrib: Attempt to write
class: inaccessible property, but __set can't recurse, and we're already in it, so do it as if __set didn't exist.
See user comments for http://php.net/__set for proof that __set can't recurse.
__set and __get only catch the call for the property if the member is protected. meaning that within the object $this->$key sets the property $key. if called from out of scope then the __set logic is called. One of your calls happens in the object outside the object.