simple "plugin" system using __call - php

so far, I have this:
class Foo{
private $plugin_methods = array();
public function registerPlugin($caller, $method){
list($object, $caller) = explode('.', $caller);
$this->plugin_methods[$object][$caller] = $method;
}
public function _doPluginMethod($object, $name, $args){
if(isset($this->plugin_methods[$object][$name]))
return call_user_func_array($this->plugin_methods[$object][$name], $args);
throw new Exception("Method '{$name}' not defined for '{$object}'.");
}
public function __call($name, $args){
return $this->_doPluginMethod('foo', $name, $args);
}
}
and now I can do this:
$foo = new Foo();
$foo->registerPlugin('foo.my_plugin', function($something){
return $something * 1000;
});
$foo->my_plugin(3453245);
But how can I get to the $this object inside my "plugin" function?

Depends on the version of PHP you are using. As of PHP 5.4, the use of $this in anonymous functions (also called closures) is possible. Prior to that, it wasn't.
Check the changelog here: http://php.net/manual/en/functions.anonymous.php

Related

Pass Method form different class for later execution to a second class

I have to classes and I want to pass a method fro classA to classB constructor and store it on a classB instance variable so as to execute it later.
class A
{
public function execute()
{
$ClassB = new ElgEmpAnalyticsImporterControllerImporterEmporioOrder(ConMW::getDB(), $this -> lPointFile, [$this, 'getLastPoints'] );
$ClassB -> import();
}
public function getLastPoints(Array $keys)
{
$res = [];
forEach ( json_decode( file_get_contents($this -> lastPointFile) ) as $key => $value ):
if ( in_array($key, $keys) ):
$res[$key] = $value;
else:
$res[$key] = '';
endif;
endforeach;
unset($key);
unset($value);
return $res;
}
}
classB
{
public $getLastPoints = null;
public function __construct(callable $getLastPoints)
{
$this -> getLastPoints = $getLastPoints;
}
public function import()
{
$lastPoints = $this -> getLastPoints(['idOrder', 'orderLastExport']);
}
}
Trying to execute it like that I get the error "Call to undefined method getLastPoints()"
I think the problem is on storing the function on the instance variable $getLastPoints of classB. I can conclude this because If I execute the function on the constructor it works. That means if I change the constructor of classB like this
classB
{
public $getLastPoints = null;
public function __construct(callable $getLastPoints)
{
$getLastPoints(['idOrder', 'orderLastExport']);
}
}
it works.
But what i need is to execute the external function inside the import function.
Can someone please help me?
thanks for your time,
Edit for clarification: My question is why I can execute the function inside the contructor like this :
$lastPoint(a,b)
but when I assign the callable into an instance variable like this:
$this -> lastPoint(a,b)
it does not work.
I read that php uses different storage for variables and function. PHP probably sees the callable $lastPoints as a variable. So can the callable $lastPoints, be added as dynamic function to my instance of classB?
Christoforos
PHP Callable Object as Object Member
$getlastpoints is a property with an array value stored in it, not a function.
call_user_func_array($this->getlastpoints, ['idOrder', 'orderLastExport']);
public function import()
{
$my_func = $this->getLastPoints;
$lastPoints = $my_func(['idOrder', 'orderLastExport']);
}
In a nutshell, the reason you will have to do this is because you can define properties and methods in a PHP class having the same name. e.g.
class foo {
public $bar
public function bar() {}
}
so in this instance, if allowed to directly access a stored callable on the $bar property... What would the call below reference?
$my_foo_obj->bar()
To avoid the situation, you cannot call it directly.

Is there a way to perform this with PHP? Static methods

Looks like impossible do something like that:
class Service
{
public function get($argument1, $argument2)
{
echo "Arguments: $argument1, $argument2.";
}
}
class Main
{
public function __callStatic($method, $arguments)
{
$method = "get".ucfirst($method);
$arguments = implode(", ", $arguments);
$object = self::$method;
return $object->get($arguments);
}
public static function getService()
{
return new Service;
}
}
$main = new Main;
$main::Service("1", "2"); // Should output 'Arguments: 1, 2.'
The problem is the self mess everthing, with this will be possible.
Use self::$variable, self this we have a variable in the Main context.
$object = self don't work too.
$object = clone self dont' work too.
define("method", $method);
self::method will not work because self thinks is a class constant property.
this can't be used.
Is there a way to perform this?
Just pass the original $arguments directly to call_user_func_array:
return call_user_func_array(array($object, "get"), $arguments);
Then, just make a static call to Main:
Main::service("1", "2");
See it here in action: http://codepad.viper-7.com/phhqy6

call back function with $this

