How to catch any method call on object in PHP? - php

I am trying to figure out how to catch any method called on an object in PHP. I know about the magic function __call, but it is triggered only for methods that do not exist on the called object.
For example i have something like this:
class Foo
{
public function bar()
{
echo 'foobar';
}
public function override($method_name,$method_args)
{
echo 'Calling method ',$method_name,'<br />';
$this->$method_name($method_args); //dirty, but working
}
}
And when i do this:
$foo = new Foo();
$foo->bar();
I want this output:
Calling method bar
foobar
instead of this one:
foobar
Is there any way how to do this? Help please :)

Taking your original Foo implementation you could wrap a decorator around it like this:
class Foo
{
public function bar() {
echo 'foobar';
}
}
class Decorator
{
protected $foo;
public function __construct(Foo $foo) {
$this->foo = $foo;
}
public function __call($method_name, $args) {
echo 'Calling method ',$method_name,'<br />';
return call_user_func_array(array($this->foo, $method_name), $args);
}
}
$foo = new Decorator(new Foo());
$foo->bar();

You can wrap an object around the object, intercepting any calls then forwarding them on the original object and returning the result.
Just store the object as a variable in your wrapper class and use overloading methods in your wrapper class to call/set/get/check on the object.
$object = new AnyObject;
$object = new Wrapper($object);
$object->anyMethod();
$object->anyVar = 'test';
echo $object->anyVar;
echo $object['array form'];
Looping the wrapper class in foreach is probably harder. Havent tried that.

If you set the function to private , call will trap any call to it from the outside will be trapped in __call, but you can call it from the inside
class Foo
{
private function bar()
{
echo 'foobar';
}
public function __call($method_name,$method_args)
{
echo 'Calling method ',$method_name,'<br />';
$this->$method_name(); //dirty, but working
}
}

Related

PHP call parent class method

class Foo {
protected static $a = 1;
protected $b = 2;
public function func() { return 'foo' . static::$a . $this->b; }
}
class Bar extends Foo {
protected static $a = 3;
protected $b = 4;
public function func() { return 'bar' . static::$a . $this->b; }
}
$obj = new Bar();
$obj->func(); // returns of course 'bar34'
Is there any option in PHP to call func() from Foo class?
In C++ I would cast $obj to Foo and simply call func()
Bar* obj = new Bar();
Foo* obj2 = (Bar*) obj;
obj2->func(); // returns 'foo14';
If you want to get down and dirty with Reflection then it's possible, but I'd strongly argue that this shouldn't be used anywhere near any production code. If you've got an instance of a child class, then you've got it for a reason, and if it's overridden a parent method then that has also happened for a reason.
Assuming you already know all this, then with that disclaimer out of the way, this should work in any remotely recent version of PHP:
class Foo { public function func() { echo 'I am the parent'; } }
class Bar extends Foo { public function func() { echo 'I am the child'; } }
// Create instance of child class
$bar = new Bar;
// Create reflection class
$reflected = new ReflectionClass(get_class($bar));
// Get parent method
$method = $reflected->getParentClass()->getMethod('func');
// Invoke method on child object
$method->invokeArgs($bar, []);
// I am the parent
See https://3v4l.org/NP6j8
This to me looks like a design issue more than anything else.
However if I were to handle this in a way that were easily readable and without rethinking my design I would do:
<?php
class Foo {
public function func() { return 'foo'; }
}
class Bar extends Foo {
public function func() { return 'bar'; }
public function parentFunc() { return parent::func(); }
}
$obj = new Bar();
$obj->parentFunc(); // returns of course 'foo'
Loek's answer also works, but doesn't call the method on the objects parent. It just calls the method on the classes parent. It all depends on the functionality you are looking for.
You could also do something like:
<?php
class Foo {
public function func() { return 'foo'; }
}
class Bar extends Foo {
public function func($parent = false) {
if ($parent) {
return parent::func();
}
return 'bar';
}
}
$obj = new Bar();
$obj->func(true); // returns of course 'foo'
Which is similar but without the need for the extra method.
Personally though I feel this issue likely requires a rethink in code design more than a coding solution.
-- edit --
To elaborate on 'a rethink in code design', I would ask myself "Why do I need an object that has two methods with the same name, but different functionalities? Is this not a job for two different objects? Trace the issue backwards until you find the design issue. Or the point at which the decision needs to be made as to which object your framework requires.
This isn't exactly what I'd call pretty, but it works and is relatively similar to what you described for C++; It works by calling get_parent_class() and then abusing PHP's ability to create objects from strings.
<?php
class Foo {
public function func() { echo 'foo'; }
}
class Bar extends Foo {
public function func() { echo 'bar'; }
}
$obj = new Bar();
$obj->func(); // Prints 'bar'
$parentClassString = get_parent_class($obj);
$newObj = new $parentClassString; // Gotta love PHP for magic like this
$newObj->func(); // Prints 'foo'
See this snippet to see it in action.
EDIT
It's a lot of work, but you could use so called Late Static Binding, perhaps more clearly explained in Jokerius's answer here. This requires you to write a crapload of custom code though, which I don't think is preferential. Overall the short answer seems to be: it isn't really possible.
I don't know should it help you but try to add this function in Bar class
public function callParent($function){
return parent::$function();
}
and call
echo $obj->callParent("func");
[UPDATED]
Also you can write cast function yourself
something like this
public function castAs($newClass) {
$obj = new $newClass;
foreach (get_object_vars($this) as $key => $name) {
$obj->$key = $name;
}
return $obj;
}

