Adding a custom function to an already instantiated object in PHP? - php

What's the best way to do something like this in PHP?:
$a = new CustomClass();
$a->customFunction = function() {
return 'Hello World';
}
echo $a->customFunction();
(The above code is not valid.)

Here is a simple and limited monkey-patch-like class for PHP. Methods added to the class instance must take the object reference ($this) as their first parameter, python-style.
Also, constructs like parent and self won't work.
OTOH, it allows you to patch any callback type into the class.
class Monkey {
private $_overload = "";
private static $_static = "";
public function addMethod($name, $callback) {
$this->_overload[$name] = $callback;
}
public function __call($name, $arguments) {
if(isset($this->_overload[$name])) {
array_unshift($arguments, $this);
return call_user_func_array($this->_overload[$name], $arguments);
/* alternatively, if you prefer an argument array instead of an argument list (in the function)
return call_user_func($this->_overload[$name], $this, $arguments);
*/
} else {
throw new Exception("No registered method called ".__CLASS__."::".$name);
}
}
/* static method calling only works in PHP 5.3.0 and later */
public static function addStaticMethod($name, $callback) {
$this->_static[$name] = $callback;
}
public static function __callStatic($name, $arguments) {
if(isset($this->_static[$name])) {
return call_user_func($this->_static[$name], $arguments);
/* alternatively, if you prefer an argument list instead of an argument array (in the function)
return call_user_func_array($this->_static[$name], $arguments);
*/
} else {
throw new Exception("No registered method called ".__CLASS__."::".$name);
}
}
}
/* note, defined outside the class */
function patch($this, $arg1, $arg2) {
echo "Arguments $arg1 and $arg2\n";
}
$m = new Monkey();
$m->addMethod("patch", "patch");
$m->patch("one", "two");
/* any callback type works. This will apply `get_class_methods` to the $m object. Quite useless, but fun. */
$m->addMethod("inspect", "get_class_methods");
echo implode("\n", $m->inspect())."\n";

Unlike Javascript you can't assign functions to PHP classes after the fact (I assume you are coming from Javascript becuase you are using their anonymous functions).
Javascript has a Classless Prototypal system, where as PHP has a Classical Classing System. In PHP you have to define every class you are going to use, while in Javascript, you can create and change each object however you want.
In the words of Douglas Crockford: You can program in Javascript like it is a Classical System, but you can't program in a Classical System like it is Javascript. This means that a lot of the stuff you are able to do in Javascript, you can't do in PHP, without modifications.

I smell Adapter Pattern, or maybe even Decorator Pattern!

Related

How to test if Callable parameter will return a string with Reflection?

I have a function which expects a Callable parameter. I want to determine that this callable returns a string and if it doesn't an exception should be thrown.
I tried searching for this, but no luck. Does the PHP reflection API provide functionality like this? I don't want to run the method and see if it actually returns a string.
Example of what I need:
class MyClass
{
protected static $overrider = null;
public static function setOverrider(Callable $callback)
{
// Pseudo code start
if (!$callback returns string) {
throw new \Exception('Wasnt a string!');
}
// Pseudo code end
self::$overrider = $callback;
}
}
Maybe you need something like this:
class MyClass
{
protected static $overrider = null;
public static function setOverrider(Callable $callback)
{
$reflection = new ReflectionFunction($callback);
if ('string' != $reflection->getReturnType()) {
throw new \Exception('Wasnt a string!');
}
self::$overrider = $callback;
}
}
So, as I mentioned previously in comments: You need to declare returning type of your callable (which is a PHP7+ feature). It is a MUST, otherwise, it will not work
Like this:
function my_function(): string
{
return 'hello';
}
or like this if you prefer anonymous functions (Closure):
$my_callable = function(): string {
return 'hello';
}
It is as simple as this:
The interpreter cannot know the returning data type of a function without invoking it if you don't first tell the interpreter what should return the function in question.

invoking runtime created functions

I'm trying to dynamically create the base for a DB entity generalization for a project I'm working on. I basically want to dynamically create a set of standard methods and tools for the properties in any class that extends this. Much like the tools you get for free with Python/Django.
I got the idea from this guy: http://www.stubbles.org/archives/65-Extending-objects-with-new-methods-at-runtime.html
So I've implemented the __call function as described in the post above,
public function __call($method, $args) {
echo "<br>Calling ".$method;
if (isset($this->$method) === true) {
$func = $this->$method;
$func();
}
}
I have a function which gives me the objects public/protected properties through get_object_vars,
public function getJsonData() {
$var = get_object_vars($this);
foreach($var as &$value) {
if (is_object($value) && method_exists($value, 'getJsonData')) {
$value = $value->getJsonData;
}
}
return $var;
}
and now I want to create some methods for them:
public function __construct() {
foreach($this->getJsonData() as $name => $value) {
// Create standard getter
$methodName = "get".$name;
$me = $this;
$this->$methodName = function() use ($me, $methodName, $name) {
echo "<br>".$methodName." is called";
return $me->$name;
};
}
}
Thanks to Louis H. which pointed out the "use" keyword for this down below.
This basically creates an anonymous function on the fly. The function is callable, but it is no longer within the context of it's object. It produces a "Fatal error: Cannot access protected property"
Unfortunately I'm bound to PHP version 5.3, which rules out Closure::bind. The suggested solution in Lazy loading class methods in PHP will therefore not work here.
I'm rather stumped here... Any other suggestions?
Update
Edited for brevity.
Try it like this (you have to make the variables you'll need available to the method)
$this->$methodName = function() use ($this, $methodName, $name){
echo "<br>".$methodName." is called";
return $this->$$name;
};
You should have access to the object context through $this.
Instead of updating the original question above, I include the complete solution here for anybody struggling with the same issues:
First of all, since the closure cannot have real object access, I needed to include the actual value with the "use" declaration when creating the closure function (see original __construct function above):
$value =& $this->$name;
$this->$methodName = function() use ($me, $methodName, &$value) {
return $value;
};
Secondly the __call magic method did not just need to call the closure function, it needed also to return any output from it. So instead of just calling $func(), I return $func();
This did the trick! :-)

