Given class A that extends class B, how can I have calls to class A's __call function override the matching function inherited from the parent?
Consider this simplified example:
class A
{
public function method_one()
{
echo "Method one!\n";
}
}
class B extends A
{
public function __call($name, $args)
{
echo "You called $name!\n";
}
}
$b = new B();
$b->method_one();
When I run it, I get the output Method one!. I WANT to get the output You called method_one!.
So, how do I have the subclass's magic method override the parent classes defined method?
I need to extend the object, because I need access to a protected method in A, but I want to channel all public methods into my own __call handler. Is there any way to do this?
So, I found a way to do it, which involves making an intermediate class to expose the protected method I need, while still using __call for the public ones. This works, but I really don't like the idea of extending a class just to expose a protected method... Still, someone might find it useful, so thought I'd share:
class A
{
public function method_one()
{
echo "Method one!\n";
}
protected function protected_method()
{
echo "Accessible!\n";
}
}
class A_accessor extends A
{
public function publicise()
{
$args = func_get_args();
return call_user_func_array(array($this, array_shift($args)), $args);
}
}
class B
{
private $A;
public function __construct()
{
$this->A = new A_accessor();
}
public function __call($name, $args)
{
echo "You called $name!\n";
}
public function protected_method()
{
return $this->A->publicise('protected_method');
}
}
$b = new B();
$b->method_one();
$b->protected_method();
This is the answer I actually used, based on Mark Baker's comment.
By having an object of the class whose methods I want access to as a variable, I can use ReflectionMethod to access any of its methods just as if I was extending it, but with __call still catching everything else. So any methods I want to pass through, I can pass through with something like this:
public function __call($name, $args)
{
$method = new ReflectionMethod($this->A, $name);
$method->setAccessible(true);
return $method->invokeArgs($this->A, $args);
}
Or in my case, with the full class like this:
class B
{
private $A;
public function __construct()
{
$this->A = new A();
}
public function __call($name, $args)
{
echo "You called $name!\n";
}
public function protected_method()
{
$method = new ReflectionMethod($this->A, 'protected_method');
$method->setAccessible(true);
return $method->invoke($this->A, $args);
}
}
try this
class A1
{
protected function method_one()
{
echo "Method one!\n";
}
}
class B1
{
private $A;
public function __construct()
{
$this->A = new A1;
}
public function __call($name, $args)
{
$class = new ReflectionClass($this->A);
$method = $class->getMethod($name);
$method->setAccessible(true);
//return $method;
echo "You called $name!\n";
$Output=$method->invokeArgs($this->A, $args);
$method->setAccessible(false);
return $Output;
}
}
$a = new B1;
$a->method_one("");
Related
I have a problem, I cannot call a function before all functions.
I have a parent class - that is a repository.
class Repository {
public function find(/*..*/){/*..*/}
public function findAll(/*..*/){/*..*/}
public function findBy(/*..*/){/*..*/}
public function findA(/*..*/){/*..*/}
public function findB(/*..*/){/*..*/}
/* then 100+ more public function */
}
And I want to create an "Adapter class" that will be calling a function that run before all only public functions BUT I don't want "overwriting" all functions of the parent.
I tried this solution:
class OwnRepo extends Repository{
public __call($methods, $args){/**/}
}
BUT the __call method not working with public methods
How can I solve this problem?
Thank you!
** UPDATE **
Sorry I was not clear!
My controller implements a model, I don't want to change/rewrite the functions of the controllers.
class IndexController {
public function index(){
$model = new Model(); // will return a OwnRepo object
$a = model->findAll();
}
}
In my opinion you can't do this with inheritance. You have to check if the desired method exists in the Repository class and then call it.
Try this one:
class Repository
{
public function find($a,$b,$c)
{
return "find $a $b $c";
}
}
class OwnRepo
{
function __call($name, $arguments)
{
if (method_exists('Repository', $name)) {
//some action before
$repo = new Repository();
$result = call_user_func_array(array($repo, $name), $arguments);
//some action afterwards
return $result;
} else {
die("Method " . $name . " does not exist");
}
}
}
$o = new OwnRepo();
echo $o->find(1,2,3);
The magical __call() and __callStatic can pretty much handle any non existing method on the class, but is there a way to handle a non existing magical method on a class?!
Here's an example on why I need this:
I have a class called DoSomething:
class DoSomething{
public function ok(){
echo 'Something!';
}
}
I want to call this class as a function for a reason! which should call the __invoke function of that class:
$doSomething = new DoSomething();
$doSomething();
Normally by doing that, the class should look for the __invoke function, however in my case I don't to have that function declared on my class (DoSomething), instead I want to be able to call another function (such as the ok()) if the __invoke doesn't exist.
I was expecting something like this to work, but of course it didn't :)
public function __call($class, $arguments)
{
$object = IoC::resolve($class);
$object->ok(...$arguments);
}
The main goal is to use the class as a function, without having to declare the __invoke method. Handle the function does not exist error and call another function instead.
I think that would be really cool :D I appreciate suggestions or other solutions to achieve this.
Internal solution
Extract an abstract class
You could extract an abstract class and have your classes extend it:
<?php
abstract class Invokable
{
public function __invoke()
{
return $this->ok();
}
abstract public function ok();
}
class DoSomething extends Invokable
{
public function ok()
{
echo 'Something';
}
}
$doSomething = new DoSomething();
echo $doSomething();
For an example, see:
https://3v4l.org/m0ih8
Extract a trait
You could extract a trait and have your classes use it:
<?php
trait InvokableTrait
{
public function __invoke()
{
return $this->ok();
}
}
class DoSomething
{
use InvokableTrait;
public function ok()
{
echo 'Something';
}
}
$doSomething = new DoSomething();
echo $doSomething();
For an example, see:
https://3v4l.org/ftUfI
External Solution
Create a proxy
You could create a proxy (a decorator) that composes the object that is not invokable:
<?php
class InvokableDecorator
{
private $decorated;
public function __construct($decorated)
{
$this->decorated = $decorated;
}
public function __call($name, $arguments)
{
/**
* delegate to decorated object if the method exists
*/
if (method_exists($this->decorated, $name)) {
return $this->decorated->{$name}($arguments);
}
}
public function __invoke()
{
return $this->decorated->ok();
}
}
class DoSomething
{
public function ok()
{
echo 'Something';
}
}
$doSomething = new InvokableDecorator(new DoSomething());
echo $doSomething();
For an example, see:
https://3v4l.org/C3XEX
Create a handler
You could create a handler that takes care of determining this externally:
<?php
class Handler
{
public function handle($subject)
{
if (is_callable($subject)) {
return $subject();
}
if (method_exists($subject, 'ok')) {
return $subject->ok();
}
throw new \BadMethodCallException(sprintf(
'Unable to handle instance of "%s"',
get_class($subject)
));
}
}
class DoSomething
{
public function ok()
{
echo 'Something';
}
}
$handler = new Handler();
echo $handler->handle(new DoSomething());
For an example, see:
https://3v4l.org/E0NVs
So I have a class which is designed to "mix" other classes in, via what i call a "bridge" class. So you have your sample classes for example:
class A{
public function __construct(){}
public function hello_a(){ echo "hello A"; }
}
class B{
public function __construct(){}
public function hello_b(){ echo "hello B"; }
}
You might also have a single class called C - which needs to inherit from both A and B, but since PHP doesn't have multiple inheritance we have the following:
class C extends Bridge{
public function __construct(){
parent::__construct();
}
public function hello_C(){
$this->hello_a(); // Freaks out*
}
}
class Bridge extends AisisCore_Loader_Mixins{
public function construct(){
parent::construct();
$this->setup(array(
'A' => array(),
'B' => array()
));
}
}
And now finally we have our mix-in class which allows all of this to work. Note: this code assumes you have a auto loader using the pear naming standards to load classes for you.
class AisisCore_Loader_Mixins {
private $_classes;
private $_class_objects = array();
private $_methods = array();
public function __construct(){
$this->init();
}
public function init(){}
public function setup($class){
if(!is_array($class)){
throw new AisisCore_Loader_LoaderException('Object passed in must be of type $class_name=>$params.');
}
$this->_classes = $class;
$this->get_class_objects();
$this->get_methods();
}
public function get_class_objects(){
foreach($this->_classes as $class_name=>$params){
$object = new ReflectionClass($class_name);
$this->_class_objects[] = $object->newInstanceArgs($params);
}
}
public function get_methods(){
foreach($this->_class_objects as $class_object){
$this->_methods[] = get_class_methods($class_object);
}
return $this->_methods;
}
public function __call($name, $param = null){
foreach($this->_methods as $key=>$methods){
foreach($methods as $method){
if($name === $method){
return $this->isParam($method, $param);
}
}
}
throw new AisisCore_Loader_LoaderException("Method: " .$name.
" does not exist or it's access is not public");
}
private function isParam($method, $param){
if($param != null){
call_user_func($method, $param);
}else{
call_user_func($method);
}
}
}
You can see in class C how the class above is used, we simply just call hello_a. All is well up to this point until it tries call_user_func() and freaks out saying:
Warning: call_user_func() expects parameter 1 to be a valid callback, function 'hello_a' not found or invalid function name
Is there a particular reason it cannot find this? the classes are loaded, the methods are stored in the array, it obviously found the method in the array of methods, the method is public. whats going on?
Your call to call_user_func is passing just the method name, so it's looking for a global function. You must pass your class name or instance:
$a = new A(); // Or however you plan to get your instance of A
call_user_func(array($a, $method));
Is it possible in PHP 5.X to have a method in a class that is executed whenever a classes method is called, prior to the called function? I need this because i want to do some dynamic validation for the parameters used in the called function.
class MyClass {
protected function preHook(){ echo "You are about to call a method."; }
public function methodA() { echo "You called methodA."; }
}
$obj = new MyClass();
$obj->methodA();
// Output: "You called methodA."
// Desired output: "You are about to call a method.You called methodA"
Also keep in mind the following:
The "methodA" needs to be public because reflection is used in the code in order to detect the method.
As mentioned in comment this is impossible as __call magic method gets called only if method with given name is not defined:
http://php.net/manual/en/language.oop5.magic.php
However, maybe one of the following hackish solutions will solve your issue.
SOLUTION 1
will require changing all your methods names:
class MyClass {
public function __call($name, $arguments){
echo "You are about to call $name method.";
return call_user_func_array(array($this, '_real_' . $name), $arguments);
}
private function _real_methodA() { echo "You called methodA."; }
}
$obj = new MyClass();
$obj->methodA();
SOLUTION 2
this will require a 'wrapper' classes:
class MyClass {
public function methodA() { echo "You called methodA."; }
}
class MyClassWrapper {
public function __construct(){
$this->myClass = new MyClass();
}
public function __call($name, $arguments){
echo "You are about to call $name method.";
return call_user_func_array(array($this->myClass, $name), $arguments);
}
}
$obj = new MyClassWrapper();
$obj->methodA();
SOLUTION 3
Third approach would be to apply decorator pattern and create one wrapper class.
class Decorator
{
protected $_instance;
public function __construct($instance)
{
$this->_instance = $instance;
}
public function __call($method, $args)
{
print 'do your stuff here';
return call_user_func_array(array($this->_instance, $method), $args);
}
}
$obj = new Decorator(new MyClass);
$obj->methodA();
SOLUTION 4
mix of solution 1 and use reflection and "runkit_method_rename" to rename all methods
http://docs.php.net/manual/en/function.runkit-method-rename.php
runkit is experimental so this is rather hardcore.
class MyClass {
public function __call($name, $arguments){
echo "You are about to call $name method.";
return call_user_func_array(array($this, '_real_' . $name), $arguments);
}
private function methodA() { echo "You called methodA."; }
}
$reflection = new ReflectionClass('MyClass');
$methods = $reflection->getMethods();
foreach ($methods as $method) {
runkit_method_rename('MyClass', $method->name , '_real_' . $method->name);
}
$obj = new MyClass();
$obj->methodA();
In the end i modified the dispatcher to call the prehook (using __call in order to keep it hidden from reflection but still be public) before the actual method is called.
From php manual:
[...] Static method calls are resolved at compile time.
When using an explicit class name the method is already identified completely and no
inheritance rules apply. If the call is done by self then self is translated to
the current class, that is the class the code belongs to.
Here also no inheritance rules apply [...]
..so im looking for a way to emulate the standard oop inheritance with static singleton.
Code explain better:
// Normal inheritance: my goal.
class Foo{
public function test(){
echo "Foo->test()\n";
}
}
class Bar extends Foo{
public function other_test()
{
echo "Bar->other_test()\n";
}
}
$obj = new Bar();
echo get_class($obj) . "\n";
$obj->test();
$obj->other_test();
/*
Output:
Bar
Foo->test()
Bar->other_test()
*/
// How i would love to do:
class Foo2{
public static function test2()
{
echo "Foo2::test2()\n";
}
// Singleton?
public static $_instance;
public static function get_instance()
{
if(is_null(self::$_instance))
{
self::$_instance = new self();
}
return self::$_instance;
}
}
class Bar2 extends Foo2{
public static function other_test2()
{
echo "Bar2::other_test2()\n";
}
}
$obj2 = Bar2::get_instance();
echo get_class($obj2) . "\n";
$obj2::test2();
$obj2::other_test2();
/*
Output:
Foo2
Foo2::test2()
Fatal error: Call to undefined method Foo2::other_test2()
*/
echo "\n-------\n";
// How im doing actually:
interface Foo3{
public static function get_instance();
}
class Bar3 implements Foo3{
// Singleton?
public static $_instance;
public static function get_instance()
{
if(is_null(self::$_instance))
{
self::$_instance = new self();
}
return self::$_instance;
}
public static function test3()
{
echo "Bar3::test3()\n";
}
public static function other_test3()
{
echo "Bar3::other_test3()\n";
}
}
$obj3 = Bar3::get_instance();
echo get_class($obj3) . "\n";
$obj3::test3();
$obj3::other_test3();
/*
Output:
Bar3
Foo3::test3()
Bar3::other_test3()
*/
The last 'way' force me to avoid the get_instance and static variables to be placed in the parent class, so I do not consider it as a best solution.. if for some reason my get_instance() function will change in the future, i dont want to edit all classes (inheritance! inheritance! we all want inheritance!)
So, is there a way or a best practices to solve this problem?
p.s: php5.3.2
The Singleton pattern in PHP is something like this:
class Singleton {
private static $instance = null;
// Constructor is private, so class cannot be instantiazed from outside
private function __construct() {
}
public static function getInstance() {
if (static::$instance === null) {
static::$instance = new Singleton();
}
return static::$instance;
}
public static function test() {
echo 'Singleton::test()';
}
public function __sleep() {
throw new Exception('Serialization is not alowed.');
}
public function __wakeup() {
throw new Exception('Serialization is not alowed.');
}
public function __clone() {
throw new Exception('Cloning is not alowed.');
}
}
For you is important that keyword static, then this:
class B extends Singleton {
public static function test2() {
echo 'B::test2()';
}
}
$b = B::getInstance();
B::test();
B::test2();
// Singleton::test()
// B::test()
Is this you looking for?