Access parent and child variables in method defined in parent - PHP - php

I have a child object that inherits from a parent. Both have a static variable which has a different value in each object; I want to add that variable from both parent and child to an array when I instantiate the child. To save duplicating code, I've written a method (addFoo) in the parent which is called from both the parent and the child constructors. However, I can't seem to find a way to distinguish between the calls when the parent constructor is called from the child constructor (as you can see below, the output from the method is the same in both cases whether using $this, self or static).
class A {
public static $foo = 'foo';
public $thisvars = array();
public $selfvars = array();
public $staticvars = array();
public function __construct() {
$this->addFoo();
}
public function addFoo() {
$this->selfvars[] = self::$foo;
$this->staticvars[] = static::$foo;
$this->thisvars[] = $this::$foo;
}
}
class B extends A {
public static $foo = 'bar';
public function __construct() {
parent::__construct();
$this->addFoo();
}
}
$b = new B;
print_r($b->selfvars);
print_r($b->staticvars);
print_r($b->thisvars);
Output:
Array
(
[0] => foo
[1] => foo
)
Array
(
[0] => bar
[1] => bar
)
Array
(
[0] => bar
[1] => bar
)
I can workaround this by passing the calling class through to the addFoo function (see below), but is there a better (correct?) way?
class C {
public static $foo = 'foo';
public $vars = array();
public function __construct() {
$this->addFoo(__CLASS__);
}
public function addFoo($class) {
$this->vars[] = $class::$foo;
}
}
class D extends C {
public static $foo = 'bar';
public function __construct() {
parent::__construct();
$this->addFoo(__CLASS__);
}
}
$d = new D;
print_r($d->vars);
Output:
Array
(
[0] => foo
[1] => bar
)
Demo on 3v4l.org

Instead of having addFoo be called by every sub-constructor, one way would be to have a single addFoos method in the base class that is called by the base constructor, that would append all the $foo values starting from the late static binding class:
class A
{
public static $foo = 'foo';
public $vars = [];
public function __construct()
{
$this->addFoos();
}
private function addFoos()
{
$class = static::class;
do {
$this->vars[] = $class::$foo;
} while ($class = get_parent_class($class));
}
}
class B extends A
{
public static $foo = 'bar';
}
class C extends B
{
public static $foo = 'baz';
}
$a = new A;
print_r($a->vars); // ['foo']
$b = new B;
print_r($b->vars); // ['bar', 'foo']
$c = new C;
print_r($c->vars); // ['baz', 'bar', 'foo']
That method is marked private as it's not supposed to be extended in this scenario (nor called from the outside).
Demo

I would alternatively consider another, more straightforward approach:
class A
{
public static $foo = 'foo';
public function getVars()
{
return [self::$foo];
}
}
class B extends A
{
public static $foo = 'bar';
public function getVars()
{
return array_merge(parent::getVars(), [self::$foo]);
}
}
class C extends B
{
public static $foo = 'baz';
public function getVars()
{
return array_merge(parent::getVars(), [self::$foo]);
}
}
$a = new A;
print_r($a->getVars()); // ['foo']
$b = new B;
print_r($b->getVars()); // ['foo', 'bar']
$c = new C;
print_r($c->getVars()); // ['foo', 'bar', 'baz']
You do have to redefine getVars on each subclass in this case, but after all, it makes sense for each class to decide which variables should be exposed. Your code becomes a bit less obscure / easier to maintain in the process.
And if a class doesn't need/want to "contribute", you can simply omit both the static property and the getVars extension for that class.
Demo
Notes:
you can easily cache the variables into a $vars property inside A if needed,
order is swapped from the other answer but you can obviously swap the array_merge if needed,
in both this sample and the one from the other answer, I'm not sure if the $foo static properties need to be public, but I've left them like they were in your question.

Related

Hierarchy of static properties in inherited class

I want to define a static property in a class, and have each child class have its own value for that property.
Here is what I tried:
class A {
static protected $v = "A";
static public function getV() {
return static::$v;
}
static public function setV($value) {
static::$v = $value;
}
}
class B extends A {}
class C extends A {}
B::setV("B");
print_r(A::getV());
print_r(B::getV());
print_r(C::getV());
print_r("\n");
C::setV("C");
print_r(A::getV());
print_r(B::getV());
print_r(C::getV());
print_r("\n");
What I expected:
ABA // C::$v hasn't been initialized, so it holds the parent's value
ABC
What I got:
BBB
CCC
So, there is only one property available, and it's the parent's one.
To get what I expected, I had to redeclare and initialize my static property in the child classes:
class B extends A {
static protected $v;
static public function init() {
self::$v = parent::$v;
}
}
B::init();
class C extends A {
static protected $v;
static public function init() {
self::$v = parent::$v;
}
}
C::init();
Result:
ABA
ABC
Is there a more elegant way to do this, without having to redeclare and initialize my property in the child classes?
You can have instead of scalar variable, an array containing all initialized values for each class. However it's not nice solution because it's pretty limited with static variables.
class A {
static protected $v = array("A" => "A");
static public function getV() {
return static::$v[get_called_class()] ?? static::$v[get_class()];
}
static public function setV($value) {
static::$v[get_called_class()] = $value;
}
}

