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.
Related
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.
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
I'm writing a library in PHP 5.3, the bulk of which is a class with several static properties that is extended from by subclasses to allow zero-conf for child classes.
Anyway, here's a sample to illustrate the peculiarity I have found:
<?php
class A {
protected static $a;
public static function out() { var_dump(static::$a); }
public static function setup($v) { static::$a =& $v; }
}
class B extends A {}
class C extends A {}
A::setup('A');
A::out(); // 'A'
B::out(); // null
C::out(); // null
B::setup('B');
A::out(); // 'A'
B::out(); // 'B'
C::out(); // null
C::setup('C');
A::out(); // 'A'
B::out(); // 'B'
C::out(); // 'C'
?>
Now, this is pretty much desired behaviour for static inheritance as far as I'm concerned, however, changing static::$a =& $v; to static::$a = $v; (no reference) you get the behaviour I expected, that is:
'A'
'A'
'A'
'B'
'B'
'B'
'C'
'C'
'C'
Can anyone explain why this is? I can't understand how references effect static inheritance in any way :/
Update:
Based on Artefacto's answer, having the following method in the base class (in this instance, A) and calling it after the class declarations produces the behaviour labelled as 'desired' above without the need to assign by reference in setters, whilst leaving the results when using self:: as the 'expected' behaviour above.
/*...*/
public static function break_static_references() {
$self = new ReflectionClass(get_called_class());
foreach($self->getStaticProperties() as $var => $val)
static::$$var =& $val;
}
/*...*/
A::break_static_references();
B::break_static_references();
C::break_static_references();
/*...*/
TL;DR version
The static property $a is a different symbol in each one of the classes, but it's actually the same variable in the sense that in $a = 1; $b = &$a;, $a and $b are the same variable (i.e., they're on the same reference set). When making a simple assignment ($b = $v;), the value of both symbols will change; when making an assignment by reference ($b = &$v;), only $b will be affected.
Original version
First thing, let's understand how static properties are 'inherited'. zend_do_inheritance iterates the superclass static properties calling inherit_static_prop:
zend_hash_apply_with_arguments(&parent_ce->default_static_members TSRMLS_CC,
(apply_func_args_t)inherit_static_prop, 1, &ce->default_static_members);
The definition of which is:
static int inherit_static_prop(zval **p TSRMLS_DC, int num_args,
va_list args, const zend_hash_key *key)
{
HashTable *target = va_arg(args, HashTable*);
if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) {
SEPARATE_ZVAL_TO_MAKE_IS_REF(p);
if (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, p,
sizeof(zval*), NULL) == SUCCESS) {
Z_ADDREF_PP(p);
}
}
return ZEND_HASH_APPLY_KEEP;
}
Let's translate this. PHP uses copy on write, which means it will try to share the same actual memory representation (zval) of the values if they have the same content. inherit_static_prop is called for each one of the superclass static properties so that can be copied to the subclass. The implementation of inherit_static_prop ensures that the static properties of the subclass will be PHP references, whether or not the zval of the parent is shared (in particular, if the superclass has a reference, the child will share the zval, if it doesn't, the zval will be copied and new zval will be made a reference; the second case doesn't really interest us here).
So basically, when A, B and C are formed, $a will be a different symbol for each of those classes (i.e., each class has its properties hash table and each hash table has its own entry for $a), BUT the underlying zval will be the same AND it will be a reference.
You have something like:
A::$a -> zval_1 (ref, reference count 3);
B::$a -> zval_1 (ref, reference count 3);
C::$a -> zval_1 (ref, reference count 3);
Therefore, when you do a normal assignment
static::$a = $v;
since all three variables share the same zval and its a reference, all three variables will assume the value $v. It would be the same if you did:
$a = 1;
$b = &$a;
$a = 2; //both $a and $b are now 1
On the other hand, when you do
static::$a =& $v;
you will be breaking the reference set. Let's say you do it in class A. You now have:
//reference count is 2 and ref flag is set, but as soon as
//$v goes out of scope, reference count will be 1 and
//the reference flag will be cleared
A::$a -> zval_2 (ref, reference count 2);
B::$a -> zval_1 (ref, reference count 2);
C::$a -> zval_1 (ref, reference count 2);
The analogous would be
$a = 1;
$b = &$a;
$v = 3;
$b = &$v; //$a is 1, $b is 3
Work-around
As featured in Gordon's now deleted answer, the reference set between the properties of the three classes can also be broken by redeclaring the property in each one of the classes:
class B extends A { protected static $a; }
class C extends A { protected static $a; }
This is because the property will not be copied to the subclass from the superclass if it's redeclared (see the condition if (!zend_hash_quick_exists(target, key->arKey, key->nKeyLength, key->h)) in inherit_static_prop).
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.
Is it possible to define attributes to a class in the constructor.
For instance I pass a Associative Array to a class constructor and I want the attributes to that class to be declared and set based on what is in the Associative Array.
TIA
class Foo{
function __construct($arr){
foreach($arr as $k => $v)
$this->$k = $v;
}
}
You can review the constructor/desctructor manual and properties manual.
to be noted since you don't define the properties in the class all are set to public which IMHO is kind of dangerous. I think it might be possible to achieve the same thing using the reflection. I just checked more in depth the reflection and it is not possible (with PHP5), since it would make sense to be able to do that from the reflection it might come with PHP6.
full sample
<?php
class Foo{
function __construct($arr){
foreach($arr as $k => $v)
$this->$k = $v;
}
function getBar(){
return $this->bar;
}
}
$bar = new Foo(array(
'bar' => 'bar',
'foo' => 'foo'
)
);
var_dump($bar->bar);
?>
Yes, you can do that. off top of my head
public function __constructor($data){
foreach($data as $k=>$v){
$this->{$k} = $v;
}
}
This should do it, and of course you need to math define the fields.
I m not too sure if it s {$k} or ${k} or ${$k} but either of these should work.
You can certainly do this, as RageZ describes above, but I don't think I would recommend doing it. What this does is creates too loose of a "contract" between the users of this class - i.e. nobody really knows which properties the class has.
Instead of defining the properties on the fly, I would bet that you have a pre-defined set of "object property sets" that could in turn define a class hierarchy.
So instead of doing
if I get an array with "a = 2 and b =
3", create object with $obj->a = 3
and $obj-b = 3
if I get an array with
"c = 5", create an object with
$obj->c = 5
Do this:
define a class for MyObjectOfTypeOne, that has properties $a and $b
define a class for MyObjectOfTypeTwo that has property $c
Then, use the Factory pattern to generate the right type of object depending on the parameters passed to the static factory method.