How do I iterate through a child's private fields? - php

After reading this question
One idea for my problem was to implement the method in the child classes like:
class Child {
private $childField = "Want to see";
public $pubChildField = "Will see";
public function method()
{
$assoc = []; // a associative array
foreach ($this as $field => $value) {
$assoc[$field] = doStuff($value);
}
return $assoc;
}
}
var_dump((new Child())->jsonSerialize());
But for this I'd have to copycat the code in each child class. For readability, I want to refactor it into an (already existing) parentClass similar to:
abstract class parentClass {
public function method()
{
$assoc = []; // a associative array
foreach ($this as $field => $value) {
$assoc[$field] = doStuff($value);
}
return $assoc;
}
}
class Child extends parentClass {
private $childField;
public $pubChildField;
}
var_dump((new Child())->jsonSerialize());
Both foreach ($this as $k=>$v) and get_object_vars() will get the public fields from the instance. I need to get it's private fields thou, for serialization.
Edit 1: Typo abstract method - what was i thinking
Edit 2: Clarified the example
Edit 3: Reformulated the question since there seemed to be misconception

Abstract class is about abstract methods, not inherited. I assume these
are not relevant to the problem (otherwise make it ordinary default/base class).
private limits visibility to concrete class - use protected
instead.
It seems to me that you need only code reuse. You might reach for traits in this casse - traits becoming part of a class, and you can iterate private fields.
interface ArrayData {
public function toArray(): array;
}
trait ToArray {
public function toArray(): array {
$assoc = []; // a associative array
foreach ($this as $field => $value) {
$assoc[$field] = $value;
}
return $assoc;
}
}
class Foo implements ArrayData {
use ToArray;
private $childField = "Want to see";
public $pubChildField = "Will see";
}
$foo = new Foo();
var_dump($foo->toArray());

The correct solution is to declare the base class implements the JsonSerializable interface and implement the JsonSerializable::jsonSerialize() method in all children classes.
You can let it unimplemented in the base class to force all the children classes implement it or you can provide a default implementation (return array();) in the base class for the children classes that do not need to be serialized.
The implementation in each class should not iterate over the list of object properties. Usually, not all object properties must be serialized and the serializable properties are not born equal.
abstract class parentClass implements JsonSerializable {
// You can remove this default implementation
// to force all children class implement the method
public function jsonSerialize()
{
return array();
}
}
class childClass extends parentClass {
private $childField;
public $pubChildField;
public function jsonSerialize()
{
return array(
'field1' => $this->childField,
'field2' => base64_encode($pubChildField),
// etc ...
);
}
}
var_dump((new Child())->jsonSerialize());
Try to write code that is easier to read and understand. You will thank yourself later.

Related

In PHP, can I alias a class property? E.g. to allow ArrayAccess methods via Trait

I implement ArrayAccess in some of my classes and often, I use almost exactly the same code for the ArrayAccess methods in my class as are in Example #1 on the ArrayAccess docs.
Since the code is the same, it would be nice to write this once as a Trait, and then my classes can just implement ArrayAccess (as now), and use ArrayAccessTrait without needing to duplicate a bunch of ArrayAccess methods.
The only thing preventing this is that I usually don't want my underlying array named $container, but something else more pertinent to the class I'm building.
So my question is, is there a way to "alias" whatever array property name I'm using within my own class with the $container being used in my ArrayAccessTrait ?
The example below has a Foo class that shows what I want; this example works, but I'd like to be able to use a property named something other than $container within the Foo class.
trait ArrayAccessTrait {
private $container = [];
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->container[] = $value;
} else {
$this->container[$offset] = $value;
}
}
public function offsetExists($offset) {
return isset($this->container[$offset]);
}
public function offsetUnset($offset) {
unset($this->container[$offset]);
}
public function offsetGet($offset) {
return isset($this->container[$offset]) ? $this->container[$offset] : null;
}
}
class Foo implements ArrayAccess {
use ArrayAccessTrait;
private $container = [];
public function __construct() {
$this->container = [
"one" => 1,
"two" => 2,
"three" => 3,
];
}
public function hello($msg = 'Hello World') {
echo $msg . "<br/>\n";
}
}
$obj = new Foo;
$obj->hello();
echo "Array accessing element ['two'] = " . $obj['two'];
If you're defining private attributes in a trait, you arguably shouldn't be referring to them directly in the classes that implement it. Instead, just use the methods in the trait. I.e., instead of doing this in the concrete class:
$this->container["one"] = 1;
Do this:
$this["one"] = 1;
Then you don't need to know what the storage attribute is called, and you don't need to write any code around setting/getting it. Note also that you do not need to redefine private $container = []; in your class, as it already comes with the trait.

