Wednesday, June 13, 2018
PHP and the Friday brain
PHP and the Friday brain
Im not gonna lie to you. Today is messed up. This post will be hard to follow. I was working on a strange JSON crawler integration in Codeigniter. I had to map JSON object properties to DB fields in an array.
Its not an extraordinary process, looks like this:
$json_object = json_decode($json_string);
$mapping = array(
field1 => $json_object->field1,
// ...
);
Thats fine - however existence of keys in the object are not sure. So you need to check it in order to avoid complier errors:
$json_object = json_decode($json_string);
$mapping = array(
field1 => isset($json_object->field1) ? $json_object->field1 : NULL,
// ...
);
Repeating this patter makes you goose bumps. Isset and the ternary operatior all the time screams for simplification. However one is important here: almost only isset can make is sure that a property exists. All right. So its PHP, we cannot define macros, pity. No time either to write a Zend extension. Anonymous functions are useless too, compiler will kill you before it hits the anon functions body.
Then it hit me the idea: magic. Lets use magic methods. We can create a class that provides safe values for objects:
class safe {
protected $data;
function __construct($data = NULL) {
$this->data = $data;
if (is_object($data)) {
foreach ($data as $key => $value) {
if (is_object($data->{$key})) {
$this->data->{$key} = new safe($data->{$key});
}
}
}
}
function __get($name) {
if (isset($this->data->{$name})) {
return $this->data->{$name};
}
return NULL;
}
}Using the new satanic power the original code looks like this:
$json_object = new safe(json_decode($json_string));
$mapping = array(
field1 => $json_object->field1,
// ...
);
Here comes the awkward: yeeeeaaah. It works, indeed. Although, I hate myself. What have we done? The loop in the constructor makes sure that nested values are safe too. The accessor provides the safe value return. Where is the problem here? Well, first its not general. Only objects. Secondly its not working over 2 level or undefined properties. Only the very first one. Why? Simply its not possible to fake a magic object to be null as a value and act as an object otherwise.
Lets make a twist and think creatively. If a NULL cannot respond to any function calls lets try to avoid calling it before we check. The only way Ive found is to separate the property path from the variable:
function safe_value($value, $keys = NULL) {
if ($keys == NULL) {
return $value;
}
elseif (!is_array($keys)) {
return isset($value->{$keys}) ?
$value->{$keys} : (
isset($value[$keys]) ?
$value[$keys] :
NULL
);
}
$key = array_shift($keys);
return isset($value->{$key}) ?
safe_value($value->{$key}, $keys) : (
is_array($value) && isset($value[$key]) ?
safe_value($value[$key], $keys) :
NULL
);
}I know. But its exactly what we needed. If thats the variable in question:
$json_object->field1->field2[1]->value;
then we dont have to do this:
isset($json_object->field1) &&
isset($json_object->field1->field2) &&
isset($json_object->field1->field2[1]) &&
isset($json_object->field1->field2[1]->value) ?
$json_object->field1->field2[1]->value :
NULL;
we can do this instead:
safe_value($json_object, array(field1, field2, 1, value));
Its looking small, general, array and object compatible. But the night is not over yet. I made some tests and found a problem. If I use my magic converter on an object, like this:
$magic_object = new safe($json_object);
Then my safe_value() function is not working. It doesnt find the objects. I was investigating some time and it looked perfect, I could access the values by writing the direct version:
$json_object->field1->field2[1]->value;
but not the new version:
safe_value($json_object, array(field1, field2, 1, value));
Any idea why? I let you think with the thinking image ...
Time is over. The culprit was the isset() call in safe_value(). When using __get() the compiler doesnt know by heart what is set - which is somewhat correct. You need to tell it explicit by adding __isset() to the safe() class:
function __isset($name) {
return isset($this->data->{$name});
}---
And now the world can move along. Whats the lesson? Nothing particular, its Friday. Go to sleep.
Peter
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.