call_user_func with parent method in child method - php

I have the following code:
class A {
protected $v = 'a';
public function f() {
echo 'f in A: ('.get_class($this).', '.$this->v.')<br/>';
}
}
class B extends A {
protected $v = 'b';
public function f() {
echo 'f in B: ('.get_class($this).', '.$this->v.')<br/>';
call_user_func(array($this, 'parent::f'));
}
}
class C extends B {
protected $v = 'c';
}
$a = new A();
$b = new B();
$c = new C();
$a->f();
$b->f();
$c->f();
My expected output is:
f in A: (A, a)
f in B: (B, b)
f in A: (B, b)
f in B: (C, c)
f in A: (C, c)
But what I get is an infinite loop at the end. After a bit of research, someone pointed to me that call_user_function(array($this, 'parent::f')) calls B::f when used in C.
From, there I found 3 options that work, but I do not know which one is the « good » way of doing it. Which one of the three following methods is the « correct » way?
// Option 1, explicit specification of the parent class
call_user_func(array($this, 'A::f')) ;
// Option 2, use of __CLASS__ instead of $this
call_user_func(array(__CLASS__, 'parent::f')) ;
// Option 3, combination of both options 1 and 2
call_user_func(array(__CLASS__, 'A::f')) ;

You've correctly identified that this code:
call_user_func (array($this, 'parent::f'));
When run in the context of C will keep calling B::f because $this will always be an instance of C and the parent of C is always B.
To fix it you can simple do:
call_user_func('parent::f');
It has no reference to the calling class, so it will resolve the parent class properly.
Out of the working alternative you have provided, the following is the better one:
call_user_func (array(__CLASS__, 'parent::f'));
This is because __CLASS__ always refers to the class declaration in which it appears and so will always be B.

Related

(PHP) Why is this the output of the following echo?

I'm going through some PHP question for my exam, and in the question below, apparently (B) is the answer.
What is the output of the following code?
class Magic {
public $a = 'A';
protected $b = array('a' => 'A', 'b' => 'B', 'c' => 'C');
protected $c = array(1, 2, 3);
public function __get($v) {
echo "$v,";
return $this->b[$v];
}
public function __set($var, $val) {
echo "$var: $val,";
$this->$var = $val;
}
}
$m = new Magic;
echo $m->a.','.$m->b.','.$m->c.',';
$m->c = 'CC';
echo $m->a.','.$m->b.','.$m->c;
A: A,Array,Array,A,Array,Array,CC
B: b,c,A,B,C,c: CC,b,c,A,B,C
C: a,b,c,A,B,C,c: CC,a,b,c,A,B,C
D: b,c,A,B,C,c: CC,b,c,A,B,CC
Sorry for the noob question, but coming from Java, I can't for the life of me understand why this is the correct answer.
$b and $c are a protected properties so they cannot be set from outside the class scope. $a is public so it can be set/accessed directly.
For accessing $b and $c, it will fallback to the magic getter which retrieves the values from the $b array.
The logic follows:
b, <- getter echo (executed by $m->b)
c, <- getter echo (executed by $m->c)
A, <- public property value (this is the start of the first global echo expression)
B, <- getter return b[b]
C, <- getter return b[c]
c: CC, <- setter echo, sets c = CC, but c is never accessed
b, <- getter echo (executed by $m->b)
c, <- getter echo (executed by $m->c)
A, <- public property value (this is the start of the second global echo expression)
B, <- getter return b[b]
C <- getter return b[c]
The getter echoes are processed first because their echo statements are reached before the echoed expression (with concatenation) has finished evaluating.
Since $a is public, no magic setters or getters are used.
Does this cut down version make it any clearer?
class Magic
{
public $a = "A";
protected $b = ['a' => 'A', 'b' => 'B', 'c' => 'C'];
public function __get($v)
{
echo "A MAGIC METHOD IS BEING CALLED TO GET THE PROPERTY $v", PHP_EOL;
return $this->b[$v];
}
}
$m = new Magic;
echo ($m->a . ',' . $m->b . ',' . $m->c);
A MAGIC METHOD IS BEING CALLED TO GET THE PROPERTY b
A MAGIC METHOD IS BEING CALLED TO GET THE PROPERTY c
A,B,C
I suppose the point is to demonstrate two things:
That PHP's magic __get method is only called for properties that are not directly accessible (i.e. not public). The implementation of the method can then return any string at all - in this case the property name is used to look up an element of a different array.
That variables concatenated into a string are resolved before the string is used. So the __get method is called twice (for the inaccessible properties b and c), and the echo statement inside that method is called before the string itself is concatenated and displayed.
The call to set the property c between the two "echo" lines is similarly resolved using the __set method, although it doesn't haven't any impact on the rest of the code.
Demo: https://3v4l.org/4Bitg
The apparent answer is wrong (Or you had a typo while writing it down):
B: b,c,A,B,C,c: CC,b,c,A,B,C
The sequence C,c is not possible, it has to be c,C, when accessing $m->c.

passing variable to a class

This is the scenario:
There are 3 classes: class A, class B , class C
//A.php
class A {
public $something1;
public $something2;
//long list....
// i am the base class
}
//B.php
class B extends A {
// i am a child of A
}
//C.php
class C extends A{
// i do the side work
}
The program makes an object and sets values for class B like this:
$b = new B();
$b->something1 = "XYZ";
$b->something2 = "ABC";
...... long list.....
Now what I want is:
I want to call some functions of class C in class B.
I know I can do that by making an object in class B like this $c = new C();.
But I also want to pass all the variables of B i.e. (something1, something2 ....)
passed by the program to class C
I know I can do this like this:
$c->something1 = $this->something1;
$c->something2 = $this->something2 .....
But is it possible to do that without the above code because
$c->somethingN will just take too many lines and i do not like repetitive code.
I just want to know if there is any other way.
I know i can do that by making an object in class B like this $c = new C();.
If this are instance members (not static), this is the way to go.
Another way would be to use static members, like this:
class A {
public static $foo;
}
class B extends A {}
class C extends A {}
# usage:
B::$foo = 'bar';
echo C::$foo; // will output 'bar'

