Example for PHP _call() function - php

Guys I just came across the __call() PHP function. I tried to understand what this is used through the manual here http://php.net/manual/en/language.oop5.overloading.php#object.call
But all that is mentioned here is that
__call() is triggered when invoking inaccessible methods in an object context.
which is not really clear honestly. I tried looking for other examples online, but they all seem complicated. Can anyone explain using a simple example what is __call() and what is it good for?

Consider this:
class Foo
{
public function __call($name, $args)
{
echo "you tried to call method $name with these args:";
print_r($args);
}
}
$foo = new Foo();
$foo->bar($args);
Note there is no method named bar. Normally, calling it will produce an error. However, in this case, you've defined a __call() method. So, instead of generating an error, PHP will invoke this method, passing it the name of the method you tried to call, and the arguments you tried to call it with.

Related

__call catches static method calls

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?

Using -> with a static function

Hi I'm a bit of a newbie to OOP, i just have a quick question: say I have a function in a class declared as
class House
{
public static function hasAlcohol()
{
// Do Something
}
}
I know i can call this as
House::hasAlcohol()
However, i would also like to know if its okay with coding standards and PHP and if it would be error free to call hasAlcohol() from an instance of house (i tried it and got no errors), for example
$house = new House();
$house->hasAlcohol();
As this has caused several problems for me in the past: Yes, it is valid code. Should you do it? No. It gives the impression that the call is non-static and will most likely cause grief for people working on your code later on. There is no reason to make your code ambiguous.
This used to be possible, but the latest versions of PHP will throw an error, if I remember correctly. You should call static functions statically. You can do $house::hasAlcohol() though.
This used to be possible, but the latest versions of PHP will throw an error, if I remember correctly. You should call static functions statically. You can do $house::hasAlcohol() though.
On a side note, should hasAlcohol really be static? From the name it appears it should be an instance method.
A more recommended pattern if you need constant access to a method is to use a static constructor and get an instance (even if it's a "blank" or "empty") instance to that class. So in the example you've shown, it might be better to have a method like this:
class House
{
public function instance()
{
return new House;
}
public function hasAlcohol()
{
// Do Something
}
}
Then if you ever needed to make a call to "hasAlcohol()" where you don't need an instance for any other purpose, you can do a one-off like so:
House::instance()->hasAlcohol();
or you can instantiate it like in your example:
$house = new House;
$house->hasAlcohol();
or, better yet, use your new factory method:
$house = House::instance();
$house->hasAlcohol();

PHP create_function Instance variable - Unable to call anonymous function: Follow up

This is somewhat a follow up to a previous question - but I've distilled the question down and have the "works" vs. "doesn't work" cases narrowed down much more precisely.
My Goal:
I have a class MyClass that has an instance variable myFunction. Upon creating a MyClass object (instantiating), the constructor assigns the instance variable myFunction with the result of a call to create_function (where the code and args come from a db call).
Once this object of type MyClass is created (and stored as an instance variable of another class elsewhere) I want to be able to call myFunction (the instance variable anonymous function) from "anywhere" that I have the MyClass object.
Experimental Cases -- below is my highly simplified test code to illustrate what works vs. what doesn't (i.e. when the expected functionality breaks)
class MyClass extends AnotherClass {
public $myFunction;
function __construct() {
$functionCode = 'echo "NyanNyanNyan";';
$this->myFunction();
/*Now the following code works as expected if put in here for testing*/
$anonFunc = $this->myFunction;
$anonFunc(); //This call works just fine (echos to page)!
/*And if i make this call, it works too! */
self::TestCallAnon();
}
public function TestCallAnon() {
$anonFunc2 = $this->myFunction;
$anonFunc2();
}
}
However, if I do the following (in another file, it errors saying undefined function () in... within the Apache error log.
//I'm using Yii framework, and this is getting the user
//objects instance variable 'myClass'.
$object = Yii::app()->user->myClass;
$object->TestCallAnon(); // **FAILS**
or
$func = $object->myFunction;
$func(); // ** ALSO FAILS **
In addition, several variations of calls to call_user_func and call_user_func_array don't work.
If anyone is able to offer any insight or help that would be great :).
Thanks in advance!
You can't pass references to functions around in PHP like you can in for instance JavaScript.
call_user_func has limited functionality. You can use it like so:
class MyClass {
function func() {}
static function func() {}
}
function myfunc() {}
$i = new MyClass();
call_user_func("myfunc", $args);
call_user_func(array($i, "func"), $args);
call_user_func(array(MyClass, "staticFunc"), $args);
I ended up solving this issue via a workaround that ended up being a better choice anyways.
In the end I ended up having a static class that had a method to randomly return one of the possible identifiers, and then another method which accepted that identifier to build the anonymous function upon each class.
Slightly less elegant than I would like but it ends up working well.
Thanks to everyone for your efforts.

Dynamically Calling PHP Class Methods

Is there any security problem with dynamically calling a method in a class from user input. For example:
<?php
class A {
public function foo() {
return true;
}
}
$obj = new A();
$method = $_GET['method'];
$obj->$method();
I am aware that the user will be able to call any method within A, and I am fine with that. I am just curious if there may be other possible security issues.
Your user will be able to try calling any possible method from your class -- even try to call non-existant methods (and get a Fatal Error).
If you're fine with this... well, I suppose this is OK.
It doesn't look nice, but I don't think one could inject any other kind of code.
Still, I would at least check if the method exists -- using method_exists()
Yes its probably a bad idea, maybe you should restrict allowed methods. Maybe define allowed methods in an array then throw an exception if $method is not in this whitelist.
Also you will need to use the magic __call($name, $args) method to allow these user defined methods to be called.

PHP Callable Object as Object Member

I have a class Logger which, among other things has a method Log.
As Log is the most common use of the Logger instance, I have wired __invoke to call Log
Another class, "Site" contains a member "Log", an instance of Logger.
Why would this work:
$Log = $this->Log;
$Log("Message");
But not this:
$this->Log("Message");
The former fails with "PHP Fatal error: Call to undefined method Site::Log()"
Is this a limitation of the callable object implementation, or am I misunderstanding something?
Unfortunately, this is (still) a limitation of PHP, but it makes sense when you think about it, as a class can contain properties and methods that share names. For example:
<?php
class Test {
public $log;
public function __construct() {
$this->log = function() {
echo 'In Test::log property';
};
}
public function log() {
echo 'In Test::log() method';
}
}
$test = new Test;
$test->log(); // In Test::log() method
call_user_func($test->log); // In Test::log property
?>
If PHP were to allow the syntax you desire, which function would be invoked? Unfortunately, that only leaves us with call_user_func[_array]() (or copying $this->log to another variable and invoking that).
However, it would be nice if the following syntax was acceptable:
<?php
{$test->log}();
?>
But alas, it is not.
Same reasons you can't do this:
$value = $this->getArray()["key"];
or even this
$value = getArray()["key"];
Because PHP syntax doesn't do short hand very well.
This may work:
${this->Log}("Message");
But perhaps it's just easier and better to use the full call? There doesn't seem to be a way to get what you want to work on the one line.
The error in your question indicates it is looking for a function defined on the class which doesn't exist. An invokable object isn't a function, and it seems it can't be treated as one in this case.
as of php7.4 the following code works for me
($this->Log)("Message");

Categories