PHP How to refer to a class method definition? - php

I would like to bind a function (a method of a class) to another class. Any idea of how i could achieve this?
Here is an example of what i want:
class A {
protected $prop = "prop A";
function method($arg1, ...) {
return $this->prop;
}
}
class B {
protected $prop = "prop B";
// need help here
}
So i want to "bind" the method "method" of class "A" to class "B" so it'll be possible to do $b = new B(); $b->method($arg1, ...); and obtain "prop B";
Thanks in advance!!
I tried:
class B {
protected $instance;
protected $prop = "prop B";
public function __construct($instance) {
$this->instance = $instance;
}
public function __call($method, $args) {
return call_user_func_array([$this->instance, $method], $args);
}
}
$b = new B(new A());
$b->method();
But still outputing "prop A";
I tried this too:
class B {
protected $prop = "prop B";
public function __call($method, $args) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
}
$a = new A();
$b = new B();
$b->method = $a->method;
$b->method();
But i'm getting this error: Closure::bind() expects parameter 1 to be Closure, null given....
At last i tried this too:
class B {
protected $instance;
protected $prop = "prop B";
public function __construct($instance) {
$this->instance = $instance;
}
public function __call($method, $args) {
$new_method = $this->instance->$method->bindTo($this);
return call_user_func_array($new_method, $args);
}
}
$b = new B(new A());
$b->method();
Here, an error says $this->instance->$method is null

Using dependency injection, you can access the passed object:
class A {
public function speak() { return ‘meow’; }
}
class B {
public function __construct($obj) {
$this->a = $obj;
}
}
$demo = new B( new A));
print $demo->a->speak();
// => meow
From here, if you want B->speak to refer to A->speak:
class A {
public function speak() { return ‘meow’; }
}
class B {
public function __construct($obj) {
$this->a = $obj;
}
public function speak() {
return $this->a->speak();
}
}
$demo = new B( new A));
print $demo->a->speak(); // meow
print $demo->speak(); // meow
Or, if B is a special kind of A:
// use obj A above
class B extends A {
// do stuff B knows
}
$demo = new B;
print $demo->speak();
// => meow
If you’re wanting the exact same method in both classes, perhaps what you’re looking for is traits. Traits are more or less an include for objects which lets objects “share” code. (Personally I think it’s a fancy way of violating DRY and is better handled with DI... but smarter people than I have included it in the language)
Using traits would be something like this (double check docs, I’ve never used this)
trait sharedMethod {
public function speak() {
return $this->prop;
}
}
class A {
use sharedMethod;
public $prop = “I’m from A”;
}
class B {
use sharedMethod;
public $prop = “I’m from B”;
public function __construct(object $a) {
$this->a = $a;
}
/**
* use this to get A’s method, or omit to keep B’s method
*/
public function speak() {
return $this->a->speak();
}
}
$demo = new B( new A));
print $demo->speak(); // I’m from A
// if you don’t override the method
$demo = new B( new A));
print $demo->speak(); // I’m from B

Related

How can I cast class for my object for PHP?

I have object $obj (class A).
Can I convert class for $obj to B?
Perhaps there is another way.
Example:
class A
{
public $AProp = 1;
public function &Convert($ATypeName)
{
// HOW?
return $this;
}
}
class B extends A
{
public $BProp = 2;
}
$obj=new A();
$obj->Convert("B");
print_r($obj->BProp);
I wrote next solution, but it is no good.
(It looks like your post is mostly code; I add some more details)
class A
{
public $AProp = 1;
public function &Convert($ATypeName)
{
$Result = new $ATypeName; // Create new object
$Result->AProp = $this->AProp; // Copy params...
return $Result;
}
}
class B extends A
{
public $BProp = 2;
}
$obj = new A();
$obj->AProp = 3;
$obj = $obj->Convert("B");
print_r($obj);
u are trying to use c++-way of class extending.
the php-way is smth like this:
<?php
class A
{
protected $prop = 1;
public function getProp()
{
return $this->prop;
}
}
class B extends A
{
protected $prop = 2;
}
$obj=new A();
print_r($obj->getProp());
$obj=new B();
print_r($obj->getProp());
also take a look on late static bindings - http://php.net/manual/en/language.oop5.late-static-bindings.php
<?php
class A
{
public static $prop = 1;
public function getProp()
{
return static::$prop;
}
}
class B extends A
{
public static $prop = 2;
}
$obj=new A();
print_r($obj->getProp());
$obj=new B();
print_r($obj->getProp());
This is the only way:
$obj = new B();
Since inheritance is used you can access all class A func and var, like $this->Aprop

Dependency injection an object without all of its methods

