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.
Related
I have a Model class A and a subclass B.
class A extends \yii\base\Model {
public $a1,$a2;
}
class B extends A {
public $b1,$b2;
}
$o = new B();
How do I get attribute values of $o as Array, but just from class B, not from class A?
When calling $o->attributes I get ['a1'=>..., 'a2'=>...,'b1'=>..., 'b2'=>...]
My expected result is ['b1'=>..., 'b2'=>...].
Is there an Yii2-way of doing or do we have to fallback on some PHP functions/language features?
If you know what attributes you want to get you can name them in first param of yii\base\Model::getAttributes() method like this:
$attributes = $o->getAttributes(['b1', 'b2']);
If you need all attributes but don't know what attributes are there, you can use yii\base\Model::attributes() method of the parent class to get list of attributes you don't want and pass it as second argument of getAttributes() method to leave them out.
$except = A::instance()->attributes();
$attributes = $o->getAttributes(null, $except);
You can use Reflection to enumerate the properties that match the class you want.
https://www.php.net/manual/en/reflectionclass.getproperties.php
class A extends \yii\base\Model {
public $a1,$a2;
}
class B extends A {
public $b1,$b2;
}
$o = new B();
$ref = new \ReflectionClass(B::class);
$props = array_filter(array_map(function($property) {
return $property->class == B::class ? $property->name : false;
}, $ref->getProperties(\ReflectionProperty::IS_PUBLIC)));
print_r($props);
/*
Will Print
Array
(
[0] => b1
[1] => b2
)
*/
You can unset variable $a1 and $a2 in class B construct
...
class B extends A{
public $b1, $b2;
public function __construct(){
unset($this->a1, $this->a2);
}
}
...
In my case, when I look up on $o->attributes. attribute a1 and a2 still exist.
But the variables value become *uninitialized* and can't used ($o->a1 will raise and showed error message).
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.
Is it possible to extend a parent class method while overriding it ? For example:
class Foo {
public function edit() {
$item = [1,2];
return compact($item);
}
}
class Bar extends Foo {
public function edit() {
// !!! Here, is there any way I could import $item from parent class Foo?
$item2 = [3,4]; //Here, I added (extended the method with) some more variables
return compact($item, $item2); // Here I override the return of the parent method.
}
}
The issue is that I cannot edit the Foo class in any way as it is a vendor package.
I don't want to edit the vendor methods I need to extend them (add something more to their return function)
If you used array_merge() instead it will probably show the results better...
class Foo {
public function edit() {
$item = [1,2];
return $item;
}
}
class Bar extends Foo {
public function edit() {
$item = parent::edit(); // Call parent method and store returned value
$item2 = [3,4]; //Here, I added (extended the method with) some more variables
return array_merge($item, $item2); // Here I override the return of the parent method.
}
}
$a = new Bar();
print_r($a->edit());
This will output -
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
So the call to parent::edit() will return the array from the parent class and this will be added to the array from the second class function.
Update:
I can't test this, but hopefully this will give you what your after...
class Foo {
protected function getData() {
return [1,2];
}
public function edit() {
return return view('view-file', compact($this->getData()));
}
}
class Bar extends Foo {
protected function getData() {
$item = parent::edit();
$item2 = [3,4];
return array_merge($item, $item2);
}
}
This means that the only time you create the view is in the base class, all you do is add the extra information in the derived class.
I'm programming an object instance to other objects.
Now I need to validate an object instantiated.
The code i'm using is correct, but the objects are children of another object, so further back methods of parents.
Code:
<?php
class MyParentClass
{
...
$objectName = "subClassExample";
$obj = new $objectName();
print_r( get_class_methods( $obj ) );
...
}
?>
Return :
Array ( [0] => __construct [1] => myMethod )
The SubClass:
<?php
class subClassExample extends parentClass
{
public function myMethod()
{
return null;
}
}
?>
I need to return:
Array ( [0] => myMethod )
The parent Class:
<?php
class parentClass
{
function __construct ()
{
return null;
}
}
?>
I hope I can help, I really appreciate it.
Greetings!
P.S.: Excuse my English is not my language, I speak Spanish and Norwegian Bokmal.
You can do this with PHP's ReflectionÂDocs:
class Foo
{
function foo() {}
}
class Bar extends Foo
{
function bar() {}
}
function get_class_methodsA($class)
{
$rc = new ReflectionClass($class);
$rm = $rc->getMethods(ReflectionMethod::IS_PUBLIC);
$functions = array();
foreach($rm as $f)
$f->class === $class && $functions[] = $f->name;
return $functions;
}
print_r(get_class_methodsA('Bar'));
Output:
Array
(
[0] => bar
)
You may do this check inside a child or a parent class if you need only UNIQUE child's methods:
$cm = get_class_methods($this); //Get all child methods
$pm = get_class_methods(get_parent_class($this)); //Get all parent methods
$ad = array_diff($cm, $pm); //Get the diff
Keep in mind: get_class_methods returns all types of methods (public, protected etc.)
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.