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
Related
I am writing a basic class with some call_user_func() and then derived class with a method I want to be called from said call_user_func(). It looks as follows:
class Basic
{
private $InputHandler = null;
public function SetNewHandler(callable $NewHandler)
{
$this->InputHandler = $NewHandler;
}
public function ProcessInput()
{
call_user_func(array($this,$this->InputHandler));
}
}
class Specific extends Basic
{
public function Handler()
{
echo "Handler() is called\n";
}
}
$spec = new Specific();
$spec->SetNewHandler('Specific\Handler');
$spec->ProcessInput();
unset($spec);
Obviously it does not work since Specific\Handler is not recognized as a valid callable.
But what is the proper way to achieve this outcome? Parent class should be oblivious to the details of implementation of derived class and\or handler method.
I use php 7.4
I would just create a default handler method in the base class:
class Base
{
public function ProcessInput()
{
$this->Handler();
}
public function Handler()
{
// Some default code or just leave empty
echo "Base Handler() is called\n";
}
}
class Specific extends Base
{
public function Handler()
{
echo "Specific Handler() is called\n";
}
}
$spec = new Specific;
$spec->ProcessInput();
This way you don't need to manually bind the handler you want to use for each instance (which could be cumbersome). If you want a new handler, create a new class with that handler.
Here's a demo
If you made the call with call_user_func(array ($this, $this->InputHandler)); a method name is expected as the 2nd array element.
class Basic
{
private $InputHandler = null;
public function SetNewHandler($NewHandler)
{
$this->InputHandler = $NewHandler;
}
public function ProcessInput()
{
call_user_func(array($this,$this->InputHandler));
}
}
class Specific extends Basic
{
public function Handler()
{
echo "Handler() is called\n";
}
}
$spec = new Specific();
$spec->SetNewHandler('Handler');
$spec->ProcessInput();
Instead of call_user_func(Array($this, $this->InputHandler)); does this work here too:
$method = $this->InputHandler;
$this->$method();
or this:
$this->{$this->InputHandler}();
Alternatively: A callable is passed similarly as described by #Magnus Eriksson in the comment.
class Basic
{
private $InputHandler = null;
public function SetNewHandler(callable $NewHandler)
{
$this->InputHandler = $NewHandler;
}
public function ProcessInput()
{
call_user_func($this->InputHandler);
}
}
class Specific extends Basic
{
public function Handler()
{
echo "Handler() is called\n";
}
}
$spec = new Specific();
$spec->SetNewHandler([$spec,'Handler']);
$spec->ProcessInput();
In PHP, how do I find out if a class method was called via a method inherited from a trait?
Say I have a class myClass that uses the Psr\Log\LoggerTrait (see: PSR-3). I need to be able to find out if the method myClass::log() was called via a method from the Psr\Log\LoggerTrait, for example LoggerTrait::debug(), or if it was called directly from outside myClass.
All the methods are non-static.
This is related to a debugging package. I'm not trying to alter behavior based on the caller, I just need to be able to pass that information forward. And to be more precise, I need just the entry point, ie. just the last call outside of my package.
I'm looking at debug_backtrace() but it doesn't seem to offer any direct solutions. Is there some rational way of doing this?
Here's some code:
<?php
class myClass
{
use Psr\Log\LoggerTrait;
public function log($level, $message, array $context = array())
{
if (called_via_trait) {
...
} else {
...
}
}
}
$myObject = new myClass;
$myObject->log('debug', 'This is a direct call');
$myObject->debug('This is a call via a trait method');
You could use get_called_class to determine the class that called it.
trait Test {
public function doTest() {
echo get_called_class() . "\n";
}
}
class Some {
use Test;
public function myFunc() {
$this->doTest();
}
}
$some = new Some();
$some->myFunc(); // Outputs "Some" since Some uses Test
So in your case, inside your class, you could do something like
function test() {
if(get_called_class() == 'myClass') {
// You're in the myClass class
} else {
// You're not in the myClass class
}
}
Yes, you have to use debug_backtrace(); Please follow my example:
namespace Psr\Log;
class LoggerTrait{
public static function debug(){
return myClass::log();
}
}
class myClass{
public static function log(){
$trace = debug_backtrace();
if(isset($trace[1])){
echo'<br />Called by <b>'.$trace[1]['class'].'</b>. ';
} else {
echo'<br />Called by <b>'.$trace[0]['class'].'</b>. ';
}
if(isset($trace[1]['class']) && $trace[1]['class']!=get_class()){
echo'Called outside';
} else {
echo'Called inside';
}
//return get_class();
}
}
trait ExampleTrait {
public function doSay() {
echo LoggerTrait::debug();
echo myClass::log();
}
}
echo LoggerTrait::debug();
echo myClass::log();
echo ExampleTrait::doSay();
Can I reuse decorators?
I have a ClientDecorator to decorate an entity that has a reference of a client, this decorator gets the client on database on call getClient (before it gets decorated, this method returns the clientId, after being decorated, it returns an instance of Client).
Okay, but, I've some other entities that can be decorated with the same decorator, for example, I have another table named questions, this table has a reference pointing to a client that has asked a question, and I have another table named schedules, that has a reference of a client.
By the way, I can decorate question and schedule with ClientDecorator.
But, I have an QuestionDecorator too; this guy decorates an Answer, etc.
How I can do this abstraction, so I can reuse decorators whenever I want?
I've tried to create ClientDecorable, QuestionDecorable interfaces, but have made no progress.
You can always instance the decorator class passing parameters to the constructor that will tell it how it should behave or what class it should impersonate. You don't really have to declare your decorator as an extension of another class.
PHP classes support magic methods that make it possible to forward calls to the class your object is impersonating, just as if it was extending it with extends.
For instance:
class Client
{
public function getId() { return 123; }
}
class Decorator
{
private $instance = null;
public function __construct($class)
{
$this->instance = new $class();
}
public function __call($method, $params) // magic method
{
return call_user_func_array(array($this->instance, $method), $params);
}
}
$object = Decorator('Client');
echo $object->getId(); // 123
The magic method __call() will be invoked when you try to access a method that doesn't belong to the class Decorator. The same can be done with properties by using the magic methods __get() and __set().
That's a really tricky problem. I could find a solution, but it is kind of McGiver style... Works for PHP 5.4+ (yes, traits).
<?php
interface Decorable
{
public function getTarget();
}
interface ClientDecorable extends Decorable
{
public function getClient();
}
interface LogDecorable extends Decorable
{
public function getLog();
}
abstract class AbstractDecorator implements Decorable
{
private $target;
public function __construct(ClientDecorable $target)
{
$this->target = $target;
}
public function getTarget()
{
// I'll be able to access the leaf node of my decorator single way 'tree'
return $this->target->getTarget();
}
public function __call($method, $args) {
$reflected = new ReflectionClass($this->target);
if ($reflected->hasMethod($method)) {
return call_user_func_array([$this->target, $method], $args);
}
}
}
class ClientDecorator extends AbstractDecorator implements ClientDecorable
{
public function __construct(Decorable $target) {
if (! $target->getTarget() instanceof ClientDecorable) {
throw new Exception('Must be an instance de ClientDecorable');
}
parent::__construct($target);
}
public function getClient()
{
return new Client($this->getTarget()->getClient());
}
}
class LogDecorator extends AbstractDecorator implements LogDecorable
{
public function __construct(Decorable $target) {
if (! $target->getTarget() instanceof LogDecorable) {
throw new Exception('Must be an instance de LogDecorable');
}
parent::__construct($target);
}
public function getLog()
{
return new Log($this->getTarget()->getLog());
}
}
abstract class AbstractTarget implements Decorable
{
// this does the trick
public function getTarget() { return $this; }
}
trait ClientDecorableTrait {
public function getClient()
{
return $this->client;
}
}
trait LogDecorableTrait {
public function getLog()
{
return $this->log;
}
}
class Payment extends AbstractTarget implements ClientDecorable, LogDecorable
{
use ClientDecorableTrait;
use LogDecorableTrait;
private $client = 1;
private $log = 101;
}
class Sale extends AbstractTarget implements ClientDecorable
{
use ClientDecorableTrait;
private $client = 2;
}
class Client
{
// ...
}
class Log
{
// ...
}
$sale = new Sale();
var_dump($sale->getClient());
$saleDec = new ClientDecorator($sale);
var_dump($saleDec->getClient());
$payment = new Payment();
var_dump($payment->getClient());
$paymentDec = new ClientDecorator($payment);
var_dump($paymentDec->getClient());
var_dump($paymentDec->getLog());
$paymentDecTwice = new LogDecorator($paymentDec);
var_dump($paymentDecTwice->getLog());
$saleDecTwice = new LogDecorator($saleDec); // will throw an exception
This is just a skeleton, a real world implementation must be tricky. I think you'd better keep your decorators separated...
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("");
sorry for that weird subject but I don't know how to express it in an other way.
I'm trying to access a method from a calling class. Like in this example:
class normalClass {
public function someMethod() {
[...]
//this method shall access the doSomething method from superClass
}
}
class superClass {
public function __construct() {
$inst = new normalClass;
$inst->someMethod();
}
public function doSomething() {
//this method shall be be accessed by domeMethod form normalClass
}
}
Both classes are not related by inheritance and I don't want to set the function to static.
Is there any way to achieve that?
Thanks for your help!
You can pass a reference to the first object like this:
class normalClass {
protected $superObject;
public function __construct(superClass $obj) {
$this->superObject = $obj;
}
public function someMethod() {
//this method shall access the doSomething method from superClass
$this->superObject->doSomething();
}
}
class superClass {
public function __construct() {
//provide normalClass with a reference to ourself
$inst = new normalClass($this);
$inst->someMethod();
}
public function doSomething() {
//this method shall be be accessed by domeMethod form normalClass
}
}
You could use debug_backtrace() for this. It is a bit iffy but for debugging purposes it is usefull.
class normalClass {
public function someMethod() {
$trace = debug_backtrace();
$trace[1]['object']->doSomething();
}
}
You have a few options. You can use aggregation like so
class normalClass
{
protected $superClass;
public function __construct( superClass $superClass )
{
$this->superClass = $superClass;
}
public function someMethod()
{
$this->superClass->doSomething();
}
}
class superClass
{
public function __construct()
{
$inst = new normalClass( $this );
$inst->someMethod();
}
public function doSomething()
{ //this method shall be be accessed by domeMethod form normalClass
}
}
Or just a straight-up setter
class normalClass
{
protected $superClass;
public function setSuperClass( superClass $superClass )
{
$this->superClass = $superClass;
}
public function someMethod()
{
if ( !isset( $this->superClass ) )
{
throw new Exception( 'you must set a superclass' );
}
$this->superClass->doSomething();
}
}
class superClass
{
public function __construct()
{
$inst = new normalClass();
$inst->setSuperClass( $this );
$inst->someMethod();
}
public function doSomething()
{ //this method shall be be accessed by domeMethod form normalClass
}
}
Depending on your use case, you might want to pass the instance to the function only:
class normalClass {
public function someMethod($object) {
$object->doSomething();
}
}
If normalClass::someMethod() can be called by multiple, distinct $objects, this might be the better choice (instead of providing the $object to the whole normalClass instance).
But regardless of that you might consider creating an Interface to use for type hinting:
interface ISomethingDoer {
public function doSomething();
}
class normalClass {
public function someMethod(ISomethingDoer $object) {
# Now PHP will generate an error if an $object is passed
# to this function which does not implement the above interface.
// ...
class superClass implements ISomethingDoer {
// ...
woah I had the same problem than you but instead of going with the so simple pass the reference to the object, I went with an event manager, Basically, when something would happen in the normal class, it would trigger an event which was listened by a class and that said class(the listener) would call the super class to execute that functionality and if necessary pass it new arguments.
Anyways, whether you pass it as a parameter to your object or you go with an event based approach, both solutions work. Choose the one you prefers.
For more information on events, sympony explains it quite good.
http://symfony.com/doc/current/components/event_dispatcher/introduction.html