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).
Related
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.
I want to give class B the ability to access the protected attribute x of class A.
It is important to note, that I do not want to make x either public nor do I want to expose its contents via a getter function.
The only classes that should have access to A->x are A and B.
<?php
class A
{
protected $x = 'some content';
}
class B
{
protected $a;
public function __construct(A $a)
{
$this->a = $a;
}
public function print_x()
{
print '???';
}
}
$b = new B(new A());
$b->print_x();
I am looking for solutions to achieve this.
Class B will have access to class A protected members if class B extends class A.
A child class will have access to protected members of the parent class. A child class can also override the methods of the parent class.
You use inheritance(parent/child relationship) when one class extends the feature of another class. For example class square can extend class rectangle. Class square will have all the properties and features of class rectangle plus its own properties and features that make it different from a rectangle.
You are implementing composition by passing in class A into class B. Composition is used one class uses another class. For example, an user class may use a database class.
class A
{
protected $x = 'some content';
}
class B
{
protected $a;
public function __construct(A $a)
{
$this->a = $a;
}
public function print_x()
{
print '???';
}
}
$b = new B(new A());
$b->print_x();
Recommended readings:
http://www.adobe.com/devnet/actionscript/learning/oop-concepts/inheritance.html
http://en.wikipedia.org/wiki/Inheritance_%28object-oriented_programming%29
http://eflorenzano.com/blog/2008/05/04/inheritance-vs-composition/
If you have to use reflection then you can try this:
class A
{
protected $x = 'some content';
}
class B
{
protected $a;
public function __construct(A $a)
{
$this->a = $a;
}
public function print_x()
{
$reflect = new ReflectionClass($this->a);
$reflectionProperty = $reflect->getProperty('x');
$reflectionProperty->setAccessible(true);
print $reflectionProperty->getValue($this->a);
}
}
$b = new B(new A());
$b->print_x();
Members declared protected can be accessed only within the class itself and by inherited and parent classes
Extend class A with class B.
An alternative if you do not want to extend class A with class B is to use Reflection.
I have the following class tree:
class A /* Base class */
{
private/protected/public $state
}
class B extends A /* Auto generated class, not to be modified */
{
private $v
public function getV() { return $this->v; }
public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
There is only one class A. There are multiple classes like class B, and all of those classes will have a subclass like C. Class B gets auto-generated and should not be modified.
I am storing objects of type(s) C in the session. What I want to do is to store some state information in every instance, just before PHP gets it serialised, and that will do something with it when it's unserialised. I want all this to be implemented in class A.
Considering, I need to use either __sleep() or Serializable interface. Using __sleep is out of the question, because of what the PHP manual says:
It is not possible for __sleep() to return names of private properties in parent classes. Doing this will result in an E_NOTICE level error. Instead you may use the Serializable interface.
Meaning that if I sleep an instance of class C, I'll loose the private variables declared in B. So I want to use Serializable, but for some reason, I simply can't get it to do what I want.
In essence, I would like the object to be serialised just as if I didn't implement any serialisation stuff myself, I just want to add information to $state right before it happens. I've tried covering all data with ReflectionObject->getProperties(), but I can't seem to find the right way to fetch and set the private values in class B to be serialised and unserialised.
How do I do this?
You can do this using the Reflection classes. You'll have to get the properties of the class itself and each of it's parent classes. Getting and setting the property values can be done using ReflectionProperty's getValue and setValue methods, combined with setAccessible to get access to private and protected properties. Combining those, I came up with the following code:
<?php
class A implements Serializable /* Base class */
{
protected $state;
public function serialize()
{
$this->state = "something";
return serialize($this->_getState());
}
public function unserialize($data)
{
$this->_setState(unserialize($data));
}
protected function _getState()
{
$reflClass = new ReflectionClass(get_class($this));
$values = array();
while ($reflClass != null)
{
foreach ($reflClass->getProperties() as $property)
{
if ($property->getDeclaringClass() == $reflClass)
{
$property->setAccessible(true);
$values[] = array($reflClass->getName(), $property->getName(), $property->getValue($this));
}
}
$reflClass = $reflClass->getParentClass();
}
return $values;
}
protected function _setState($values)
{
foreach ($values as $_)
{
list($className, $propertyName, $propertyValue) = $_;
$property = new ReflectionProperty($className, $propertyName);
$property->setAccessible(true);
$property->setValue($this, $propertyValue);
}
}
}
class B extends A /* Auto generated class, not to be modified */
{
private $v;
public function getV() { return $this->v; }
public function setV($val) { $this->v = $val; }
}
class C extends B { /* Custom code */ }
$instance = new C();
$instance->setV("value");
$s = serialize($instance);
$instance2 = unserialize($s);
var_dump($instance, $instance2);
Which seems to do what you want.
class A {
protected $a;
// SOME CODE
}
class B extends A {
// SOME CODE
}
How can i edit the protected value of the variable $a inside the B class ?
I'm trying to use parent::$a = "Some Value" but doesn't work.
protected instance properties, those which where not declared using static, can be accessed in subclasses using $this :
class A {
protected $a;
// SOME CODE
}
class B extends A {
// SOME CODE
public function edit($val) {
$this->$a = $val;
echo "a is now {$this->a}\n";
}
}
call:
$b = new B();
$b->edit('foo'); // a is now foo
Refer to the manual, especially the examples.
class B extends A {
public function foo($val)
{
$this->a = $val;
}
}
quite simple :)
Remember that
Members declared protected can be accessed only within the class
itself and by inherited and parent classes
from php manual
I have the following setup:
abstract class AParentLy{
private $a;
private $b;
function foo(){
foreach(get_class_vars(get_called_class()) as $name => $value){
echo "$name holds $value";
}
}
}
class ChildClass extends AParentLy{
protected $c='c';
protected $d='d';
}
$object = new ChildClass();
$object->foo();
What I want it to output is:
c holds c
d holds d
What it does output is:
c holds c
d holds d
a holds
b holds
The get_called_class() method correctly outputs "ChildClass"
I'm fairly new to class inheritance in PHP and from what I can gather the problem lies somewhere in the so scope. But I can't figure out how to make it work.
(The somewhat questionable solution would be to just go ahead and add a great big if($name!='a' && $name!='b' ...~ into the mix. But I'm sure there must be another, more sane and stable way to do this)
Change the visibility of Child's properties to PROTECTED.
When properties are private, its not visibles.
More info at:
http://php.net/manual/en/language.oop5.visibility.php
Had another go at the whole experiment this question was a part of.
The eventual solution (just in case anyone stumbles over this and has the same problem) I came to was to create the following method within the parent class:
function get_properties(){
foreach(get_class_vars(get_called_class()) as $name => $value){
if(!in_array($name,array_keys(get_class_vars('Parent')))){
$r[$name]=$this->$name;
}
}
return $r;
}
with this you get every parameter (and their value) of the child class without any of the parent class. You'll have to change the function a bit if you ever change the class name, but at least for me this was a workable solution.
class Parent1 {
//private $a;
//private $b;
function foo(){
foreach(get_object_vars($this) as $name => $value){
echo "$name holds $value";
}
}
}
class Child1 extends Parent1 {
protected $c='c';
protected $d='d';
}
Parent is a reserved name.
in class Parent1 you can see $a and $b so removed.
changed $c/$c to protected.
the other solution would be:
class Parent1 {
private $a;
private $b;
}
class Child1 extends Parent1 {
private $c='c';
private $d='d';
function foo(){
foreach(get_object_vars($this) as $name => $value){
echo "$name holds $value<br>";
}
}
}
putting foo in Child
EDIT
Sorry to wake an old post. I think i have a preferable solution (actually 2 solutions) for this:
The first one is to use a middle class that will create a barrier between the parent and the child:
abstract class Parent1 {
private $a;
private $b;
abstract function foo();
}
class ParentClone1 {
function foo(){
foreach(get_object_vars($this) as $name => $value){
echo "$name holds $value<br />";
}
}
}
class Child1 extends ParentClone1 {
protected $c='c';
protected $d='d';
}
$c = new Child1();
$c->foo();
// c holds c
// d holds d
The other solution is to use visibility:
If you call get_class_vars()/get_object_vars() from inside a class, it sees all the variables (including private/protected). If you run it from outside it will only see public:
function get_object_vars_global($class){
return get_object_vars($class);
}
abstract class Parent1 {
private $a;
private $b;
function foo(){
foreach(get_object_vars_global($this) as $name => $value){
echo "$name holds $value<br />";
}
}
}
class Child1 extends Parent1 {
public $c='c';
public $d='d';
}
$c = new Child1();
$c->foo();
since this will result in putting class fields as public, I'd go with the first solution.
First some basic mistakes:
Don't use a $ in a function name (here: $foo), this will result into a syntax error.
You shouldn't name a class Parent, because it is a reserved word.
Calling this would result into an error like Fatal error: Cannot use 'Parent' as class name as it is reserved
There is a good example how it works in the php manual, and there can be found this important sentence, which answers your question:
Class members declared public can be accessed everywhere. Members
declared protected can be accessed only within the class itself and by
inherited and parent classes. Members declared as private may only be
accessed by the class that defines the member.