PHP Serialization with private attributes & getters & setters

I have a base class which uses php magic methods __get and __set to be able to modify private attributes in the extended class. Then I built setter getter functions for the relevant private attributes
(similar to what is found here
http://www.beaconfire-red.com/epic-stuff/better-getters-and-setters-php
)
So my child class will look as follows:
class User extends BaseObject {
public $id = -1;
private $_status = "";
function __construct($members = array()) {
parent::__construct($members);
}
//Setter/Getter
public function status($value = null) {
if($value) {
$this->_status = $value;
} else {
return $this->_status;
}
}
Now when I serialize this object which is a JsonSerialize method in the base class, the serialization will only pick up public attributes from the child class (ie "Id") but it won't pick up the private attributes (ie "_status")
This is the serialization function:
public function jsonSerialize() {
$json = array();
foreach($this as $key => $value) {
$json[$key] = $value;
}
return $json;
}
Is there any way the above method in the base class can identify all Getters in the child class so that they can be included in the serialization?
In other words I want the serialization to include both "id" and "status"
I realize I could get all methods on the class and use some kind of naming convention to identify the getter/setter but i specifically need to keep the getter/setter name the same as the attribute name, ie _status MUST have a getter setter called status()
so is there any other way to identify these specific functions?
Maybe a bit different approach:
public function toArray()
{
$descriptor = new \ReflectionClass(get_class($this));
/** #var \ReflectionMethod[] $methods */
$methods = $descriptor->getMethods(\ReflectionMethod::IS_PUBLIC);
$array = [];
foreach ($methods as $method) {
if (substr_compare('get', $method->getName(), 0, 3) === 0) {
$property = lcfirst(substr($method->getName(), 3));
$value = $method->invoke($this);
$array[$property] = $value;
}
}
return $array;
}
Instead of returning array, you can also serialize it. This approach uses public getter methods on child classes.
Use Reflection and ReflectionProperty::setAccessible:
public function jsonSerialize()
{
$json = array();
$class = new ReflectionClass($this);
foreach ($class->getProperties() as $key => $value) {
$value->setAccessible(true);
$json[$value->getName()] = $value->getValue($this);
}
return $json;
}
This is for to answer to explicit question. But I'm not sure that you should use this design in order to solve your problem.
I am going with a different approach...I think i prefer it over reflection.
But am interested to hear if others agree.
I am going to register each Getter/Setter in the child class based on when its accessed, ie I will store the getter/setter name in an array.
At serialization time I will not only interate over all the public attributes but also over the registered getter/setters.
Here is when I register (store the getter/setter name) in my base class, storing it in an array called _getters.
function __set($name,$value){
if(method_exists($this, $name)){
$this->$name($value);
//save in getters list for serialization
if(!in_array($name,$this->_getters))
array_push($this->_getters,$name);
}
else{
// Getter/Setter not defined so set as property of object
$this->$name = $value;
}
}
And now in my serialization I also retrieve any registered getters and serialize them.
its working well!
public function jsonSerialize() {
$json = array();
//do public properties
foreach($this as $key => $value) {
$json[$key] = $value;
}
//do any getter setters
foreach($this->_getters as $key => $getter) {
$value = $this->$getter;
$json[$getter] = $value;
}
return $json;
}
Any issues with this approach?

How to build static property that is aware of inheritance chain in PHP without boilerplate

I have been struggling to implement an OOP concept in PHP, and the solution is eluding me. I have an abstract base class whose constructor calls a series of setters in a dispatch table based on an associative array of argument data, as follows:
abstract class Base {
protected static $_SETTER_DISPATCH_MODEL = array();
public function __construct(array $data) {
foreach (static::$_SETTER_DISPATCH_MODEL as $dataKey => $method) {
if (array_key_exists($dataKey, $data)) {
if (method_exists($this, $method)) {
$this->$method($data[$dataKey]);
}
else {
// Handle error
}
}
}
}
}
What I would like to do is be able to define a method in my base class that builds the setter dispatch table by merging the subclass' dispatch table with that of each of its parents, so I would be able to do this:
class Foo extends Base {
protected static $_SETTER_DISPATCH_MODEL = array('foo' => 'setFoo');
}
class Bar extends Foo {
protected static $_SETTER_DISPATCH_MODEL = array('bar' => 'setBar');
// The base constructor should call setFoo() and setBar()
}
class Baz extends Bar {
protected static $_SETTER_DISPATCH_MODEL = array('baz' => 'setBaz');
// The base constructor should call setFoo(), setBar(), and setBaz()
}
I can't figure out a way to define this behavior in the base class and make it take into account everything that happens all the way down the inheritance chain. The only way I've been able to come up with to do what I'm trying to do is to create a second property that contains the setters the class wishes to add to the model, and add this boilerplate constructor to each subclass:
public function __construct(array $data) {
if (!array_key_exists(
key(self::$_SUPPLEMENTAL_SETTER_DISPATCH_MODEL),
static::$_SETTER_DISPATCH_MODEL
)) {
static::$_SETTER_DISPATCH_MODEL = array_merge(
static::$_SETTER_DISPATCH_MODEL,
self::$_SUPPLEMENTAL_SETTER_DISPATCH_MODEL
);
}
parent::__construct($data);
}
There has to be a better way to do this, right?

Changing object right before PHP serialises it

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.

PHP: How to call private values of parent construct in child construct?

I want to be able to set a private attribute's value in the parent constructor, and call the value in a child's constructor or method.
For example:
<?php
abstract class MainClass
{
private $prop_1;
private $prop_2;
function __construct()
{
$this->prop_2 = 'this is the "prop_2" property';
}
}
class SubClass extends MainClass
{
function __construct()
{
parent::__construct();
$this->prop_1 = 'this is the "prop_1" property';
}
public function GetBothProperties()
{
return array($this->prop_1, $this->prop_2);
}
}
$subclass = new SubClass();
print_r($subclass->GetBothProperties());
?>
Output:
Array
(
[0] => this is the "prop_1" property
[1] =>
)
However, if I change prop_2 to protected, the output will be:
Array
(
[0] => this is the "prop_1" property
[1] => this is the "prop_2" property
)
I have basic knowledge of OO and php, but I can't figure out what is preventing prop_2 from being called (or shown?) when it's private; it can't be a private/public/protected issue, since 'prop_1' is private and able to be called and shown... right?
Is it an issue of assigning the values in the child class vs parent class?
I would appreciate help in understanding why.
Thank you.
Private properties of parent class can not be accessed in Child class and vice versa.
You can do like this
abstract class MainClass
{
private $prop_1;
private $prop_2;
function __construct()
{
$this->prop_2 = 'this is the "prop_2" property';
}
protected function set($name, $value)
{
$this->$name = $value;
}
protected function get($name)
{
return $this->$name;
}
}
class SubClass extends MainClass
{
function __construct()
{
parent::__construct();
$this->set('prop_1','this is the "prop_1" property');
}
public function GetBothProperties()
{
return array($this->get('prop_1'), $this->get('prop_2'));
}
}
If you want to access the parent's properties from the child class, you must make the parent's properties protected NOT private. This way they are still inaccessible externally.
You can't override the parent's private properties visibility in the child class in the way you are trying to.
As others have noted, you'd need to change the parent's properties to protected. However, the other way is by implementing a get method for your parent class, which allows you access to the property, or implementing a set method if you want the ability to over-ride it.
So, in your parent class, you'd declare:
protected function setProp1( $val ) {
$this->prop_1 = $val;
}
protected function getProp1() {
return $this->prop_1;
}
Then, in your child class, you can access $this->setProp1("someval"); and $val = $this->getProp1(), respectively.
There is a simple trick you an use with lambdas, which I found here: https://www.reddit.com/r/PHP/comments/32x01v/access_private_properties_and_methods_in_php_7/
basically you use a lambda and bind it to the instance and then you can access it's private methods and properties
Info on the lambda call: https://www.php.net/manual/en/closure.call.php

Categories