override a static variable

I have two classes (Model and User) but I have a problem so I have tried to explain it in a simple example :
class person
{
protected static $todo ="nothing";
public function __construct(){}
public function get_what_todo()
{
echo self::$todo;
}
}
class student extends person
{
protected static $todo ="studing";
}
$s = new student();
$s->get_what_todo(); // this will show the word (nothing)
//but I want to show the word (studing)
Please give me a solution but without writing any function in the student class I only want to make declarations there :) and thank you :)
The principle is called "late static binding", and was introduced in PHP 5.3.0; with the self keyword to access the property defined in the calling class within the inheritance tree, or static to access the property defined in the child class within that inheritance tree.
class person
{
protected static $todo ="nothing";
public function __construct(){}
public function get_what_todo()
{
echo static::$todo; // change self:: to static::
}
}
class student extends person
{
protected static $todo ="studying";
}
class teacher extends person
{
protected static $todo ="marking";
}
class guest extends person
{
}
$s = new student();
$s->get_what_todo(); // this will show the "studying" from the instantiated child class
$t = new teacher();
$t->get_what_todo(); // this will show the "marking" from the instantiated child class
$g = new guest();
$g->get_what_todo(); // this will show the "nothing" from the parent class,
// because $todo is not overriden in the child class
The reliable way to override a static variable is to do it by redeclaring it. Some people may suggest modifying it in the construct method, but I don't think that's reliable.
It won't reflect the changes until the class is constructed at least once. And of course, in class methods, don't forget to call the static variable using "static::" instead of "self::" when you want to always access the overridden variable.
Here's an example of what I mean:
The class Foo is the base class, the class Bar is changing the variable inside its constructor, and the class Baz is overriding the variable in its declaration.
class Foo
{
public static $a = "base";
}
class Bar extends Foo
{
function __construct()
{
self::$a = "overridden";
}
}
class Baz extends Foo
{
public static $a = "overridden";
}
echo 'Foo: ' . Foo::$a . '<br>';
echo 'Bar: ' . Bar::$a . '<br>';
echo 'Baz: ' . Baz::$a . '<br>';
new Bar();
echo 'Bar after instantiation: ' . Bar::$a;
This is the output from phptester.net
Foo: base
Bar: base
Baz: overridden
Bar after instantiation: overridden
As you can see, Bar's way of changing the variable isn't taking effect until after the constructor is called at least once.
EDIT: However, there is another way to edit a variable permanently and reliably: do it after the class declaration. This is especially handy if you only need to modify a variable and not completely override it, like for example an array. It feels a bit dirty, but in theory should work everytime.
class Foo
{
public static $a = [
'a' => 'a'
];
}
class Bar extends Foo
{
public static $a;
}
Bar::$a = Foo::$a;
Bar::$a['b'] = 'b';
echo 'Foo: ' . print_r(Foo::$a, true) . '<br>';
echo 'Bar: ' . print_r(Bar::$a, true) . '<br>';
This is the output from phptester.net
Foo: Array ( [a] => a )
Bar: Array ( [a] => a [b] => b )
EDIT 2: This last method also gets picked up by ReflectionClass::getStaticPropertyValue in my tests.
you can try set variable in construction
class person
{
protected static $todo = null;
public function __construct(){
self::$todo = "nothing";
}
public function get_what_todo()
{
echo self::$todo;
}
}
class student extends person
{
public function __construct() {
self::$todo = "student";
}
}
$s = new student();
$s->get_what_todo();
you can try set parent variable in construction
class person
{
protected static $todo = null;
public function __construct(){
self::$todo = "nothing";
}
public function get_what_todo()
{
echo self::$todo;
}
}
class student extends person
{
public function __construct() {
parent::$todo = "student";
}
}
$s = new student();
$s->get_what_todo();

PHP - pass variable between child and parent class

