I'm using WordPress as a CMS, and I want to extend one of its classes without having to inherit from another class; i.e. I simply want to "add" more methods to that class:
class A {
function do_a() {
echo 'a';
}
}
then:
function insert_this_function_into_class_A() {
echo 'b';
}
(some way of inserting the latter into A class)
and:
A::insert_this_function_into_class_A(); # b
Is this even possible in tenacious PHP?
If you only need to access the Public API of the class, you can use a Decorator:
class SomeClassDecorator
{
protected $_instance;
public function myMethod() {
return strtoupper( $this->_instance->someMethod() );
}
public function __construct(SomeClass $instance) {
$this->_instance = $instance;
}
public function __call($method, $args) {
return call_user_func_array(array($this->_instance, $method), $args);
}
public function __get($key) {
return $this->_instance->$key;
}
public function __set($key, $val) {
return $this->_instance->$key = $val;
}
// can implement additional (magic) methods here ...
}
Then wrap the instance of SomeClass:
$decorator = new SomeClassDecorator(new SomeClass);
$decorator->foo = 'bar'; // sets $foo in SomeClass instance
echo $decorator->foo; // returns 'bar'
echo $decorator->someMethod(); // forwards call to SomeClass instance
echo $decorator->myMethod(); // calls my custom methods in Decorator
If you need to have access to the protected API, you have to use inheritance. If you need to access the private API, you have to modify the class files. While the inheritance approach is fine, modifiying the class files might get you into trouble when updating (you will lose any patches made). But both is more feasible than using runkit.
An updated way for 2014 that copes with scope.
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
Eg:
class stdObject {
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
}
$obj = new stdObject();
$obj->test = function() {
echo "<pre>" . print_r($this, true) . "</pre>";
};
$obj->test();
You can use the runkit extension for this, but you should really consider regular inheritance instead.
See runkit_method_add.
No you can't dynamically change a class during runtime in PHP.
You can accomplish this by either extending the class using regular inheritance:
class Fancy extends NotSoFancy
{
public function whatMakesItFancy() //can also be private/protected of course
{
//
}
}
Or you could edit the Wordpress source files.
I'd prefer the inheritance way. It's a lot easier to handle in the long run.
Related
The object is to make the name of the function shorter.
I have a function called somedescriptivefunctionName.
function somedescriptivefunctionName($a) {
echo $a;
}
I want to assign it to $this->f so that I do not litter the code with long names.
$this->f('Yahoo!!'); // will echo 'Yahoo!!'
e.g. I imagine something like this:
$this->f = $this->somedescriptivefunctionName; // wrong way
Is this possible?
Of course I could make a new function f, and return somedescriptivefunctionName(). But this is not the point.
You will store somewhere the string
$f = "somedescriptivefunctionName";
and then call the function by specifying its name in curly brackets:
$this->{$f}('Yahoo!!');
EDIT
According to the comment section, the aim is to call a method of an instance of another class by the same name. You can extend the config class to achieve a somewhat similar behavior, but I think that's not acceptable as a solution in this case. You can convert your class into a decorator, like
class SomeClassDecorator
{
protected $_instance;
public function myMethod() {
return strtoupper( $this->_instance->someMethod() );
}
public function __construct(SomeClass $instance) {
$this->_instance = $instance;
}
public function __call($method, $args) {
return call_user_func_array(array($this->_instance, $method), $args);
}
public function __get($key) {
return $this->_instance->$key;
}
public function __set($key, $val) {
return $this->_instance->$key = $val;
}
// can implement additional (magic) methods here ...
}
The code above was copied from Gordon's amazing answer here: How to add a method to an existing class in PHP?
An evolution of this idea could be to create a decorator for your config class as a base class and extend that base class for your classes that are configured by this, so you will implement a single decorator and (re)use it.
You can bind a property to a closure and invoke the property as a method:
class T{
private $f;
function abcdefghijklmnopqrstuvwxyz($param){
echo $param;
}
function __construct(){
$this->f = fn() => $this->abcdefghijklmnopqrstuvwxyz(...func_get_args());
}
function doWork() {
($this->f)('hello');
$g = $this->f;
$g('world');
}
}
(new T())->doWork();
In this code, the class f property is bound to a closure via an arrow function, which became available as of PHP 7.4, and we can dynamically get the arguments passed via func_get_args. If you are using a version less than 7.4 you can also just use a regular anonymous function.
Unfortunately, do to name resolution issues, properties cannot be invoked as methods without using an extra set of parentheses which means you have to do ($this->f)('hello'). If you want to skip that, you can further assign the class property to a local variable which can then be invoked normally as the $g is done.
edit
Here's a version of the constructor that doesn't use arrow functions, it is invoked in the same way.
function __construct()
{
$this->f = function () {
return $this->abcdefghijklmnopqrstuvwxyz(...func_get_args());
};
}
My coworker ask me how to dynamically implement methods in a class. What I come up with was strategy pattern. At the first time, I made regular strategy pattern, and at the end I understood it's not good idea to make property call function. Because the child class is controller class whose methods needs to be called directly.
So, I'm trying to implement Package's method to B class directly. But I'm stuck when calling __call function. The function tried to implement works in class B. However, when it's extended the function I saved in B class doesn't work.
<?php
class A {
public $once = 0;
}
class B extends A {
public $methods = [];
public function __construct()
{
//load package
$package = new Package;
//get method names
$methods = get_class_methods($package);
//loop to assign methods to class B instance
foreach($methods as $method)
{
$this->methods[$method] = $this->setFunc($package, $method);
}
}
//I made this function because [$package, $method] was regarded as
// array instead of function when it is assigned to other variable
private function setFunc($package, $methodName)
{
return function() use($package, $methodName)
{
return call_user_func([$package, $methodName]);
};
}
}
//package class
class Package {
public function hello_world(){
return "hello world";
}
}
class C extends B{
public function __construct()
{
parent::__construct();
//assigning functions to class C
foreach($this->methods as $key => $val)
{
//I did it in child class because dynamically produced properties
// weren't recognized
$this->$key = $val;
}
}
//dynamically assigned functions to dynamic properties must be called by
//__call function
public function __call($name, $arguments)
{
//I made this condition because calling function loops infinitely.
if($this->once >= 1)
{
return;
}
$this->once++;
//not working here. nothing shown
return $this->$name();
}
}
$c = new C;
echo $c->hello_world(); //I want to display hello world!
replace return $this->$name(); with call_user_func($this->$name,[]);
or in php7 this works return ($this->$name)();
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
I would like to add some logic before and after every time I'm using a method (doesn't really matter if is private, protected or public) in a class.
For example:
class Service
{
function test1() {
Log:start(__METHOD__);
someLogicInThere(); ....
Log:end(__METHOD__);
}
function test2() {
Log:start(__METHOD__);
someLogicInThere(); ....
Log:end(__METHOD__);
}
...
}
My idea is to finally have something like this:
/**
* #LogDecorate
*/
class Service
{
function test1() {
someLogicInThere();
}
function test2() {
someLogicInThere();
}
...
}
Use annotations is not important. There is any way to do that?
As your question-title already suggests, you could use the Decorator Pattern for this. I'm not quite sure though if a full-stack Decorator pattern is necessary here. If it's a really simple use-case, something like this could suffice.
What you could do, is extend the class and 'route' all calls to the extended class. Then add some logic before and after, and call the parent method in between. Something like this:
class Service {
function method1() {
doSomeFunkyStuff();
}
function method2() {
doSomeOtherFunkyStuff();
}
}
class DecoratedService extends Service {
function method1() {
Log::start(__METHOD__);
parent::method1();
Log::end(__METHOD__);
}
function method2() {
Log::start(__METHOD__);
parent::method2();
Log::end(__METHOD__);
}
}
$service = new DecoratedService();
$service->method1();
$service->method2();
Now you can either choose to use the original Service or use the DecoratedService. The funcionality is the same, and the DecoratedService will not have to be altered if the Service changes, assuming the method names won't change (which would actually be a bad thing).
But also look to the wiki page (or any other resource) to understand fully what's the intent of the Decorator Pattern. This (above) might not be the ideal solution for your problem.
EDIT a bit more automatic as requested, sir.
As you cannot change the visibility of methods, using the magic __call() doesn't work (as public or protected parent methods will be accessible from the child as well). But, what you can do, is create your own call method!
class DecoratedService extends Service {
function call($method) {
if(!method_exists(parent, $method)) {
return false; // OR:
throw Exception;
// OR handle this case some other way
}
Log::start(array(parent, $method));
call_user_func(array(parent, $method));
Log::end(array(parent, $method));
}
}
$service = new DecoratedService;
$service->call('method1');
I guess, it is a typicall case of Smart Reference Pattern (some mix of Proxy and Decorator Patterns).
class A {
function test1() {
echo 'TEST 1', PHP_EOL;
}
function test2() {
echo 'TEST 1', PHP_EOL;
}
}
class ProxyA {
protected $wrapped;
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function __call($name, $args) {
echo 'Log:start';
$this->wrapped->$name($args);
echo 'Log:end';
}
}
$proxy = new ProxyA(new A());
$proxy->test1();
But it works only for public methods.
Mixing Smart Reference with DecoratedService::call() method from #giorgio and #yceruto answer can cover all method, or just implement __call() twice:
class A {
public function test1() {
echo 'TEST 1', PHP_EOL;
}
private function test2() {
echo 'TEST 2', PHP_EOL;
}
public function __call($name, $args) {
if (method_exists($this, $name)) {
$this->$name($args);
}
}
}
class ProxyA {
protected $wrapped;
public function __construct($wrapped) {
$this->wrapped = $wrapped;
}
public function __call($name, $args) {
if (method_exists($this->wrapped, $name)) {
echo 'Log:start';
$this->wrapped->$name($args);
echo 'Log:end';
}
}
}
$proxy = new ProxyA(new A());
$proxy->test0(); // Nothing to do
$proxy->test1(); // Done
$proxy->test2(); // Done
Use of the magic __call method might make this easy for you:
class Service
{
public function test1() {
echo 'TEST 1', PHP_EOL;
}
protected function test2() {
echo 'TEST 2', PHP_EOL;
}
public function __call($method, $args) {
echo 'Some stuff before', PHP_EOL;
$returnValue = $this->$method($args);
echo 'Some stuff after', PHP_EOL;
return $returnValue;
}
}
$x = new Service();
$x->test2();
$x->test1();
Note that if the method is accessible from outside the class (like test1) then __call() won't be invoked; it only executes if the methods involved are protected or private; and doesn't trigger if they are invoked internally within the object either
I have an existent class and I want to create a system to load "plugins" for it. Those "plugins" are created as files and then included in the file with the main class.
Now I think the main class needs to extend those little "plugins" with their own classes. The problem is that I don't know what plugins will include different users. So the extending of the classes is dynamically.
How can I extend on-the-fly a class, maybe without using eval (I didn't tested that either)?
Are you talking about __autoload?
function __autoload($class) {
require_once($class.'.php');
}
$object = new Something();
This will try to require_once(Something.php);
You can sort of do it by using PHP's magic functions. Suppose you have class A. You want an "instance" of A with some extra methods available.
class A {
public $publicA;
public function doA() {
$this->publicA = "Apple";
}
}
class B {
public $publicB;
public function doB() {
$this->publicB = "Bingo";
}
private $object;
public function __construct($object) {
$this->object = $object;
}
public function __call($name, $arguments) {
return call_user_func_array(array($this->object, $name), $arguments);
}
public function __get($name) {
return $this->object->$name;
}
public function __set($name, $value) {
$this->object->$name = $value;
}
}
$b = new B(new A);
$b->doA();
$b->doB();
echo "$b->publicA, $b->publicB";
The instance $b has the methods and properties of both A and B. This technique can be extended to aggregate functionalities from any number of classes.