so a class:
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
its beign created and updated:
$obj = new ToBeUsed();
$obj->setSomething('a');
and passed to another object
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //// !!!!! THIS IS BAD!
}
}
now a classic DI example, except that the passed object should be "dulled" - only some methods are allowed to use. E.g. getSomething() is allowed to use, but setSomething() is not. What pattern / practice can get away with it? There used to be friend classes is C but its Php...
class ToBeUsed
{
private $a;
public function setSomething($a)
{
$dbg = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,2);
if(count($dbg) > 1){
return;
}
$this->a = $a;
}
public function getSomething()
{
return $this->a;
}
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsed $obj)
{
$this->obj = $obj;
}
public function work()
{
echo $this->obj->getSomething().PHP_EOL; // a
$this->obj->setSomething('b'); // this does nothing
echo $this->obj->getSomething().PHP_EOL; // a
}
}
$obj = new ToBeUsed();
$obj->setSomething('a');
$obj2 = new UseIt($obj);
$obj2->work();
Alternatively, you can perform more complex checks on debug_backtrace() output.
I would probably do something with Interfaces, it doesn't prevent a method form being used. But "they" (whoever they is) would be using it outside of the Interface for $obj.
Like this:
class ToBeUsed implements ToBeUsedInterface
{
private $a;
public function getSomething()
{
return $this->a;
}
public function setSomething($a)
{
$this->a = $a;
}
}
interface ToBeUsedInterface{
public function getSomething();
}
class UseIt
{
/**
* #var ToBeUsed
*/
private $obj;
public function __construct(ToBeUsedInterface $obj)
{
$this->obj = $obj;
}
public function work()
{
$this->obj->getSomething();
$this->obj->setSomething(); //This now exists outside of the interface for $obj
}
}
In terms of IDE's this would prevent the methods from auto-completing as well.
The only other thing I can think of, ( besides the other answer ) would be to set the method to protected and then use ReflectionMethod to change the viability, when you want to use it.
Another Option, is Using Reflection
class ToBeUsed
{
private $a;
public function getSomething()
{
return $this->a;
}
protected function setSomething($a)
{
$this->a = $a;
}
}
$ToBeUsed = new ToBeUsed();
$ReflectionMethod = new ReflectionMethod($ToBeUsed, 'setSomething');
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invoke($ToBeUsed, 'foo');
echo $ToBeUsed->getSomething();
Outputs:
foo
You can see it live here
And Obviously sense it's protected under normal conditions, it could not be used inside UseIt. If I was going to use this for any amount of code, I would extend or wrap the Reflection class. Just to make the call a bit more concise, like this:
class MyReflector
{
public static function invoke($class, $method, ...$args)
{
$ReflectionMethod = new ReflectionMethod($class, $method);
$ReflectionMethod->setAccessible(true);
$ReflectionMethod->invokeArgs($class, $args);
}
}
$ToBeUsed = new ToBeUsed();
MyReflector::invoke($ToBeUsed,'setSomething', 'foo');
Please note I got all fancy with the variadic ...$arg which is for PHP 5.6+ it just lets you do
MyReflector::invoke($ToBeUsed,'setSomething', 'foo', 'bar');
And $args would be ['foo','bar'] in the first example it's just ['foo'] which can be used for invokeArgs for the second argument which takes an array of arguments to pass on to the actual method.

Does this smell if I cant decide which descendant should I use?

abstract class X
{
private $v;
protected function setV($v)
{
$this->v = $v;
}
public function getV()
{
return $v;
}
}
class A extends X
{
public function doIt()
{
parent::setV(1);
}
}
class B extends X
{
public function doIt()
{
parent::setV(2);
}
}
$a = new A();
$a->doIt();
$b = new A();
$b->doIt();
but if I want to use getV(), I can both call
$a->getV() and $b->getV()
which sounds silly. Which one to use? To be honest, I would like to see something like that:
X::getV();
which is not possible, an instance must be exists/
It depends on "what do you want". Firstable, it's possible to use X::getV() method, but you need to make v member and getV method static, as shown below.
<?php
abstract class X
{
private static $v;
protected static function setV($v)
{
self::$v = $v;
}
public static function getV()
{
return self::$v;
}
}
class A extends X
{
public function doIt()
{
self::setV(2);
}
}
class B extends X
{
public function doIt()
{
self::setV(1);
}
}
$a = new A();
$a->doIt();
echo X::getV();
// prints 2
// but be aware, that ANY instance of X children class will change the same X::$v value
$b = new B();
$b->doIt();
echo X::getV();
// prints 1
Static members (like X::$v) are stored only once, they are "binded" to the class, not to the instance of this class.
<?php
class Foo
{
public static $v = 5;
}
$instance1 = new Foo();
$instance2 = new Foo();
echo Foo::$v;
echo $instance1::$v;
echo $instance2::$v;
// prints 5, 5, 5
$instance1::$v = 10;
echo Foo::$v;
echo $instance1::$v;
echo $instance2::$v;
// prints 10, 10, 10