Execute a method when an instance variable is referenced in a PHP class?

Is it possible to run a function when referring to a variable in a php class rather than simply returning its value, similar to javascript's ability for a variable to hold a method?
class LazyClassTest()
{
protected $_lazyInitializedVar;
public function __construct()
{
/* // How can this call and return runWhenReferrenced() when
// someone refers to it outside of the class:
$class = new LazyClass();
$class->lazy;
// Such that $class->lazy calls $this->runWhenReferrenced each
// time it is referred to via $class->lazy?
*/
$this->lazy = $this->runWhenReferrenced();
}
protected function runWhenReferrenced()
{
if (!$this->_lazyInitializedVar) {
$this->_lazyInitializedVar = 'someValue';
}
return $this->_lazyInitializedVar
}
}
PHP5s magic method __get($key) and __set($key, $value) might be what you need. More information about them is available in the PHP manual.
This sounds like PHP5.3: lambda / closures / anonymous functions
http://php.net/manual/en/functions.anonymous.php:
<?php
$greet = function($name) {
printf("Hello %s\r\n", $name);
};
$greet('World');
$greet('PHP');
?>
You are probably heading in the wrong direction. You normally want to define a getter getLazyVar(). There is a reason why people always make properties protected and defined getters / setters: So they can pre- or postprocess the values.

PHP[OOP] - How to call class constructor manually?

