i was wanting to wrap an object in another - favoring composition over inheritance. but i am not sure i am doing it right tho there are no errors.
i created a class Wrapped thats is wrapped by Wrapper. i made it such that when a method/property is called on $wrapper, if it exists in the class, Wrapper, it will be returned else, it will delegate to the $wrapped object. i wonder apart from the fact i didnt check if the method/property exists, what have i done wrong? can some1 explain __callStatic() too?
class Wrapped {
protected $wrappedProp1 = 'Wrapped: Property 1';
protected $wrappedProp2 = 'Wrapped: Property 2';
function method1($arg1, $arg2) {
echo "Called Wrapped::method1() with the following parameters: $arg1, $arg2";
}
static function sMethod2() {
echo 'Called a static method in wrapped';
}
function __get($name) {
return $this->$name;
}
function __set($name, $val) {
$this->$name = $val;
}
}
class Wrapper {
protected $wrapperProp1 = 'Wrapper: Property 1';
protected $wrapped;
function __construct($wrapped) {
$this->wrapped = $wrapped;
}
function wrapperMethod() {
echo 'In wrapper method';
}
function __get($name) {
if (property_exists($this, $name)) {
return $this->$name;
}
return $this->wrapped->$name;
}
function __set($name, $val) {
if (property_exists($this, $name)) {
$this->$name = $val;
}
$this->wrapped->$name = $val;
}
function __call($name, $args = array()) {
call_user_func_array(array($this->wrapped, $name), $args);
}
static function __callStatic($name, $args = array()) {
call_user_func_array(array('Wrapped', $name), $args);
}
}
$wrapper = new Wrapper(new Wrapped);
// testing normal methods
$wrapper->wrapperMethod();
echo $wrapper->wrapperProp1;
$wrapper->wrapperProp1 = 'New Wrapper Prop 1';
echo $wrapper->wrapperProp1;
// testing delegates
$wrapper->method1('hello', 'world'); //delegated to Wrapped::method1()
$wrapper->sMethod2(); // delegated to static Wrapped::sMethod2() ... what is callStatic for then
echo $wrapper->wrappedProp2;
Wrapper::sMethod2();
As it seems - it's all ok.
About __callStatic() - it allows you to workaround undefined static functions in class.
Example:
<?php
class Foo {
static function __callStatic($name, $args = array()) {
echo "Called static function $name with arguments ". print_r($args, true);
}
}
Foo::Bar('test');
// will output "Called static function Bar with arguments Array ( 0 => test );"
Related
Try to call method_exists on method registred with call_user_func.
<?php
class stdClass1
{
public static $methods = [];
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind(self::$methods[$method], $this, get_called_class()), $arguments);
}
public function __set($name, $value) {
if (is_callable($value)) {
self::$methods[$name] = $value;
} else {
parent::__set($name, $value);
}
}
}
class stdClass2
{
function stdRunMethod()
{
$obj = new stdClass1();
$obj->test = function () {
echo 'a simple function'.PHP_EOL;
};
var_dump(method_exists($obj, "test"));
}
}
$obj = new stdClass2();
$obj->stdRunMethod();
method_exists return false. How to check this method with method_exists? Why method_exists return false?
Because test is not a method. It's a property which stores anonymous function.
If you want to check if value of property can be called as function you can use is_callable:
var_dump(is_callable([$obj, "test"]));
Need dynamically append method to class.
My code:
<?php
class stdClass1 {
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
}
class stdClass2 {
function stdRunMethod() {
$obj = new stdClass1();
$obj->test = function() {
echo 'a simple function';
};
$obj->test();
$obj2 = new stdClass1();
$obj2->test();
}
}
$obj = new stdClass2();
$obj->stdRunMethod();
Question: why test method run only for first instance of stdClass1 class? How to append this method for all new instances?
try this instead (demo):
<?php
class stdClass1 extends \stdClass
{
private static $addedClosures = array();
public function __set($name, $value)
{
if ($value instanceof \Closure) {
self::$addedClosures[$name] = $value;
}
else {
parent::__set($name, $value);
}
}
public function __call($method, $arguments)
{
if (isset(self::$addedClosures[$method]))
return call_user_func_array(self::$addedClosures[$method], $arguments);
return call_user_func_array($method, $arguments);
}
}
class stdClass2 extends \stdClass
{
function stdRunMethod()
{
$obj = new stdClass1();
$obj->test = function () {
print_r('a simple function');
};
$obj->test();
$obj2 = new stdClass1();
$obj2->test();
}
}
The reason it only runs once is that each copy of stdClass1 maintains their own set of variables. In the following
$obj1 = new stdClass1();
$obj1->a = '1';
$obj2 = new stdClass1();
$obj2->a = '2';
echo $obj1->a;
You'll get the value 1 as the output. Because in most cases they maintain different references. Unless you use the static keyword. Static properties are shared between all instances of the class, and should be used carefully, but that's what you're thinking of. What you're thinking of can be done like this
<?php
class stdClass1 {
private static $methods = [];
public function __call($method, $arguments) {
return call_user_func_array(Closure::bind($this->methods[$method], $this, get_called_class()), $arguments);
}
public function __set($name, $value) {
if (is_callable($value)) {
$this->methods[$name] = $value;
} else {
parent::__set($name, $value);
}
}
}
Here we're using a static, defined property to hold all of the dynamic methods, and we're using the magic __set property to set the methods in to the array.
That being said, dynamically loading methods in to an object is bad. Don't do that
I have two Classes viz foo & Bar
class bar extends foo
{
public $element = null;
public function __construct()
{
}
}
and the Class foo goes as
class foo implements ArrayAccess
{
private $data = [];
private $elementId = null;
public function __call($functionName, $arguments)
{
if ($this->elementId !== null) {
echo "Function $functionName called with arguments " . print_r($arguments, true);
}
return true;
}
public function __construct($id = null)
{
$this->elementId = $id;
}
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->data[] = $value;
} else {
$this->data[$offset] = $value;
}
}
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
public function offsetUnset($offset)
{
if ($this->offsetExists($offset)) {
unset($this->data[$offset]);
}
}
public function offsetGet($offset)
{
if (!$this->offsetExists($offset)) {
$this->$offset = new foo($offset);
}
}
}
i want that when i run the below piece of code:
$a = new bar();
$a['saysomething']->sayHello('Hello Said!');
should return Function sayHello Called with arguments Hello Said! from foo's __call magic method.
Here, i want to say is saysomething should be passed in $this->elementId from foo's __construct function and sayHello should be taken as method and Hello Said should be taken as parameters for sayHello Function which would be rendered from __call magic method.
Also, need to chain methods like:
$a['saysomething']->sayHello('Hello Said!')->sayBye('Good Bye!');
If I'm not mistaken, you should change foo::offsetGet() to this:
public function offsetGet($offset)
{
if (!$this->offsetExists($offset)) {
return new self($this->elementId);
} else {
return $this->data[$offset];
}
}
It returns an instance of itself if there's no element at the given offset.
That said, foo::__construct() should be called from bar::__construct() as well and be passed a value other than null:
class bar extends foo
{
public $element = null;
public function __construct()
{
parent::__construct(42);
}
}
Update
To chain calls, you need to return the instance from __call():
public function __call($functionName, $arguments)
{
if ($this->elementId !== null) {
echo "Function $functionName called with arguments " . print_r($arguments, true);
}
return $this;
}
how can i access the second property or method in this statement
$this->test->hello();
In my __get() I can only figure out how to figure out what the test property is. I want to be also be able to capture the 'hello' method call. and do some dynamic things with it.
So in short if I type
$this->test->hello()
I want to echo each segment
echo $propert // test
echo $method //hello
The issue is that my test is being used to instantiate a new class object from an outside class. The method hello belongs to the test class object.
I want to capture the method within my __get().
How can i do this?
EDIT:
public function __get($name)
{
if ($name == 'system' || $name == 'sys') {
$_class = 'System_Helper';
} else {
foreach (get_object_vars($this) as $_property => $_value) {
if ($name == $_property)
$_class = $name;
}
}
$classname = '\\System\\' . ucfirst($_class);
$this->$_class = new $classname();
//$rClass = new \ReflectionClass($this->$_class);
$rClass = get_class_methods($this->$_class);
foreach($rClass as $k => $v)
echo $v."\n";
//print_r($rClass);
return $this->$_class;
It seems you are after some kind of proxy class, this might suit your needs.
class ObjectProxy {
public $object;
public function __construct($object) {
$this->object = $object;
}
public function __get($name) {
if (!property_exists($this->object, $name)) {
return "Error: property ($name) does not exist";
}
return $this->object->$name;
}
public function __call($name, $args) {
if (!method_exists($this->object, $name)) {
return "Error: method ($name) does not exist";
}
return call_user_func_array(array($this->object, $name), $args);
}
}
class A {
public $prop = 'Some prop';
public function hello() {
return 'Hello, world!';
}
}
class B {
public function __get($name) {
if (!isset($this->$name)) {
$class_name = ucfirst($name);
$this->$name = new ObjectProxy(new $class_name);
}
return $this->$name;
}
}
$b = new B();
var_dump($b->a->hello());
var_dump($b->a->prop);
var_dump($b->a->foo);
var_dump($b->a->bar());
Output:
string 'Hello, world!' (length=13)
string 'Some prop' (length=9)
string 'Error: property (foo) does not exist' (length=36)
string 'Error: method (bar) does not exist' (length=34)
Example:
http://ideone.com/dMna6
It could be easily extend for other magic methods like __set, __callStatic, __isset, __invoke, etc.
I think you want to use __call instead of __get. Also, don't.
The object you instantiated for $this will use the __get magic method to create the object (as a property) test. The object stored at $this->test needs to implement the __call magic method to use hello() if it's not defined
i'm trying to play with php5.3 and closure.
I see here (Listing 7. Closure inside an object : http://www.ibm.com/developerworks/opensource/library/os-php-5.3new2/index.html) that it's possible to use $this in the callback function, but it's not. So I try to give $this as use variable :
$self = $this;
$foo = function() use($self) { //do something with $self }
So to use the same example :
class Dog
{
private $_name;
protected $_color;
public function __construct($name, $color)
{
$this->_name = $name;
$this->_color = $color;
}
public function greet($greeting)
{
$self = $this;
return function() use ($greeting, $self) {
echo "$greeting, I am a {$self->_color} dog named {$self->_name}.";
};
}
}
$dog = new Dog("Rover","red");
$dog->greet("Hello");
Output:
Hello, I am a red dog named Rover.
First of all this example does not print the string but return the function, but that's not my problem.
Secondly I can't access to private or protected, because the callback function is a global function and not in the context from the Dog object. Tha't my problem. It's the same as :
function greet($greeting, $object) {
echo "$greeting, I am a {$self->_color} dog named {$self->_name}.";
}
And I want :
public function greet($greeting) {
echo "$greeting, I am a {$self->_color} dog named {$self->_name}.";
}
Which is from Dog and not global.
Well, the whole reason that you can't use $this, is because the closure is an object in the background (the Closure class).
There are two ways around this. First, is add the __invoke method (What's called if you call $obj())..
class Dog {
public function __invoke($method) {
$args = func_get_args();
array_shift($args); //get rid of the method name
if (is_callable(array($this, $method))) {
return call_user_func_array(array($this, $method), $args);
} else {
throw new BadMethodCallException('Unknown method: '.$method);
}
}
public function greet($greeting) {
$self = $this;
return function() use ($greeting, $self) {
$self('do_greet', $greeting);
};
}
protected function do_greet($greeting) {
echo "$greeting, I am a {$this->_color} dog named {$this->_name}.";
}
}
If you want the closure to not change if you modify the host object, you can just change the return function to something like:
public function greet($greeting) {
$self = (clone) $this;
return function() use ($greeting, $self) {
$self('do_greet', $greeting);
};
}
The other option, is to provide a generic getter:
class Dog {
public function __get($name) {
return isset($this->$name) ? $this->$name : null;
}
}
For more information, see: http://www.php.net/manual/en/language.oop5.magic.php
As of PHP 5.4.0 Alpha1, you can access $this from within the context of an object instance:
<?php
class Dog
{
private $_name;
protected $_color;
public function __construct($name, $color)
{
$this->_name = $name;
$this->_color = $color;
}
public function greet($greeting)
{
$func = function() use ($greeting) {
echo "$greeting, I am a {$this->_color} dog named {$this->_name}.";
};
$func();
}
}
$dog = new Dog("Rover","red");
$dog->greet("Hello");
You can also do this:
$dog = new Dog("Rover", "red");
$getname = Closure::bind($dog, function() { return $this->_name; });
echo $getname(); // Rover
As you can see, it's possible to easily mess with private data... so be careful.
Well it makes sense that you cannot access private and protected fields of an object. And by explicitly passing $self to your function, it is treated just as a normal object.
You should create getters in order to access these values , i.e. :
class Dog
{
private $_name;
protected $_color;
public function __construct($name, $color)
{
$this->_name = $name;
$this->_color = $color;
}
public function getName() {
return $this->_name;
}
public function getColor() {
return $this->_color;
}
public function greet($greeting)
{
$self = $this;
return function() use ($greeting, $self) {
echo "$greeting, I am a {$self->getColor()} dog named {$self->getName()}.";
};
}
}
You should create getter (and setters) anyway, for matter of encapsulation.
Another note: The article you link to was published before the final version of PHP 5.3 was released. Maybe this implicit object passing was removed.
I use this create_closure() in my work to seperate callbacks into Classes:
<?php
function create_closure($fun, $args, $uses)
{$params=explode(',', trim($args.','.$uses, ','));
$str_params='';
foreach ($params as $v)
{$v=trim($v, ' &$');
$str_params.='\''.$v.'\'=>&$'.$v.', ';
}
return "return function({$args}) use ({$uses}) {{$fun}(array({$str_params}));};";
}
?>
example:
<?php
$loop->addPeriodicTimer(1, eval(create_closure('pop_message', '$timer', '$cache_key, $n, &$response, &$redis_client')));
function pop_message($params)
{extract($params, EXTR_REFS);
$redis_client->ZRANGE($cache_key, 0, $n)
->then(//normal
function($data) use ($cache_key, $n, &$timer, &$response, &$redis_client)
{//...
},
//exception
function ($e) use (&$timer, &$response, &$redis_client)
{//...
}
);
}
?>