Nested method in php object

How can I create something like
MyObject->property->method()
in PHP?
I only know how to create a method for a class:
class MyObject
{
public function MyMethod()
{
// do something
}
}
In Javascript I can easily do something like
var MyObject = {
property : {
method : function ()
{
// do something
}
}
}
How do I do that?
In Javascript you can create objects and methods inline, in PHP you need to have a class and instantiate it:
class Foo {
public function method() {}
}
class MyObject {
public $property;
public function __construct() {
$this->property = new Foo;
}
}
$o = new MyObject;
$o->property->method();
You can set an object as the value of a property. Something like this:
class Foo {
public $Bar;
public function __construct() {
$this->Bar = new Bar();
}
}
class Bar {
public function ShowBar() {
echo 'Bar';
}
}
$Foo = new Foo();
$Foor->Bar->ShowBar();
As others have correctly answered, this works differently in PHP and Javascript. And these differences are also the reason why in PHP you need to define the class methods before you run them. It might become a bit more dynamic in the future but I'm sure not on the level of Javascript.
You can however fake this a bit in PHP because you can assign functions to properties dynamically:
$myObject = new PropCall;
$myObject->property->method = function() {
echo "hello world\n";
};
$myObject->property->method();
This example outputs:
hello world
This does work because some little magic has been added in the instantiated object:
class PropCall
{
public function __call($name, $args) {
if (!isset($this->$name)) {
return null; // or error handle
}
return call_user_func_array($this->$name, $args);
}
public function __get($name) {
$this->$name = new PropCall;
return $this->$name;
}
}
This class code checks if a dynamic property has been added with the name of the method called - and then just calls the property as a function.

calling a class function from another class function