Please see the code bellow:
01. class Test {
02. public function __construct($param1, $param2, $param3) {
03. echo $param1.$param2.$param3;
04. }
05. }
06.
07. $params = array('p1','p2','p3');
08.
09. $ob = new Test;
10.
11. if(method_exists($ob,'__construct')) {
12. call_user_func_array(array($ob,'__construct'),$params);
13. }
Now, the problem is the constructor is called in line 09
But i want to call it manually at line 11-13
Is it possible? If then how? Any idea please?
It is not possible to prevent the constructor from being called when the object is constructed (line 9 in your code). If there is some functionality that happens in your __construct() method that you wish to postpone until after construction, you should move it to another method. A good name for that method might be init().
Why not just do this?
class Test {
public function __construct($param1, $param2, $param3) {
echo $param1.$param2.$param3;
}
}
$ob = new Test('p1', 'p2', 'p3');
EDIT: I just thought of a hacky way you could prevent a constructor from being called (sort of). You could subclass Test and override the constructor with an empty, do-nothing constructor.
class SubTest extends Test {
public function __construct() {
// don't call parent::__construct()
}
public function init($param1, $param2, $param3) {
parent::__construct($param1, $param2, $param3);
}
}
$ob = new SubTest();
$ob->init('p1', 'p2', 'p3');
This is might make sense if you're dealing with some code that you cannot change for some reason and need to work around some annoying behavior of a poorly written constructor.
Note that if the constructor (__construct method) contains arguments passed by
reference, then the function:
call_user_func_array
will fail with an error.
I suggest you to use Reflection class instead; here is how you can do so:
// assuming that class file is already included.
$refMethod = new ReflectionMethod('class_name_here', '__construct');
$params = $refMethod->getParameters();
$re_args = array();
foreach($params as $key => $param)
{
if ($param->isPassedByReference())
{
$re_args[$key] = &$args[$key];
}
else
{
$re_args[$key] = $args[$key];
}
}
$refClass = new ReflectionClass('class_name_here');
$class_instance = $refClass->newInstanceArgs((array) $re_args);
I don't know if there are some security concerns by using the eval() method, but you could make yourself a function like this:
function CreateObj($className,$params)
{
$strArgs = '$params['.implode('],$params[',array_keys($params)).']';
eval('$ob = new '.$className.'('.$strArgs.');');
return $ob
}
And now $ob should now be defined with its correct parameters, i haven't tested it and maybe there is a mistake in the code, but you get the idea....
If separating instantiation from initialization isn't strictly a requirement, there are two other possibilities: first, a static factory method.
class Test {
public function __construct($param1, $param2, $param3) {
echo $param1.$param2.$param3;
}
public static function CreateTest($param1, $param2, $param3) {
return new Test($param1, $param2, $param3);
}
}
$params = array('p1','p2','p3');
if(method_exists($ob,'__construct')) {
call_user_func_array(array($ob,'CreateTest'),$params);
}
Or, if you're using php 5.3.0 or higher, you can use a lambda:
class Test {
public function __construct($param1, $param2, $param3) {
echo $param1.$param2.$param3;
}
}
$params = array('p1','p2','p3');
$func = function ($arg1, $arg2, $arg3) {
return new Test($arg1, $arg2, $arg3);
}
if(method_exists($ob,'__construct')) {
call_user_func_array($func, $params);
}
The initialization method described by Asaph is great if for some reason you have a need to logically separate initialization from instantiation, but if supporting your use case above is a special case, not a regular requirement, it can be inconvenient to require users to instantiate and initialize your object in two separate steps.
The factory method is nice because it gives you a method to call to get an initialized instance. The object is initialized and instantiated in the same operation, though, so if you have a need to separate the two it won't work.
And lastly, I recommend the lambda if this initialization mechanism is uncommonly used, and you don't want to clutter your class definition with initialization or factory methods that will hardly ever be used.
In PHP you can create objects w/o calling the constructor. But that does not work by using new but by un-serializing an object instance.
The constructor can then be called manually.
Normally this is not needed. See as well: Loading an object from PHP session, does it call constructor?
<?php
class Test
{
public function __construct()
{
echo '__construct called.',"\n";
}
}
function old($class)
{
return unserialize
(
sprintf
(
'O:%d:"%s":0:{}',
strlen($class),
$class
)
);
}
$test = old('Test');
$test->__construct();
to construct your object first and then pass parameters your could try this way:
class Test {
public function __CONSTRUCT($p1="Bundy", $p2="house", $p3=10) {
$this->init($p1, $p2, $p3);
}
public function init($p1, $p2, $p3) {
//setup your object here
}
}
then it is possible to construct the object and call
$ob->init($p1, $p2, $p3);
later.
I see no reason why the constructor should be deferred so this below still achieves what you probably want and even better because the constructor will by default be called on object instantiation.
class Test {
public function __construct()
{
}
public function init($param1, $param2, $param3){
echo $param1.$param2.$param3;
}
}
$params = array('p1','p2','p3');
$ob = new Test();
if(method_exists($ob,'init')) {
call_user_func_array(array($ob,'init'),$params);
}

Something like a callback delegate function in php

I would like to implement something similar to a c# delegate method in PHP. A quick word to explain what I'm trying to do overall: I am trying to implement some asynchronous functionality. Basically, some resource-intensive calls that get queued, cached and dispatched when the underlying system gets around to it. When the asynchronous call finally receives a response I would like a callback event to be raised.
I am having some problems coming up with a mechanism to do callbacks in PHP. I have come up with a method that works for now but I am unhappy with it. Basically, it involves passing a reference to the object and the name of the method on it that will serve as the callback (taking the response as an argument) and then use eval to call the method when need be. This is sub-optimal for a variety of reasons, is there a better way of doing this that anyone knows of?
(Apart from the observer pattern) you can also use call_user_func() or call_user_func_array().
If you pass an array(obj, methodname) as first parameter it will invoked as $obj->methodname().
<?php
class Foo {
public function bar($x) {
echo $x;
}
}
function xyz($cb) {
$value = rand(1,100);
call_user_func($cb, $value);
}
$foo = new Foo;
xyz( array($foo, 'bar') );
?>
How do you feel about using the Observer pattern? If not, you can implement a true callback this way:
// This function uses a callback function.
function doIt($callback)
{
$data = "this is my data";
$callback($data);
}
// This is a sample callback function for doIt().
function myCallback($data)
{
print 'Data is: ' . $data . "\n";
}
// Call doIt() and pass our sample callback function's name.
doIt('myCallback');
Displays: Data is: this is my data
I was wondering if we could use __invoke magic method to create "kind of" first class function and thus implement a callback
Sound something like that, for PHP 5.3
interface Callback
{
public function __invoke();
}
class MyCallback implements Callback
{
private function sayHello () { echo "Hello"; }
public function __invoke () { $this->sayHello(); }
}
class MySecondCallback implements Callback
{
private function sayThere () { echo "World"; }
public function __invoke () { $this->sayThere(); }
}
class WhatToPrint
{
protected $callbacks = array();
public function register (Callback $callback)
{
$this->callbacks[] = $callback;
return $this;
}
public function saySomething ()
{
foreach ($this->callbacks as $callback) $callback();
}
}
$first_callback = new MyCallback;
$second_callback = new MySecondCallback;
$wrapper = new WhatToPrint;
$wrapper->register($first_callback)->register($second_callback)->saySomething();
Will print HelloWorld
Hope it'll help ;)
But I'd prefer the Controller pattern with SPL for such a feature.

Categories