PHP OOP how to group configuration data

i wanna store some configuration data in some objects, and i've a problem...
class B {
const attr1 = 'something1';
const attr2 = 'something2';
const attr3 = 'something3';
}
class A {
const attr1 = 'somethingA1';
const attr2 = 'somethingA2';
const attr3 = 'somethingA3';
const b = <---- i wanna put here a reference of B, but B as a class (not an object...), i'm crazy?
}
I'm not sure if my example is clear... from A, i want to access to attr1 of B, like A::B::attr1, or something like this, there's another way to do this? Or i'm wrong?
There is no way to assign reference to a Class, nor is there a way to assign class constants at runtime. So your entire approach is pretty much impossible. What you can do is
const b = 'B'
and as of PHP 5.3.0 you could then do
$b = A::b;
echo $b::attr1;
but you cannot do A::b::attr1. This will raise a T_PAAMAYIM_NEKUDOTAYIM error. It's a parser limitation. PHP cannot do this as of this writing.
Because the class B contains a group of data of A, i want to store in b (of A) a complex data, i want to do like this because i wanna keep the code clean
You can solve this easily by making B a composite of A, e.g. you either inject B into A when you create A
class A
{
private $b;
public function __construct(B $b)
{
$this->b = $b;
}
}
$a = new A(new B);
or create B inside A, e.g.
class A
{
private $b;
public function __construct()
{
$this->b = new B;
}
}
Because B is just the data parts of A, you tunnel all public access through A instead of getting hold of B and then using B's methods. So any code using A does not need to know there is some B inside A. This will allow you to change B easily without needing to worry about code that consumes A, e.g. to get attr1 you add a getter to A:
public function getAttr1()
{
$b = $this->b;
return $b::attr1;
}
You can mitigate the clumsy need for assignment when using properties instead of constants in B (constants are stupid in PHP anyway as you have to treat them as public API), e.g.
class B
{
private $attr1;
public function getAttr1()
{
return $this->attr1;
}
}
And then you can do in A:
public function getAttr1()
{
return $this->b->getAttr1();
}
Even better would be not to expose the internals of B through A altogether though and only add public methods that do something with A. This will make your API much smaller and your code more OO.
You cannot reference to a class like that, because the only way you will ever use the class is through an object, so there is no point trying to do that either.
User this:
class A {
const attr1 = 'somethingA1';
const attr2 = 'somethingA2';
const attr3 = 'somethingA3';
public $b;
function __construct() {
$this -> b = new b();
}
You can simply say b = "B" ..
and if you want to create object of B , You can do new b or access the properties

How to get the property name used by outer-object for inner-object from the inner-object in PHP

Okay, so code which is outside my control works like this:
Class A {
function use_b($b_name) {
$this -> $b_name = new B();
}
}
Class B {
var $stuff;
}
So that the object of class B is inside of class A and class A will use a passed in name for the property name of the class B property. Class B is totally unaware of the name it has been given and really doesn't need to know. But I need to know it in order to access class B from class A.
I do have enough control over the code that I can create a property for class B and set it to the name class A has given it, if that is possible. Then I can have class B pass it's name back to my function that needs to traverse class B (currently it returns only the outer object, so I have access to class B I just don't know the name it's been given.)
If that made no sense at all, please comment and let me know.
Finding out the members of A that hold instances of B is not that hard. Try this code:
<?php
Class A {
function use_b($b_name) {
$this -> $b_name = new B();
}
}
Class B {
var $stuff;
}
$a = new A();
$a->use_b('test');
foreach(get_object_vars($a) as $key=>$val)
if(get_class($val) == 'B')
echo $key . " is the member that holds an instance of B";

In PHP, are there any advantages to using forward_static_call_array() instead of call_user_func_array() when dynamically calling a static method?

Specifically, is one more efficient than the other?
There is at leat two differences between forward_static_call_array and call_user_func_array :
The first one only exists since PHP 5.3
The first one must be called from inside a class
After that, I suppose there is some difference that's related to Late Static Binding, that was introduced with PHP 5.3.
Actually, if you take a closer look at the given example, it seems to be exactly that : the "context" of the class inside which you are using forward_static_call_array is "kept", in the called method.
Considering this portion of code, that's derived from the given example :
class A {
const NAME = 'A';
public static function test() {
$args = func_get_args();
echo static::NAME, " ".join(',', $args)." \n"; // Will echo B
}
}
class B extends A {
const NAME = 'B';
public static function test() {
echo self::NAME, "\n"; // B
forward_static_call_array(array('A', 'test'), array('more', 'args'));
}
}
B::test('foo');
You'll get this output :
B
B more,args
i.e. from the method in class A, you "know", via the static:: keyword, that you're "coming from B".
Now, if you try to do the the same thing with call_user_func :
class B extends A {
const NAME = 'B';
public static function test() {
echo self::NAME, "\n"; // B
call_user_func_array(array('A', 'test'), array('more', 'args'));
}
}
(the rest of the code doesn't change)
You'll get this output :
B
A more,args
Note the A on the second line ! With forward_static_call_array, you didn't get an A, but a B.
That's the difference : forward_static_call_array forwards the static context to the method that's called, while call_user_func_array doesn't.
About your efficiency question : I have no idea -- you'd have to benchmark ; but that's really not the point : the point is that those two functions don't do the same thing.

Categories