I would to know what is the best way to pass variable between parent and child class and updating this class all along the class is executed.
For example I have this parent class which execute is child class inside it:
class My_Class {
public $data;
public $data2;
public function __construct() {
}
public function output() {
$data['key1'] = 1;
$data['key2'] = 2;
$data2['key1'] = 'a';
$data2['key2'] = 'b';
$child_class = new Child_Class();
$child_class->output();
print_r($this->data); // only contains key1 & key2, I want to get key3 and 4 also
print_r($this->data2);
}
}
class Child_Class extends My_Class {
public function __construct() {
}
public function output() {
$data = parent::$data; // want to get data array but it's empty
$data2 = parent::$data2; // want to get data2 array but it's empty
this->set_data();
}
public function set_data() {
$this->data['key3'] = 3;
$this->data['key4'] = 4;
$this->data['key3'] = 'c';
$this->data['key4'] = 'd';
}
}
$class = new My_class();
$class->output();
Currently I execute the child class inside the parent class because I need to populate the main data of the parent class. This class will execute child class based on some variable.
What is the right way to inherit and assign variable from parent to child and child to parent. If I use dependency injection to retrieve the data in the extends class how can i assign the variable to the parent class?
"Do you have an example" - here you go....
<?php
class My_Class {
public $data = array('key1'=>1, 'key2'=>2);
public $data2 = array('key1'=>'a', 'key2'=>'b');
public function output() {
echo "MyClass::output\r\n";
print_r($this->data);
print_r($this->data2);
}
}
class Child_Class extends My_Class {
public function __construct() {
$this->data['key3'] = 3;
$this->data['key4'] = 4;
$this->data2['key3'] = 'c';
$this->data2['key4'] = 'd';
}
public function output() {
echo "Child_Class::output\r\n";
parent::output();
}
}
$class = new Child_Class();
$class->output();
prints
Child_Class::output
MyClass::output
Array
(
[key1] => 1
[key2] => 2
[key3] => 3
[key4] => 4
)
Array
(
[key1] => a
[key2] => b
[key3] => c
[key4] => d
)
see also:
https://en.wikipedia.org/wiki/Method_overriding
http://php.net/manual/en/keyword.parent.php
What is polymorphism, what is it for, and how is it used?
https://en.wikipedia.org/wiki/Inheritance_%28object-oriented_programming%29
https://en.wikipedia.org/wiki/Constructor_%28object-oriented_programming%29
My problem was a type, instead of $this->data I wrote $this->$data, the $ sign is only needed at the beginning of each statement.
My second problem was omitting the $this-> part when accessing variables on parent class thus creating locally scoped variables for parent class which was not shared with child classes, should have used $this->variable.

assigning properties of one object to another

is it possible to easily and quickly "assigning properties of one object to another"
class a {
public $number_one;
public $number_two;
public $number_three;
function __contruct() {
//do stuff
}
}
class b {
public $my_var;
function __contruct() {
$instanc_a = new a();
extract( $instance ); // but make these extracted object properties of class b????
// how? :-(
echo $this->number_one;
}
}
You can use get_object_vars to copy the public (only) properties of class a to the current object:
class b {
public $my_var;
function __construct() {
$instanc_a = new a();
$vars = get_object_vars($instanc_a);
foreach($vars as $name => $value) {
$this->$name = $value;
}
echo $this->number_one;
}
}
See it in action.
Note: You have a typo in your code (two cases of "contruct" instead of "construct") which will prevent things from working as they should.
Sounds like you actually want class b to extend class a
class b extends a {
public $my_var;
function __construct () {
parent::__construct();
// Now $this refers to anything in class b, or if it doesn't exist here, looks to class a for it
echo $this->number_one;
}
}

Merging properties of parent and child classes

I am trying to merge a property in an abstract parent class with the same property in a child class. The code looks sort of like this (except in my implementation, the property in question is an array, not an integer):
abstract class A {
public $foo = 1;
function __construct() {
echo parent::$foo + $this->foo; # parent::$foo NOT correct
}
}
class B extends A {
public $foo = 2;
}
$obj = new B(); # Ideally should output 3
Now I realize that parent::$foo in the constructor will not work as intended here, but how does one go about merging the property values without hardcoding the value into the constructor or creating an additional property in the parent class?
In the constructor of your parent class, do something like this:
<?php
abstract class ParentClass {
protected $foo = array(
'bar' => 'Parent Value',
'baz' => 'Some Other Value',
);
public function __construct( ) {
$parent_vars = get_class_vars(__CLASS__);
$this->foo = array_merge($parent_vars['foo'], $this->foo);
}
public function put_foo( ) {
print_r($this->foo);
}
}
class ChildClass extends ParentClass {
protected $foo = array(
'bar' => 'Child Value',
);
}
$Instance = new ChildClass( );
$Instance->put_foo( );
// echos Array ( [bar] => Child Value [baz] => Some Other Value )
Basically, the magic comes from the get_class_vars( ) function, which will return the properties that were set in that particular class, regardless of values set in child classes.
If you want to get the ParentClass values with that function, you can do either of the following from within the ParentClass itself: get_class_vars(__CLASS__) or get_class_vars(get_class( ))
If you want to get the ChildClass values, you can do the following from within either the ParentClass, or the ChildClass: get_class_vars(get_class($this)) although this is the same as just accessing $this->var_name (obviously, this depends on variable scope).
You can't directly do that. You'd need to define it in the constructor of B, since B->$foo would overwrite A's at compile time (and hence A->$foo would be lost):
abstract class A {
public $foo = 1;
function __construct() {
echo $this->foo;
}
}
class B extends A {
public function __construct() {
$this->foo += 2;
}
}
Now, there are ways around that, but they involve Reflection and will be dirty. Don't do that. Just increment it in the constructor and be done...
You can't. The best option you have is to have another property. I know that you already know this, but that's the best solution.
<?php
class A {
protected $_foo = 2;
}
class B extends A {
protected $foo = 3;
function bar( ) {
return $this->_foo + $this->foo;
}
}
That's your best bet.

Categories