Can i bind method of class Foo to class Bar? And why the code below throws a warning "Cannot bind method Foo::say() to object of class Bar"? With function instead of method code works fine.
P.S. I know about extending) it is not practical question, just want to know is it real to bind non-static method to another class
class Foo {
public $text = 'Hello World!';
public function say() {
echo $this->text;
}
}
class Bar {
public $text = 'Bye World!';
public function __call($name, $arguments) {
$test = Closure::fromCallable(array(new Foo, 'say'));
$res = Closure::bind($test, $this);
return $res();
}
}
$bar = new Bar();
$bar->say();
Code below works fine
function say(){
echo $this->text;
}
class Bar {
public $text = 'Bye World!';
public function __call($name, $arguments) {
$test = Closure::fromCallable('say');
$res = Closure::bind($test, $this);
return $res();
}
}
$bar = new Bar();
$bar->say();
This is currently not supported. If you want to bind a closure to a new object, it must not be a fake closure, or the new object must be compatible with the old one (source).
So, what is a fake closure: A fake closure is a closure created from Closure::fromCallable.
This means, you have two options to fix your problem:
Bar must be compatible with the type of Foo - so just make Bar
extend from Foo, if possible.
Use unbound functions, like annonymous, static or functions outside of classes.
It is not supported by PHP. However in PHP 7.0 it was kinda possible. Consider this example:
class Foo {
private $baz = 1;
public function baz() {
var_dump('Foo');
var_dump($this->baz);
}
}
class Bar {
public $baz = 2;
public function baz() {
var_dump('Bar');
var_dump($this->baz);
}
}
$fooClass = new ReflectionClass('Foo');
$method = $fooClass->getMethod('baz');
$foo = new Foo;
$bar = new Bar;
$closure = $method->getClosure($foo);
$closure2 = $closure->bindTo($bar);
$closure2();
The output of foregoing code is:
string(3) "Foo"
int(2)
And this means method Foo::baz was called on object $bar and has accessed its $baz property rather than Foo::$baz.
Also, please note that Bar::$baz is public property. If it was private, php would throw Fatal error cannot access private property. This could've be fixed up by changing the scope of closure $closure->bindTo($bar, $bar);, however doing this was already prohibited in php7.0 and would lead to a following warning: Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure().
There's workaround however, which will work for latest versions of php. Laravel created a splendid package called laravel/serializable-closure. What it does is simple - it reads source code of closure in order to serialize it and be able to unserialize it later with all necessary context.
So basically the functionality of unserialized closure remains the same, however it is different from PHP's perspective. PHP doesn't know it was created from class method and therefore allows to bind any $this.
Final variant will look like this:
$callback = [$foo, 'baz'];
$closure = unserialize(
serialize(new SerializableClosure(Closure::fromCallable($callback)))
)->getClosure();
$closure->call($bar);
Please, note that serialization and unserialization are expensive operations, so do not use provided solution unless there's no other solution.
Related
Can i bind method of class Foo to class Bar? And why the code below throws a warning "Cannot bind method Foo::say() to object of class Bar"? With function instead of method code works fine.
P.S. I know about extending) it is not practical question, just want to know is it real to bind non-static method to another class
class Foo {
public $text = 'Hello World!';
public function say() {
echo $this->text;
}
}
class Bar {
public $text = 'Bye World!';
public function __call($name, $arguments) {
$test = Closure::fromCallable(array(new Foo, 'say'));
$res = Closure::bind($test, $this);
return $res();
}
}
$bar = new Bar();
$bar->say();
Code below works fine
function say(){
echo $this->text;
}
class Bar {
public $text = 'Bye World!';
public function __call($name, $arguments) {
$test = Closure::fromCallable('say');
$res = Closure::bind($test, $this);
return $res();
}
}
$bar = new Bar();
$bar->say();
This is currently not supported. If you want to bind a closure to a new object, it must not be a fake closure, or the new object must be compatible with the old one (source).
So, what is a fake closure: A fake closure is a closure created from Closure::fromCallable.
This means, you have two options to fix your problem:
Bar must be compatible with the type of Foo - so just make Bar
extend from Foo, if possible.
Use unbound functions, like annonymous, static or functions outside of classes.
It is not supported by PHP. However in PHP 7.0 it was kinda possible. Consider this example:
class Foo {
private $baz = 1;
public function baz() {
var_dump('Foo');
var_dump($this->baz);
}
}
class Bar {
public $baz = 2;
public function baz() {
var_dump('Bar');
var_dump($this->baz);
}
}
$fooClass = new ReflectionClass('Foo');
$method = $fooClass->getMethod('baz');
$foo = new Foo;
$bar = new Bar;
$closure = $method->getClosure($foo);
$closure2 = $closure->bindTo($bar);
$closure2();
The output of foregoing code is:
string(3) "Foo"
int(2)
And this means method Foo::baz was called on object $bar and has accessed its $baz property rather than Foo::$baz.
Also, please note that Bar::$baz is public property. If it was private, php would throw Fatal error cannot access private property. This could've be fixed up by changing the scope of closure $closure->bindTo($bar, $bar);, however doing this was already prohibited in php7.0 and would lead to a following warning: Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure().
There's workaround however, which will work for latest versions of php. Laravel created a splendid package called laravel/serializable-closure. What it does is simple - it reads source code of closure in order to serialize it and be able to unserialize it later with all necessary context.
So basically the functionality of unserialized closure remains the same, however it is different from PHP's perspective. PHP doesn't know it was created from class method and therefore allows to bind any $this.
Final variant will look like this:
$callback = [$foo, 'baz'];
$closure = unserialize(
serialize(new SerializableClosure(Closure::fromCallable($callback)))
)->getClosure();
$closure->call($bar);
Please, note that serialization and unserialization are expensive operations, so do not use provided solution unless there's no other solution.
Why some developers create one method that returns new static? What is the reason to have a method that returns new static? I am not asking what is the difference between static and self, or what static & self mean. For example, here is one simple class:
<?php
class Expression
{
public static function make()
{
return new static;
}
public function find($value)
{
return '/' . $value .'/';
}
public function then($value)
{
return $this->find($value);
}
public function hi($value)
{
return "hi";
}
}
As you can see, there is a static method make() which returns new static. Then, some developers call other methods like this:
$regex = Expression::make()->find('www');
What is the purpose of this? I see that here we don't use new Expression syntax, and if that's the point - then why not make all methods static? What is the difference, what is the reason to have that one method that returns new static (while other methods are not static)?
new static instantiates a new object from the current class, and works with late static bindings (instantiates the subclass if the class was subclassed, I expect you understand that).
Having a static method on a class which returns a new instance of same is an alternative constructor. Meaning, typically your constructor is public function __construct, and typically it requires a certain bunch of parameters:
class Foo {
public function __construct(BarInterface $bar, array $baz = []) { ... }
}
Having an alternative constructor allows you to provide different defaults, or convenience shortcuts to instantiate this class without having to supply those specific arguments and/or for being able to provide different arguments which the alternative constructor will convert to the canonical ones:
class Foo {
public function __construct(BarInterface $bar, array $baz = []) { ... }
public static function fromBarString($bar) {
return new static(new Bar($bar));
}
public static function getDefault() {
return new static(new Bar('baz'), [42]);
}
}
Now, even though your canonical constructor requires a bunch of complex arguments, you can create a default instance of your class, which will probably be fine for most uses, simply with Foo::getDefault().
The canonical example in PHP for this is DateTime and DateTime::createFromFormat.
In your concrete example the alternative constructor doesn't actually do anything, so it's rather superfluous, but I expect that's because it's an incomplete example. If there's indeed an alternative constructor which does nothing other than new static, it's probably just meant as convenience syntax over (new Foo)->, which I find questionable.
Complete answer here
TLDR
get_called_class().
class A {
public static function get_self() {
return new self();
}
public static function get_static() {
return new static();
}
}
class B extends A {}
echo get_class(A::get_self()); // A
echo get_class(A::get_static()); // A
echo get_class(B::get_self()); // A
echo get_class(B::get_static()); // B
Below are the examples of php class code that is static method and non static method.
Example 1:
class A{
//None Static method
function foo(){
if (isset($this)) {
echo '$this is defined (';
echo get_class($this);
echo ")<br>";
} else {
echo "\$this is not defined.<br>";
}
}
}
$a = new A();
$a->foo();
A::foo();
//result
$this is defined (A)
$this is not defined.
Example 2:
class A{
//Static Method
static function foo(){
if (isset($this)) {
echo '$this is defined (';
echo get_class($this);
echo ")<br>\n";
} else {
echo "\$this is not defined.<br>\n";
}
}
}
$a = new A();
$a->foo();
A::foo();
//result
$this is not defined.
$this is not defined.
I am trying to figure out what is the difference between these two Classes.
As we can see on the result of the none static method, the "$this" was defined.
But on the other hand the result on the static method was not defined even they were both instantiated.
I am wondering why they have different result since they were both instantiated?
Could you please enlighten me on what is happening on these codes.
Before we go any further: Please, get into the habbit of always specifying the visibility/accessibility of your object's properties and methods. Instead of writing
function foo()
{//php 4 style method
}
Write:
public function foo()
{
//this'll be public
}
protected function bar()
{
//protected, if this class is extended, I'm free to use this method
}
private function foobar()
{
//only for inner workings of this object
}
first off, your first example A::foo will trigger a notice (calling a non-static method statically always does this).
Secondly, in the second example, when calling A::foo(), PHP doesn't create an on-the-fly instance, nor does it call the method in the context of an instance when you called $a->foo() (which will also issue a notice BTW). Statics are, essentially, global functions because, internally, PHP objects are nothing more than a C struct, and methods are just functions that have a pointer to that struct. At least, that's the gist of it, more details here
The main difference (and if used properly benefit) of a static property or method is, that they're shared over all instances and accessible globaly:
class Foo
{
private static $bar = null;
public function __construct($val = 1)
{
self::$bar = $val;
}
public function getBar()
{
return self::$bar;
}
}
$foo = new Foo(123);
$foo->getBar();//returns 123
$bar = new Foo('new value for static');
$foo->getBar();//returns 'new value for static'
As you can see, the static property $bar can't be set on each instance, if its value is changed, the change applies for all instances.
If $bar were public, you wouldn't even need an instance to change the property everywhere:
class Bar
{
public $nonStatic = null;
public static $bar = null;
public function __construct($val = 1)
{
$this->nonStatic = $val;
}
}
$foo = new Bar(123);
$bar = new Bar('foo');
echo $foo->nonStatic, ' != ', $bar->nonStatic;//echoes "123 != foo"
Bar::$bar = 'And the static?';
echo $foo::$bar,' === ', $bar::$bar;// echoes 'And the static? === And the static?'
Look into the factory pattern, and (purely informative) also peek at the Singleton pattern. As far as the Singleton pattern goes: Also google why not to use it. IoC, DI, SOLID are acronyms you'll soon encounter. Read about what they mean and figure out why they're (each in their own way) prime reasons to not go for Singletons
Sometimes you need to use a method but you don't want call class, because some functions in a class called automatically like as __construct, __destruct,... (Magic Methods).
called class:
$a = new A();
$a->foo();
Don't called class: (just ran foo() function)
A::foo();
http://php.net/manual/en/language.oop5.magic.php
Am I missing something or do closures simply not work as class methods? Take this for instance:
$foo = new stdClass();
$foo->bar = function() {
echo '###';
};
$foo->bar();
Seems to give me an error of "Fatal error: Call to undefined method stdClass::bar() in /blah/blah.php on line X"
Shouldn't this instead invoke the closure that was placed in the "bar" property?
Yes, that is indeed correct.
The only way to call bar is:
$bar = $foo->bar;
$bar();
Sad, but true.
Also worth noting, because of this same effect, there is no $this inside $bar call (unless you pass it as function argument named as $this).
Edit: As nikic pointed out, the value of $this inside the closure is the same value of the scope of when the closure was created.
This may mean that $this might be undefined on two occasions: when the scope was the global PHP scope or when the scope was from a static context. This, however, means that you can in theory feed the correct instance:
class Foo {
public $prop = 'hi';
function test(){
$this->bar = function(){
echo $this->prop;
}
$bar = $this->bar;
$bar();
}
}
$foo = new Foo();
$foo->test();
Also, it seems that with some class magic, you can achieve $this->bar() as well:
class Foo {
// ... other stuff from above ...
public function __call($name, $args){
$closure = $this->$name;
call_user_func_array( $closure, $args ); // *
}
}
[*] Beware that call_user_func_array is very slow.
Oh, and this is strictly for PHP 5.4 only. Before that, there's no $this in closures :)
Also, you can see it in action here.
Methods and fields are completely separate; in fact, you can even have a method and field of the same name:
<?php
class foo{
function bar() { echo "hello\n"; }
}
$object = new foo;
$object->bar = 1;
$object->bar(); // echoes "hello"
?>
This explains why your syntax could not have created a "real" method.
Is it possible to set the parent of the class? I.e. an instance of the parent class gets instantiated during runtime and then a child class instance extending a certain parent instance gets created. For example:
class A {
var $test = 'default';
}
class B extends A {
public function __contsruct(A $a) {
parent = $a; //does not work
}
}
$a = new A();
$a->test = 'changed';
$b = new B($a);
$b->test == 'changed'; //should be true
I know that I could just $b = new B(); $b->test = 'changed', but that's not what I'm asking about.
A simple way to accomplish this is like so:
class Foo
{
function __construct() {
$this->hello = "Hello";
}
public function sayHello() {
echo $this->hello."!";
}
}
class Bar
{
function __construct(&$foo) {
$this->foo = $foo;
}
public function sayHelloAgain() {
echo $this->foo->sayHello()." Again!";
}
}
$foo = new Foo();
echo $foo->sayHello(); //outputs "Hello!"
$bar = new Bar($foo);
echo $bar->sayHelloAgain(); //outputs "Hello! Again!"
What you've asked for is not possible in base PHP. There are a few ways to do similar things.
You could use the highly experimental runkit extension. The runkit_class_emancipate and runkit_class_adopt functions should work. However, they operate on entire classes, not instances of a class. This probably limits their usefulness for your application.
If you're trying to emulate the expandable class features of other languages, like Ruby and Perl, runkit_method_add and related functions might be more suitable. Again, however, it still operates on entire classes.
The normally accepted "PHP way" to do things like this is via __call. In fact, with anonymous functions in 5.3, you can do something like...
class Bar {
public function say($thing) {
echo "Bar::say says: $thing\n";
}
}
class Foo extends Bar {
private $extensions = array();
public function addExtension($func_name, $func) {
$this->extensions[ $func_name ] = $func;
}
public function __call($func_name, $arguments) {
array_unshift($arguments, $this);
if(array_key_exists($func_name, $this->extensions))
call_user_func_array($this->extensions[ $func_name ], $arguments);
}
}
$f = new Foo();
$anon = function($obj, $string){ $obj->say($string); };
$f->addExtension('example', $anon);
$f->example("Hello, world!");
You'll note in __call and that in creating the anonymous function that the first argument becomes the instance. That's because PHP 5.3's implementation of anonymous functions can't reference $this. That also means that they can't reference protected or private members of the class. This can be corrected by cloning the instance and using Reflection to expose the protected and private members. See this comment on the anonymous function manual page for an example implementation.
Because of limitations of PHP, you can't directly assign an anonymous function to a property and call it. This example will not work:
class WillNotWork {
public $fatal_error;
}
$broken = new WillNotWork();
$anon = function($arg) { echo "I'm about to create a {$arg} error!"; };
$broken->fatal_error = $anon;
$broken->fatal_error("fatal");
// Fatal error: Call to undefined method WillNotWork::fatal_error()
No, because $a is a separate instance than $b.