I am just starting out with PHP, and I am wondering if there is a way to add an anonymous function to a class instance.
For instance, lets say...
class A{
public B;
}
$c = new A();
//This is where I am getting a little confused...
//The following wont work
$c->B = function(){echo('HelloWorld');};
$c->B();
What I am hoping to do is reuse the same spit of code in a great number of different applications, and make it so that I can just 'swap-out' and replace functions in specific instances.
I am using php5.3 (so anonymous functions should work, just not in the way that I am using them).
Thanks so very much for your time!!
-GK
You can use the __call magic function for this job. Not a beauty, but it works..
like this:
class A {
public $B;
public function __call($closure, $args)
{
call_user_func_array($this->$closure, $args);
}
}
$c = new A();
$c->B = function () { echo('HelloWorld'); };
$c->B();
FWIW:
PHP 5.3's treatment of anonymous functions is entertaining. This won't work:
$c->B = function() { echo func_get_arg(0); };
$c->B("This fails :(");
This WILL work:
$c->B = function() { echo func_get_arg(0); };
$hilarious = $c->B;
$hilarious("This works!");
To work around this, you need to use a __call hack like the one provided by Oden.
This behavior may change in the future. The array dereferencing RFC was recently committed to PHP's trunk, and the patch has set off a discussion on function call chaining, the syntax of which may allow what you're trying to do without the __call hack. Unfortunately it's proven difficult in the past to get function call chaining working.
# real ugly, but PoC...
class a {
function __call($f, $x) {
call_user_func_array($this->$f, $x);
}
}
$a = new a;
$a->b = function() { echo "Hello world"; };
$a->b();
Sounds like you are describing a Strategy Pattern or Decorator Pattern - there are other ways to achieve this in way which is more easily communicated with other developers who read your code.
You can do something along these lines (which will also work with callbacks that are not closures):
<?php
class A {
private $fun;
function setFun($fun) {
if (!is_callable($fun))
throw new InvalidArgumentException();
$this->fun = $fun;
}
public function fun() {
call_user_func_array($this->fun, func_get_args());
}
}
$c = new A();
$c->setFun(function($a) { echo('HelloWorld ' . $a);});
$c->fun("here");
which gives HelloWorld here.
That said, you should also consider inheritance or the decorator pattern.
This is not an issue anymore by PHP 7;
// no error
$result = ($this->anonFunc)();
$result = ($this->anonFunc)($arg1, $arg2, ...);
See more about AST.
Rather than hooking a __call magic method into your class, you can instead execute the callable directly using call_user_func.
class A {
public $b;
}
$c = new A();
$c->b = function(){echo('HelloWorld');};
call_user_func($c->b); // HelloWorld
Obviously it would be nice for PHP to provide some syntax to execute this directly.
Related
I would like to be able to call a closure that I assign to an object's property directly without reassigning the closure to a variable and then calling it. Is this possible?
The code below doesn't work and causes Fatal error: Call to undefined method stdClass::callback().
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
$obj->callback();
As of PHP7, you can do
$obj = new StdClass;
$obj->fn = function($arg) { return "Hello $arg"; };
echo ($obj->fn)('World');
or use Closure::call(), though that doesn't work on a StdClass.
Before PHP7, you'd have to implement the magic __call method to intercept the call and invoke the callback (which is not possible for StdClass of course, because you cannot add the __call method)
class Foo
{
public function __call($method, $args)
{
if(is_callable(array($this, $method))) {
return call_user_func_array($this->$method, $args);
}
// else throw exception
}
}
$foo = new Foo;
$foo->cb = function($who) { return "Hello $who"; };
echo $foo->cb('World');
Note that you cannot do
return call_user_func_array(array($this, $method), $args);
in the __call body, because this would trigger __call in an infinite loop.
You can do this by calling __invoke on the closure, since that's the magic method that objects use to behave like functions:
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
$obj->callback->__invoke();
Of course that won't work if the callback is an array or a string (which can also be valid callbacks in PHP) - just for closures and other objects with __invoke behavior.
As of PHP 7 you can do the following:
($obj->callback)();
Since PHP 7 a closure can be called using the call() method:
$obj->callback->call($obj);
Since PHP 7 is possible to execute operations on arbitrary (...) expressions too (as explained by Korikulum):
($obj->callback)();
Other common PHP 5 approaches are:
using the magic method __invoke() (as explained by Brilliand)
$obj->callback->__invoke();
using the call_user_func() function
call_user_func($obj->callback);
using an intermediate variable in an expression
($_ = $obj->callback) && $_();
Each way has its own pros and cons, but the most radical and definitive solution still remains the one presented by Gordon.
class stdKlass
{
public function __call($method, $arguments)
{
// is_callable([$this, $method])
// returns always true when __call() is defined.
// is_callable($this->$method)
// triggers a "PHP Notice: Undefined property" in case of missing property.
if (isset($this->$method) && is_callable($this->$method)) {
return call_user_func($this->$method, ...$arguments);
}
// throw exception
}
}
$obj = new stdKlass();
$obj->callback = function() { print "HelloWorld!"; };
$obj->callback();
It seems to be possible using call_user_func().
call_user_func($obj->callback);
not elegant, though.... What #Gordon says is probably the only way to go.
Well, if you really insist. Another workaround would be:
$obj = new ArrayObject(array(),2);
$obj->callback = function() {
print "HelloWorld!";
};
$obj['callback']();
But that's not the nicest syntax.
However, the PHP parser always treats T_OBJECT_OPERATOR, IDENTIFIER, ( as method call. There seems to be no workaround for making -> bypass the method table and access the attributes instead.
I know this is old, but I think Traits nicely handle this problem if you are using PHP 5.4+
First, create a trait that makes properties callable:
trait CallableProperty {
public function __call($method, $args) {
if (property_exists($this, $method) && is_callable($this->$method)) {
return call_user_func_array($this->$method, $args);
}
}
}
Then, you can use that trait in your classes:
class CallableStdClass extends stdClass {
use CallableProperty;
}
Now, you can define properties via anonymous functions and call them directly:
$foo = new CallableStdClass();
$foo->add = function ($a, $b) { return $a + $b; };
$foo->add(2, 2); // 4
well, it should be emphisized that storing the closure in a variable, and call the varible is actually (wierdly) faster, depending on the call amount, it becomes quite a lot, with xdebug (so very precise measuring), we are talking about 1,5 (the factor, by using a varible, instead of directly calling the __invoke. so instead , just store the closure in a varible and call it.
Here's another alternative based on the accepted answer but extending stdClass directly:
class stdClassExt extends stdClass {
public function __call($method, $args)
{
if (isset($this->$method)) {
$func = $this->$method;
return call_user_func_array($func, $args);
}
}
}
Usage example:
$foo = new stdClassExt;
$foo->blub = 42;
$foo->whooho = function () { return 1; };
echo $foo->whooho();
You are probably better off using call_user_func or __invoke though.
Updated:
$obj = new stdClass();
$obj->callback = function() {
print "HelloWorld!";
};
PHP >= 7 :
($obj->callback)();
PHP >= 5.4 :
$callback = $obj->callback;
$callback();
If you're using PHP 5.4 or above you could bind a callable to the scope of your object to invoke custom behavior. So for example if you were to have the following set up..
function run_method($object, Closure $method)
{
$prop = uniqid();
$object->$prop = \Closure::bind($method, $object, $object);
$object->$prop->__invoke();
unset($object->$prop);
}
And you were operating on a class like so..
class Foo
{
private $value;
public function getValue()
{
return $this->value;
}
}
You could run your own logic as if you were operating from within the scope of your object
$foo = new Foo();
run_method($foo, function(){
$this->value = 'something else';
});
echo $foo->getValue(); // prints "something else"
I note that this works in PHP5.5
$a = array();
$a['callback'] = function() {
print "HelloWorld!";
};
$a['callback']();
Allows one to create a psuedo-object collection of closures.
I have a small problem. I've tried searching, but I can't get the search terms quite right and was hoping someone could help.
I have an include on every page in my system that works something like this:
<?PHP
require_once("class.system.php");
require_once("class.mysql.php");
$oMySQL = new MySQL();
$oSystem = new SystemClass();
... ?>
But I have a problem. As you may guess - the MySQL class is a bunch of functions that I use to make MySQL calls easier. This isn't the only example of where I want to use it but it's a good example.
I have functions in the system class I want to be able to reference the MySQL class (And vice versa...).
As an example, I have a function in the system class that will populate a session variable with data from MySQL. The only way I can think of doing this (Which I know is wrong...) is:
class SystemClass {
function PopulateSession(){
global $oMySQL;
if($oMySQL->Select('abc')){
$_SESSION['def']= blahblahblah;
return true;
} else {
return false;
}
}
}
It works, but it means every function I want to use it, I have to use global, which I'm sure is very bad practice. Could someone advise??
Thanks
What you encountered is called composition. A good solution would be to use a dependency injection framework. An easy solution is to roll with constructor parameters.
public class A {
private $b;
public function __construct($b) {
$this->b = $b;
}
}
$b = new B;
$a = new A($b);
Or, as a more flexible solution, when you have mutual dependencies:
public class A {
private $b;
public function setB($b) {
$this->b = $b;
}
}
public class B {
private $a;
public function setA($a) {
$this->a = $a;
}
}
$a = new A;
$b = new B;
$a->setB($b);
$b->setA($a);
But the downside is that as the number of dependencies grows, it's hard to manage and remember to set all the dependencies. This is exactly the reason why Dependency Injection frameworks are popular.
ok I do have the code below
<?php
class foo{
public $bar = NULL;
public function boo(){
$this->bar();
}
}
$mee = new foo();
//save a closure function on the property
$mee->bar = function(){
echo 'hahaha';
};
//invoke the closure function by using a class method
$mee->boo();
?>
and you can see it running here http://codepad.org/s1jhi7cv
now what i want here is to store the closure function on the class method.
well closures are possible as i read the documentation about it here http://php.net/manual/en/functions.anonymous.php
is this possible? did i went to something wrong? please correct me
Your example code at codepad.org does not work because codepad.org uses PHP 5.2.5, and closure support was only added in 5.3.
However, your code will also not work in a PHP version that supports closures, although you will get a different error: http://codepad.viper-7.com/Ob0bH5
This is a limitation of PHP at present. $obj->member() looks for a method named member and will not look at properties to see if they are callable. It is, frankly, annoying.
The only way I am aware of to make this work without call_user_func()/call_user_func_array() is:
public function boo() {
$func = $this->bar;
$func();
}
You need to exploit some magic functionality of PHP (__call) to make use of that. Extend from Extendable for example:
class Extendable {
static function import($context) {
$self = new static();
while (is_callable($context)) $context = $context($self);
if (is_array($context) || is_object($context) || is_a($context, 'Traversable')) {
foreach($context as $key => $value)
$self->$key = &$value; # preserve keys if
}
return $self;
}
public function __call($name, $args) {
if (isset($this->$name) && is_callable($this->$name)) {
return call_user_func_array($this->$name, $args);
}
throw new BadFunctionCallException(sprintf('Undefined function %s.', $name));
}
}
And you can do the job. It's not that nice. Background and examples are in one of my blog posts:
PHP: Extending stdClass with Closures (plus Visitor)
You can naturally implement that magic functionality your own, too.
Use call_user_func() function:
<?php
class foo{
public $bar = NULL;
public function boo(){
call_user_func($this->bar);
}
}
$mee = new foo();
//save a closure function on the property
$mee->bar = function(){
echo 'hahaha';
};
//invoke the closure function by using a class method
$mee->boo();
This will display "ahahah"
Hope it helps.
You will not be able to do that.
Take for example this code:
class T {
function foo() {
echo 'T::foo';
}
}
$t = new T;
$t->foo = function() {
echo 'Closure::foo';
};
$t->foo();
It works fine on PHP 5.4.6 and/or PHP 5.3.16, however it will result in T::foo getting printed.
This happens because methods, in PHP, are not modifiable class properties, as they are for example in javascript.
However,
$foo = $t->foo;
$foo();
will print Closure::foo as expected.
PHP is not a prototype based language hence you cannot redefine functions
Use __call to catch all non-defined methods and then look up the closure and invoke it. Take a look at my post on this SitePoint thread.
Starting at php 7, you can put round brackets around the instance and method to call the method like so: ($this->bar)();.
This appears to cause a syntax error on earlier versions however.
Is it possible to add methods to functions?
For example:
<?
function func(){
;
}
//add method
func->test = function(){
;
}
func->test();
func();
I'm coming from a javascript background, and therefore I'm used to 'everything is an object'.
EDIT:
I was just explaining where the misconception may often come from for new phpers. I understand the above code doesn't work.
EDIT 2
Figured it out.
class myfunc_class{
function __invoke(){
//function body
}
function __call($closure, $args)
{
call_user_func_array($this->$closure, $args);
}
}
$func = new myfunc_class;
$func->test = function(){
echo '<br>test<br>';
};
$func->test();
$func();
Even sexier :)
class func{
public $_function;
function __invoke(){
return call_user_func_array($this->_function,func_get_args());
}
function __construct($fun){
$this->_function = $fun;
}
function __call($closure, $args)
{
call_user_func_array($this->$closure, $args);
}
}
$func = new func(function($value){
echo $value;
});
$func->method = function(){
echo '<br>test<br>';
};
$func('someValue');
$func->method();
No.
Not everything is an object in PHP. In fact the only thing that is an object is, well, an object. More specifically, and generally, an instantiation of a class.
Your code converted to PHP
// function_object.php
<?php
class FunctionObject {
public method func() {
// do stuff
}
}
?>
In other code you would use it like this:
<?php
// example.php in same folder as function_object.php
include 'function_object.php';
$FuncObj = new FunctionObject;
$FuncObj->func();
Also: read more about PHP & OOP
No, because an object is a different PHP language construct than a function. Functions do not have properties, but are instead simply execution instructions.
But, if func were instead a pre-defined class, then yes... with a bit of witchcraft, ignoring public outcry, foregoing readability and PHP coding standards, and by using closures with the __call() magic method...
class func
{
function __call($func, $args)
{
return call_user_func_array($this->$func, $args);
}
}
$obj = new func;
$obj->test = function($param1, $param2)
{
return $param1 + $param2;
};
echo $obj->test(1,1);
This won't work as you'd think without __call(), because by $obj->test(1,1), PHP thinks you're trying to call a non-existent method of func when out of object scope. But inside, being that the new "test" property is of a type: closure, the call_user_func_array() just sees the "test" property as just another function, so you can hide this bit of trickery from outside scope.
You would need your function func() to return an object, then you'd be able to do something like: func()->test();
But please note that your way of handling objects is not right in PHP and I suggest that you go read the OO documentations here.
In difference to javacript, in PHP not everything is an object. Therefore you need to differ between function and class.
If you want to create an object, you need to define the class first.
class myClass {
}
You can then add as many functions to the class as you need. But you need to define them first:
class myClass {
function test() {
echo "test!\n";
}
}
When everything is ready, you can bring it to life then:
$class = new myClass;
$class->test();
Checkout the manual for more.
You can't do what you're trying to do, but you can define functions inside of other functions.
This example outputs text:
function a() {
function b() { echo 'Hi'; }
}
a();
b();
Output: HiHi
This example outputs an error:
function a() {
function b() { echo 'Hi'; }
}
b();
Output: ERROR
php 5.3
Is there a way to do this (viable in java for example)
(new MyClass())->myMethod();
i am receving: Parse error: syntax error, unexpected T_OBJECT_OPERATOR in D.. on line 7
Add
I really need that RFC to be implemented in the next PHP version!
http://wiki.php.net/rfc/instance-method-call
Is there a way we can subscribe to it so it can get more attention?
No, its not possible. There is a RFC for that
http://wiki.php.net/rfc/instance-method-call
But no one knows, when this will come to the userland.
Jacob mentioned the static method. There are other more or less useful methods to achieve the same
function instanciate($className, $arg1 = null) {
$args = func_get_args();
array_shift($args);
$c = new ReflectionClass($className);
return $c->newInstanceArgs($c);
}
instanciate('Classname', 1, 2, 3)->doSomething();
However, I prefer the temporary variable (like in the question).
Update:
I can swear there where an example for the temporary variable stuff in the question in the past. However, I meant this
$x = new Class;
$x->method();
where $x is the temporary variable.
That is not valid syntax. A handy way to achieve what you want is to use a static method to create the object.
In your MyClass:
public static function create() {
return new MyClass();
}
Then you can use:
MyClass::create()->myMethod();
However it is extra code that you have to maintain, if for example the constructor is changed or the class is extended. So you need to weigh up the benefits.
You can do something like this:
function chain_statements($statement1, $statement2) { return $statement2; }
class TClass { public Method() { ...; return $this; } }
$b = chain_statements($a = new TClass(), $a->Method());
... or more generalized:
function chain_statements(array $statements) { return end($statements); }
For example:
function chain_statements($statement1, $statement2) { return $statement2; }
function chain_statements2(array $statements) { return end($statements); }
class TClass
{
public $a = 0;
public function Method1() { $this->a = $this->a + 1; return $this; }
public function Method2() { $this->a = $this->a + 2; return $this; }
}
$b = chain_statements($c = new TClass(), $c->Method1()); echo($b->a);
$b = chain_statements2(array($c = new TClass(), $c->Method1(), $c->Method2())); echo($b->a);
... or even better:
function call_method($object) { return $object; }
$b = call_method(new TClass())->Method2(); echo($b->a);
Not as such. In PHP new is not an expression, but a language construct. The common workaround is to provide a static instantiation method for MyClass::get()->... use.
A more concise alternative is a hybrid factory function:
function MyClass() { return new MyClass; }
class MyClass {
...
}
Which then simplifies the instantiation to MyClass()->doSomething();
You can put it in one statement if you really wanted to. Use eval() ;p
But you probably shouldn't.
I had this same problem a while ago but I found this simple solution which is pretty readable too. I like the fact it uses only the standard PHP functions. There's no need to create any utility functions of your own.
call_user_func(
array(new ClassToInstance(), 'MethodName'),
'Method arguments', 'go here'
);
You can also use call_user_func_array to pass the arguments as an array.
call_user_func_array(
array(new ClassToInstance(), 'MethodName'),
array('Method arguments', 'go here')
);