-
Notifications
You must be signed in to change notification settings - Fork 2
Added mode to allow recursive marshalling of objects #22
base: master
Are you sure you want to change the base?
Conversation
|
Also, because I mentioned it today, the idea of introducing annotations for this, much like JSON struct field tags in GoLang. type Identity struct {
Id int64 `json:"id"`
}(see https://golang.org/pkg/encoding/json/ and https://play.golang.org/p/ZDb6bVGiO6) We could get this: class Identity implements \lang\Struct {
#[@name('id')]
private $id;
}...and then wouldn't have to rely on fuzzy getter/setter lookup semantics. This could be a part of XP9... |
|
Hrm, implementing this myself (including a fix for your recursion implementation which would call getters for protected members twice or more) I stumbled upon the fact that private members may exist parallel to a parent class' private member: $ xp -d 'class P { private $p= "P"; } class C extends P { private $p= "C"; } return new C()'
object(C)#29 (2) {
["p":"C":private]=>
string(1) "C"
["p":"P":private]=>
string(1) "P"
}If we implement this with recursion, which field's value should we fetch and serialize? Whatever answer you select will never completely make you happy. I'm not sure this is really solveable in a "right" way except for leaving it as-is. I suggest using /cc @mikey179 |
|
I think setting the members to protected would be an ugly hack, which is difficult to understand for someone not involved in this Problem. So, I have to add comments explaining the hack, which is even more ugly. If there are two protected members, the getter should return the value of the child, which I think is the correct behavior. I think that the only problematic cases are the ones, when you mix private and public/protected members with the same name. |
|
Another option would be to write specialized marshallers for your types, e.g. by adding a marker interface (or maybe you already have some sort of parent class). For this, we only need to make marshalling accessible, e.g. by adding a Here's a full example of how implementing marshalling for value objects implementing the getX() / setX() paradigm would work: interface ValueObject {
}
class ValueObjectMarshaling implements TypeMarshaller {
public function marshal($value, $marshalling) {
$type= typeof($value);
$fields= [];
do {
foreach ($type->getFields() as $field) {
if ($field->getModifiers() & MODIFIER_STATIC) continue;
$fields[$field->getName()]= true;
}
} while (null !== ($type= $type->getParentclass()));
$result= [];
foreach ($fields as $key => $val) {
$result[$key]= $marshalling->marshal($type->getMethod('get'.$key)->invoke($value));
}
return $result;
}
public function unmarshal(Type $target, $value, $marshalling) {
$instance= $target->newInstance();
foreach ($value as $key => $val) {
$method= $target->getMethod('set'.$key);
$type= $method->getParameter(0)->getType();
$method->invoke($instance, [$marshalling->unmarshal($type, $val)]);
}
return $instance;
}
}
class Identity implements ValueObject {
private $id;
public function setId(int $id): self { $this->id= $id; return $this; }
public function getId(): int { return $this->id; }
}
$api= (new Endpoint($url))->marshalling(ValueObject::class, new ValueObjectMarshalling()); |
|
How do other PHP libraries handle this, by the way? |
If an object has private fields which can be accessed via getter functions, the marshalling (with RestMarshalling) of those fields works, but if the parent object also has such fields, it won't work.
Example:
Produces:
But is should produce:
This pull request implements this behavior, which can be enabled by adding the
#[@recursive]annotation to the child class.