Simulate multiple inheritance in PHP

Assuming I have 2 classes
Class A {
public function doA(){
echo "Im A";
}
}
Class B {
public function doB(){
echo "Im B";
}
}
write Class C, in order that the following code runs:
$c = new C();
$c->doA();
$c->doB();
and outputs:
>> Im A
>> Im B
This was in a test, and the conditions where:
use no static calls
you can't modify class A or class B
so I wrote:
Class C {
public function doA() {
$a = new A();
$a->doA();
}
public function doB() {
$b = new B();
$b->doB();
}
}
So apparently I was wrong as it can be "more optimized"
can someone tell me how to do it?
You could keep instances of A and B instead of instantiating them each time.
class C {
private $a, $b;
public __construct() {
$this->a = new A();
$this->b = new B();
}
public function doA() {
$this->a->doA();
}
public function doB() {
$this->b->doB();
}
}
PHP has no "native" multiple inheritance, but you can achieve something similar to it by using traits.
Trait A {
public function doA(){
echo "Im A";
}
}
Trait B {
public function doB(){
echo "Im B";
}
}
Class C {
use A, B;
}
$c = new C;
$c->doA();
$c->doB();
Note that this would require at least PHP 5.4.
To do it without modifying classes, the best and optimised option would be as follows.
class C {
private $a;
private $b;
public __construct() {
$this->a = new A();
$this->b = new B();
}
public function __call($method, $arguments = array()) {
if(method_exists($this->as, $method)) {
return call_user_func(array($this->a, $method));
}
}
}
The above is also future proof, so adding new methods would also follow.
While you were told not to modify classes A and B, the correct way to do this would be by having B extend A, then having C extend B, like below.
class A {
public function doA(){
echo "Im A";
}
}
class B extends A {
public function doB(){
echo "Im B";
}
}
class C extends B {
}
$c = new C();
$c->doA();
$c->doB();

Copying an instance of a PHP class while preserving the data in a base class?

I have the following three classes:
class a
{ public $test; }
class b extends a { }
class c extends a
{
function return_instance_of_b() { }
}
As you can see, both classes b and c derive from a. In the return_instance_of_b() function in c, I want to return an instance of the class b. Basically return new b(); with one additional restriction:
I need the data from the base class (a) to be copied into the instance of b that is returned. How would I go about doing that? Perhaps some variant of the clone keyword?
You can use the get_class_vars function to retrieve the names of the variables you want to copy, and just loop to copy them.
The variables that are defined are protected so they are visible to get_class_vars in its scope (since c extends a), but not directly accessible outside the class. You can change them to public, but private will hide those variables from get_class_vars.
<?php
class a
{
protected $var1;
protected $var2;
}
class b extends a
{
}
class c extends a
{
function __construct()
{
$this->var1 = "Test";
$this->var2 = "Data";
}
function return_instance_of_b()
{
$b = new b();
// Note: get_class_vars is scope-dependant - It will not return variables not visible in the current scope
foreach( get_class_vars( 'a') as $name => $value) {
$b->$name = $this->$name;
}
return $b;
}
}
$c = new c();
$b = $c->return_instance_of_b();
var_dump( $b); // $b->var1 = "Test", $b->var2 = "Data
I believe you can achieve this with some reflection. Not very pretty code, I'm sure there is a much more succinct method to achieve this but here you go.
class a
{
public $foo;
public $bar;
function set($key, $value) {
$this->$key = $value;
}
function get($key) {
return $this->$key;
}
}
class b extends a
{
function hello() {
printf('%s | %s', $this->foo, $this->bar);
}
}
class c extends a
{
public $ignored;
function return_instance_of_b() {
$b = new b();
$reflection = new ReflectionClass($this);
$parent = $reflection->getParentClass();
foreach($parent->getProperties() as $property) {
$key = $property->getName();
$value = $property->getValue($this);
$b->$key = $value;
}
return $b;
}
}
$c = new c();
$c->set('foo', 'bar');
$c->set('bar', 'bar2');
$c->set('ignored', 'should be!');
$b = $c->return_instance_of_b();
$b->hello();
// outputs bar | bar2
Additionally you could use nickb's answer but instead of hard coding the class you could use get_parent_class
function return_instance_of_b()
{
$b = new b();
foreach(get_class_vars(get_parent_class(__CLASS__)) as $name => $value) {
$b->$name = $this->$name;
}
return $b;
}

Categories