I'm new to programming. I have this going on:
I have Class A, which have many functions. One of those functions is functionX.
In functionX I need to make a call to functionY which belongs to another class: Class B.
So how do I acces to functionY from inside functionX?
I use Codeigniter.
Thanks in advance.
Try and experiment with this.
class ClassA {
public function functionX() {
$classB = new ClassB();
echo $classB->functionY();
}
}
class ClassB {
public function functionY() {
return "Stahp, no more OO, stahp!";
}
}
Class function? A static method?
If you have an instance (public) method, you just call $classB->functionY().
If you have a static method, you would call ClassB::functionY();
So:
class ClassA {
public function functionX(){
$classB = new ClassB();
// echo 'foo';
echo $classB->functionY();
// echo 'bar';
echo ClassB::functionYStatic();
}
}
class ClassB {
public $someVar;
public static $someVar2 = 'bar';
function __construct(){
$this->someVar = 'foo';
}
public function functionY(){
return $this->someVar;
}
public static function functionYStatic(){
return self::$someVar2;
}
}
Well that depends. If that function is a static function or not.
First off you must include the file with the class...
include_once('file_with_myclass.php');
If it is static you can call it like this:
ClassName::myFunction()
If it is not, then you create an instance of the class and then call the function on that instance.
$obj = new ClassName();
$obj->myFunction();
As you can guess the function being static means you can call it without the need of creating an instance. That is useful for example if you have a class Math and want to define a function that takes to arguments to calculate the sum of them. It wouldn't really be useful to create an instance of Math to do that, so you can declare as static and use it that way.
Here's a link to the docs with further info
http://www.php.net/manual/en/keyword.class.php
If functionY is static you can call ClassB::functionY(). Else you must create instance of Class B first. Like:
$instance = ClassB;
$instance->functionY();
But maybe you mean something else?
Looks like one of your class has a dependency to another one:
<?php
class A
{
public function x()
{
echo 'hello world';
}
}
class B
{
private $a;
public function __construct(A $a)
{
$this->a = $a;
}
public function y()
{
$this->a->x();
}
}
$a = new A();
$b = new B($a);
$b->y();
Depending how your code looks like, if it makes sense, you can inject class A into y()
public function y(A $a)
{
// your code with $a
}

How to call the __invoke method of a member variable inside a class

PHP 5.4.5, here. I'm trying to invoke an object which is stored as a member of some other object. Like this (very roughly)
class A {
function __invoke () { ... }
}
class B {
private a = new A();
...
$this->a(); <-- runtime error here
}
This produces a runtime error, of course, because there's no method called a. But if I write the call like this:
($this->a)();
then I get a syntax error.
Of course, I can write
$this->a->__invoke();
but that seems intolerably ugly, and rather undermines the point of functors. I was just wondering if there is a better (or official) way.
There's three ways:
Directly calling __invoke, which you already mentioned:
$this->a->__invoke();
By assigning to a variable:
$a = $this->a;
$a();
By using call_user_func:
call_user_func($this->a);
The last one is probably what you are looking for. It has the benefit that it works with any callable.
FYI in PHP 7+ parenthesis around a callback inside an object works now:
class foo {
public function __construct() {
$this -> bar = function() {
echo "bar!" . PHP_EOL;
};
}
public function __invoke() {
echo "invoke!" . PHP_EOL;
}
}
(new foo)();
$f = new foo;
($f -> bar)();
Result:
invoke!
bar!
I know this is a late answer, but use a combination of __call() in the parent and __invoke() in the subclass:
class A {
function __invoke ($msg) {
print $msg;
}
}
class B {
private $a;
public function __construct() { $this->a = new A(); }
function __call($name, $args)
{
if (property_exists($this, $name))
{
$prop = $this->$name;
if (is_callable($prop))
{
return call_user_func_array($prop, $args);
}
}
}
}
Then you should be able to achieve the syntactic sugar you are looking for:
$b = new B();
$b->a("Hello World\n");

Call to undefined function handler

Is it possible to handle this type of errors? Something like spl_autoload_register, but for functions.
Basically, what I am trying to do:
I have the class:
class Foo {
public function bar() {
echo 1;
}
}
So, when I call a nonexistent function Foo() like this:
Foo()->bar();
The possible handler should create a function Foo(), which looks like that:
function Foo() {
return new Foo();
}
If you never need an actual instance of the object, why not use a static class?
class Foo {
public static function bar() {
echo 1;
}
}
Foo::bar();
Then, you can do this in your app:
$this->fiends = FriendsModel::getUserFriends($userId);

Categories