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.
Related
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)();
So, I have an existing class hierarchy that I can't modify. There are existing consumers of classes in that hierarchy in more than just my codebase. I have another class (in a new, but external library) that has a different contract (class prototype) with similar but improved functionality. I wish to provide that new functionality to existing consumers of the old code.
class OldBase {}
class OldSubClass extends OldBase{}
class NewCode {}
//consumers
existingMethod(OldSubClass $c) {}
alsoExistingMethod(OldBase $c) {}
I thought of using an AdapterInterface, but this seems, perhaps, inelegant.
interface NewCodeAdapterInterface
{
//interface that mimics the contract from OldBase
}
class NewCodeImplementation implements NewCodeAdapterInterface{}
//now this code can not be used with any existing OldBase objects :-\
existingMethod(NewCodeAdapterInterface $c) {}
I'd like to ensure a backwards compatible way to allow old code to be used while allowing a clean way to use the new with as few ramifications as possible, but how?
Starting with the premise that you want to implement a unified replacement of disparate classes consumed by existing code, without modifying the existing consumers, then I have a... "solution".
Here's an example of the current problem:
class A
{
public function test()
{
echo "A\n";
}
}
class B
{
public function test()
{
echo "B\n";
}
}
class Consumer
{
public function runTestA(A $a)
{
$a->test();
}
public function runTestB(B $b)
{
$b->test();
}
}
$con = new Consumer();
$a = new A();
$b = new B();
$con->runTestA($a);
$con->runTestB($b);
You're trying to find a solution that will allow something like, without modifying anything in Consumer:
$con = new Consumer();
$c = new C();
$con->runTestA($c);
$con->runTestB($c);
I'm going to heavily advise against doing what I'm about to outline. It would be better to modify the method signatures in Consumer to allow a new class to be passed that has the joint functionality. But, I'm going to answer the question as asked...
To start with, we need a couple of classes which can pass any existing method signatures. I'll use a trait to define the joint functionality.
trait ExtensionTrait
{
public function test()
{
echo "New Functionality\n";
}
}
class ExtendedA extends A
{
use ExtensionTrait;
}
class ExtendedB extends B
{
use ExtensionTrait;
}
Now we have some classes with the new functionality, which can pass the method checks... if we pass the right one. So, how do we do that?
Let's first put together a quick utility class that allows easy switching between the two classes.
class ModeSwitcher
{
private $a;
private $b;
public $mode;
public function __construct($a, $b)
{
$this->a = $a;
$this->b = $b;
$this->mode = $this->a;
}
public function switchMode()
{
if ($this->mode instanceof ExtendedA)
{
$this->mode = $this->b;
}
elseif ($this->mode instanceof ExtendedB)
{
$this->mode = $this->a;
}
}
public function __set($name, $value)
{
$this->a->$name = $value;
$this->b->$name = $value;
}
public function __isset($name)
{
return isset($this->mode->$name);
}
public function __unset($name)
{
unset($this->a->$name);
unset($this->b->$name);
}
public function __call($meth, $args)
{
return call_user_func_array([$this->mode, $meth], $args);
}
}
This mode switcher class maintains a current mode class, which passes through gets and calls. Sets and unsets are applied to both classes, so any properties modified aren't lost upon a mode switch.
Now, if we can modify the consumer of the consumer, we can put together a translation layer that automatically switches between modes to find the correct mode.
class ConsumerTranslator
{
private $consumer;
public function __construct(Consumer $consumer)
{
$this->consumer = $consumer;
}
public function __get($name)
{
return $this->consumer->$name;
}
public function __set($name, $value)
{
$this->consumer->$name = $value;
}
public function __isset($name)
{
return isset($this->consumer->$name);
}
public function __unset($name)
{
unset($this->consumer->$name);
}
public function __call($methName, $arguments)
{
try
{
$tempArgs = $arguments;
foreach ($tempArgs as $i => $arg)
{
if ($arg instanceof ModeSwitcher)
{
$tempArgs[$i] = $arg->mode;
}
}
return call_user_func_array([$this->consumer, $methName], $tempArgs);
}
catch (\TypeError $e)
{
$tempArgs = $arguments;
foreach ($tempArgs as $i => $arg)
{
if ($arg instanceof ModeSwitcher)
{
$arg->switchMode();
$tempArgs[$i] = $arg->mode;
}
}
return call_user_func_array([$this->consumer, $methName], $tempArgs);
}
}
}
Then, we can use the combined functionality like so:
$con = new Consumer();
$t = new ConsumerTranslator($con);
$a = new ExtendedA();
$b = new ExtendedB();
$m = new ModeSwitcher($a, $b);
$t->runTestA($m);
$t->runTestB($m);
This allows you to interchangeably utilize either class tree without any modification of Consumer whatsoever, nor any major changes to the usage profile of Consumer, as the Translator is basically a passthrough wrapper.
It works by catching the TypeError thrown by a signature mismatch, switching to the paired class, and trying again.
This is... not recommended to actually implement. The constraints declared provided an interesting puzzle, though, so, here we are.
TL;DR: Don't bother with any of this mess, just modify the consuming contract and use a joint interface, like you were intending.
I need to organize some kind of access control to object methods when it is used in different contexts (API's in my system). Here is code example:
class A
{
public function doA(){}
public function doB(){}
}
class APIAClient
{
public function getA()
{
return new A();
}
}
class APIBClient {
public function getA()
{
return new A();
}
}
In APIAClient object A should have both methods doA() and doB(), but in APIBClient should not have doB() method.
For now I've implemented APIBClientAProxy (which is returned by APIBCleint->getA())
class APIBClientAProxy
{
private $a = new A;
public function doA()
{
$this->a->doA()
}
}
But may be there is a better pattern for solving my problem, without using a additional proxy object for every context (i.e. API). I'm thinking about magic __call method with list of allowed methods in particular context, but magic calls is hard do document and documentation is the big point in my app (API's should be documented well)
Thanks!
Instead of inheritance you can use composition through traits (introduced in PHP 5.4).
First define traits
trait A {
public function doA() {
// do something here
}
}
trait B {
public function doB() {
// do something here
}
}
then use those traits in your class declaration
class APIAClient {
use A, B
}
class APIBClient {
use A
}
You could use inheritance here, like this:
class A {
public function doA() {
// do something here
}
}
class B extends A {
public function doB() {
// do something here
}
}
class APIAClient
{
public function getObj() {
return new B();
}
}
class APIBClient {
public function getObj() {
return new A();
}
}
This way, when you call getObj() on APIAClient, it will return an instance of B which which has both doA() and doB(). However, when you call it on APIBClient, you return an instance of A which only has doA().
You can't change the class depending on when and how it's instances are created (well, not really). You could use a hacky workaround (but I'd recommend against it)
class A
{
private $_canDoB = null;
public function __construct($doB = true)
{
$this->_canDoB = !!$doB;//force bool
}
public function doB()
{
if ($this->_canDoB === false)
{
throw new LogicError('You can\'t doB');
}
}
}
So if you pass a falsy value to the constructor of A(in your APIBClient), doB will throw an error. However, I'd recommend using inheritance, too:
class AB
{
public function doA()
{
//both B and B share this method
}
}
class B
{//nothing atm
}
class A
{
public function doB()
}
And have your APIAClient return a new A(), whereas APIBClient returns a new instance of the B class.When using type-hinting, you can just check for AB instances:
public function doSomething(AB $instance)
{
if ($instance instanceof A)
{
return $instance->doB();
}
return $instance->doA();
}
Or, when not relying on type-hinting and type-checking, you can always use one of the many functions like method_exists
I've created a custom library using singleton pattern. Why by this way? because I need to be able to call my functions without $this-> reference, for example, I could execute the code below:
function foo() {
MyLibrary::instance()->foo();
}
then I could call my functions in controllers like this:
function foo();
instead of
$this->mylibrary->foo();
I am in trouble because CodeIgniter try to instantiate my library when the best way to do is "read" the static instance.
Why I need to do this?
My custom library must register "hooks" from external php files located in /plugins directory outside of application folder. Look:
/application
/plugins
/plugins/helloworld.php
This is my Loader library:
class Loader{
public static $instance;
protected $hooks;
private function __construct($params = array()){
$this->hooks= array();
$this->includePlugins();
}
public static function instance()
{
if (!self::$instance)
{
self::$instance = new Loader();
}
return self::$instance;
}
public function includePlugins()
{
include_once "/plugins/helloworld.php";
}
public function do_action($hook= "after")
{
foreach ($this->actions[$hook] as $function)
{
call_user_func_array($function, array());
}
}
public function add_action($hook, $function, $priority)
{
if ($hooks !== false)
{
$this->actions[$hook][] = $function;
}
else
{
echo "hook name is unavalaible";
}
}
}
/**
* Functions using the Singleton instance !!!
*/
function do_action($hook)
{
Loader::instance()->do_action($hook);
}
function add_action($hook, $function, $priority)
{
Loader::instance()->add_action($hook, $function, $priority);
}
No, my helloworld.php (plugin) looks like this:
add_action('right_here', 'show_message', 11);
function show_message()
{
echo "hello world!";
}
Finally, I could call my the function do_action in my controller/view in order to print the message, like this:
do_action('right_here');
Note that all my important functions are called by a global function that allow me to use the singleton instance. But I am experiencing two issues:
Codeigniter needs that my contruct be public in order to
instantiate. Here I need to take my singleton.
If I let it public, several instances will be created and the library
will no longer works as expected.
I've solved the problem :)
In order to create a singleton library you must:
Include your library at system/core/Codeigniter.php like this:
require_once("/path/to/library.php");
Make your instance return with & symbol, like this:
Singleton:
public static function &instance()
{
if (!self::$instance)
{
self::$instance = new Loader();
}
return self::$instance;
}
Ready to uses your functions as well. Also, you can include th CI
object in you library.
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.