I am trying to use an instance method as a callback for PHP 5.2.1. I am aware that as of PHP 5.4 you can use $this inside a closure, and in PHP 5.3 you can rename $this to $self and pass that to the closure. However, neither of these methods will suffice since I need this to work for PHP 5.2.1. The two commented lines was my last attempt. That results in Fatal error: Call to a member function hello() on a non-object - is there anyway I can have a callback to an instance method in PHP 5.2.1?
<?php
class Test {
public function __construct() {
$self = &$this;
$cb = function() use ( $self ) {
$self->hello();
};
call_user_func( $cb );
// $cb = create_function( '$self', '$self->hello();' );
// call_user_func( $cb );
}
public function hello() {
echo "Hello, World!\n";
}
}
$t = new Test();
Pass an array to include the object:
call_user_func( array( $this, 'hello' ) );
http://us3.php.net/manual/en/language.types.callable.php
$cb = create_function('$self', '$self->hello();');
This is just making a function that can take a parameter called $self. It's the same as this:
function test($self){
$self->hello();
}
You can try passing $self (or $this) to the function when you call it:
call_user_func($cb, $this);
You can also try to make $self a global variable, so that the anonymous function made by create_function can read it.
$GLOBALS['mySelf'] = $self;
$cb = create_function('', 'global $mySelf; $mySelf->hello();');
call_user_func($cb);
// You may want to unset this when done
unset($GLOBALS['mySelf']);
How about SIMPLICITY?
class Test {
public function __construct() {
$this -> funcName($this);
}
public function funcName($obj) {
$obj->hello();
}
public function hello() {
echo "Hello, World!\n";
}
}
Update: Just tested the codes. They are working fine using this.
call_user_func_array(array($self, "hello"), array());

Accessing private/protected properties of an object in anonymous function in PHP

I'm trying to dump elements of an object's private property through an anonymous function - of course I could achieve this in any number of other ways, but this highlights a PHP conundrum I can't solve off the top of my head, short of $foo = $this and using $foo - but THAT won't give me the private stuff, so... suggestions ?
Sample code:
class MyClass
{
private $payload = Array( 'a' => 'A element', 'b' => 'B element');
static $csvOrder = Array('b','a');
public function toCSV(){
$values = array_map(
function($name) use ($this) { return $this->payload[$name]; },
self::$csvOrder
);
return implode(',',$values);
}
}
$mc = new MyClass();
print $mc->toCSV();
I believe there is absolutely no way to do directly what you propose.
However, you can work around it either by making the anonymous method a class method (this is not what you asked for, but it could be a practical solution) or pulling everything you need out of $this explicitly and passing the extracted values into the function:
class MyClass
{
private $payload = Array( 'a' => 'A element', 'b' => 'B element');
static $csvOrder = Array('b','a');
public function toCSV(){
$payload = $this->payload;
$values = array_map(
function($name) use ($payload) { return $payload[$name]; },
self::$csvOrder
);
return implode(',',$values);
}
}
You can hack around the limitation by creating a wrapper that utilizes Reflection to allow you to access all properties and methods. You can use it like this then:
$self = new FullAccessWrapper($this);
function () use ($self) { /* ... */ }
Here a sample implementation of the wrapper, taken from here:
class FullAccessWrapper
{
protected $_self;
protected $_refl;
public function __construct($self)
{
$this->_self = $self;
$this->_refl = new ReflectionObject($self);
}
public function __call($method, $args)
{
$mrefl = $this->_refl->getMethod($method);
$mrefl->setAccessible(true);
return $mrefl->invokeArgs($this->_self, $args);
}
public function __set($name, $value)
{
$prefl = $this->_refl->getProperty($name);
$prefl->setAccessible(true);
$prefl->setValue($this->_self, $value);
}
public function __get($name)
{
$prefl = $this->_refl->getProperty($name);
$prefl->setAccessible(true);
return $prefl->getValue($this->_self);
}
public function __isset($name)
{
$value = $this->__get($name);
return isset($value);
}
}
Obviously the above implementation doesn't cover all aspects (e.g. it can't use magic properties and methods).
As you said yourself, it is private and therefore in accessible.
You can:
Pass $this->payload as a parameter to the anonymous function.
Create a method in the class and use it instead.

PHP class problem

class Gdn {
const AliasDispatcher = 'Dispatcher';
protected static $_Factory = NULL;
public static function Dispatcher() {
$Result = self::Factory(self::AliasDispatcher);
return $Result;
}
public static function Factory($Alias = FALSE) {
if ($Alias === FALSE)
return self::$_Factory;
// Get the arguments to pass to the factory.
//$Args = array($Arg1, $Arg2, $Arg3, $Arg4, $Arg5);
$Args = func_get_args();
array_shift($Args);
return self::$_Factory->Factory($Alias, $Args);
}
}
If I call the Dispatcher() like $Dispatcher = Gdn::Dispatcher();, what does return self::$_Factory->Factory($Alias, $Args); mean?
It means Dispatcher() is returning an object, and that object is a copy of something created by Factory().
self:: means you are refering to the class of the current object
since factory is a recursive function it will keep calling itself until it runs out of arguments and then returns the factory that is set in the current factory class.
if you would do:
"blah->Factory('test1','test2',test3','test4')" it would run like:
blah->factory('test1','test2','test3','test4')
blah->$_Factory->Factory('test1',array('test2','test3','test4'))
blah->$_Factory->Factory(array('test2','test3','test4'));
blah->$_Factory->Factory();
// oh hey, i dont have any arguments, replace it with my default argument 'false' and thus return the factory
return self::$_Factory;
i dont know WHY you would want it, but this is what it does

Categories