Accessing the properties of an object of an object dynamically in PHP - php

Given that I'm getting different object results depending of the API I query, here are two examples out of 35 I have to implement:
stdClass Object
(
[mid] => 7127.75
[bid] => 7126.6
[ask] => 7128.9
[last_price] => 7128.8
[low] => 7000.0
[high] => 7492.1
[volume] => 53255.4195502
[timestamp] => 1510265647.9803913
)
stdClass Object
(
[Success] => 1
[Message] =>
[Data] => stdClass Object
(
[AskPrice] => 7095
[BidPrice] => 7070
[Low] => 7001
[High] => 7540
[Volume] => 17.38943459
[LastPrice] => 7090
[Change] => -1.02
[Open] => 7163
[Close] => 7090
)
[Error] =>
)
I want to build mapping variable array to access the object easily.
$xmap["a"]["bid"] = "bid";
$xmap["b"]["bid"] = "Data->BidPrice";
Let's assume $content has the first example object, this will work:
print $content->{$xmap["a"]["bid"]}; // the result is 7128.9
As for the second example object, it does not:
print $content->{$xmap["b"]["bid"]}; // the result is PHP Notice: Undefined property: stdClass::$Data->BidPrice in ...
Can this be done or am I stuck with if statements!

First, convert all the objects into assoc. arrays by using json_encode/decode. You'll find this code multiple times here in stackoverflow
$arr = json_decode(json_encode($obj), true);
Second, I recommend a dot-notation for the key-path, plus a tiny function to find the value in a multidimensional array.
Example:
function fromArray($key, $arr) {
$keys = explode('.', $key);
foreach($keys as $k) {
if (!isset($arr[$k]))
return array(); // return empty array
$arr = $arr[$k];
}
return $arr; // can be a scalar value or array
}
$key = 'my.super.array.content';
$array = ['my' => [
'super' => [
'array' =>[
'content'=>4711
]
]
]
];
var_dump($array, fromArray($key, $array));
/*
array(1) {
["my"]=>
array(1) {
["super"]=>
array(1) {
["array"]=>
array(1) {
["content"]=>
int(4711)
}
}
}
}
int(4711)
*/
I found the dot-notation very useful when dealing with complex structures.

You can convert your objects to arrays and build a large mapping array that needs to be maintained and then explode it and loop through it to access the other arrays; or you can try using patterns. I'm thinking Adapter, but maybe another is a better fit. This is using your second object as an example, but just add as many as needed:
class ContentAdapter {
public function __get($name) {
return $this->obj->{$xmap[$name]};
}
}
class ContentAdapter_API_B extends ContentAdapter {
public $xmap = ['bid' => 'BidPrice', 'ask' => 'AskPrice'];
public function __construct($obj) {
$this->obj = $obj->data;
}
}
Now it is consistant regardless of the object since each has an adapter:
$content = new ContentAdapter_API_B($content);
echo $content->bid;
Using your first object you can either create a child as well (ContentAdapter_API_A in case the structure ever changes) or instantiate directly:
$content = new ContentAdapter($content);
echo $content->bid;
Or obviously just use it as is:
echo $content->bid;
An alternate way without inheritance is to use getters:
class ContentAdapter_API_B {
public function __construct($obj) {
$this->obj = $obj->data;
}
public function getBid() { return $this->obj->BidPrice; }
}
So long as the methods are consistent then it will always work:
$content = new ContentAdapter_API_B($content);
echo $content->getBid;

Related

Check all function arguments

I've been wondering how I could check all my parameters efficiently in any function in a clean and concise way.
I've come up with :
function fooBar($myArg, $mySecondArg, $myOptionnalArg = "defaultValue"){
if(!isset($myArg, $mySecondArg, $myOptionnalArg){
die(__FUNCTION__ . ":missing parameter");
}
if(empty($myArg, $mySecondArg, $myOptionnalArg){
die(__FUNCTION__ . ":empty parameter");
}
//Do stuff
}
What I'm looking for is more like:
function fooBar($myArg, $mySecondArg, $myOptionnalArg = "defaultValue"){
$argArray = func_get_args();
foreach($argArray as $a){
if(empty($a)){
die(__FUNCTION__.":".get_arg_name($a)."is empty");
}
if(!isset($a)){
die(__FUNCTION__.":".get_arg_name($a)."is unset");
}
}
//Do stuff
}
Wich doesn't work since get_arg_name(); is pure fiction, and func_get_args(); returns the actual parameters, rather than the ones defined in the prototype.
Could someone give me a hint about it? Any other good/clean way to achieve that is also welcome.
Thanks
With reflection only. Some example:
function test($a, $b, $c) {
$function = new ReflectionFunction(__FUNCTION__);
$parameters = $function->getParameters();
print_r($parameters);
}
test(0, 1, 2);
You will see something like this:
Array
(
[0] => ReflectionParameter Object
(
[name] => a
)
[1] => ReflectionParameter Object
(
[name] => b
)
[2] => ReflectionParameter Object
(
[name] => c
)
)
Its possible to get what you need with using php reflection:
http://php.net/manual/en/book.reflection.php
Please, read more about how to use it.

json encode is not working with array of objects

I want to convert array of objects to json encoding, I make like this
$allVisits = $mapper->getAllVisits($year, $month);
echo json_encode($allVisits);
and here's is getAllVisists method
function getAllVisits($year, $month) {
$where = array(
'year = ?' => $year,
'month = ?' => $month
);
$resultSet = $this->getDbTable()->fetchAll( $where);
$visitsEntries = array();
foreach ($resultSet as $row) {
$entry = new Visits_Model_Visit();
$entry->setId($row->visit_id)
->setDay($row->day)
->setDate($row->date)
->setTarget($row->target)
->setStatus($row->visit_status)
->setTime($row->visit_time);
$visitsEntries[] = $entry;
}
return $visitsEntries;
}
when I echo the size of $allVisits it return correct number of records, but in js the values are received empty like this [{},{},{},{}]
Edit
When I print_r($allVisists) brfore encoding it it returns
Array
(
[0] => Visits_Model_Visit Object
(
[day:private] => sunday
[date:private] => 2012-03-06
[target:private] => شسي
[id:private] => 1
[status:private] => 0
[time:private] => 12:00:00
)
[1] => Visits_Model_Visit Object
(
[day:private] => sunday
[date:private] => 2012-03-06
[target:private] => clinnics
[id:private] => 4
[status:private] => 0
[time:private] => 00:00:00
)
[2] => Visits_Model_Visit Object
(
[day:private] => Tuesday
[date:private] => 2012-03-06
[target:private] => clinnics
[id:private] => 5
[status:private] => 0
[time:private] => 00:00:00
)
[3] => Visits_Model_Visit Object
(
[day:private] => Wednesday
[date:private] => 2012-03-28
[target:private] => ??????? ???????
[id:private] => 7
[status:private] => 0
[time:private] => 12:00:00
)
)
You are using json_encode with objects that don't have any public members. json_encode only works on the members it can "see", that's why those are empty.
Since PHP 5.4 you can make use of the JsonSerializable interface to control which data will be offered for json_encode, e.g.:
class Visits_Model_Visit implements JsonSerializable {
...
public function jsonSerialize() {
return (object) get_object_vars($this);
}
...
}
If you are below 5.4 you can also implement that function w/o extending from the interface and then assigning the correct value manually:
$visitsEntries[] = $entry->jsonSerialize();
Hope this helps.
As Ray says if your class properties are protected or private, these will not be jsoned.
Since PHP 5.4 instead of using the commented toJson method, you have the ability to specify which data will be serialized implementing the JsonSerializable interface, so json_encode knows how to work on this.
/* PHP >= 5.4 only */
class Visits_Model_Visit implement JsonSerializable {
public function jsonSerialize()
{
return array(
'day' => $this->day,
'date' => $this->date,
'target' => $this->target,
'id' => $this->id,
'status' => $this->status,
);
}
}
By default, json_encode() only serializes public properties of an object. Making all properties you want serialized public is NOT the solution! PHP 5.4 and later has the JsonSerializable interface, but I propose a straightforward solution for earlier versions of PHP.
Since JsonSerializable is only part of PHP 5.4 and later, create it yourself.
if (!interface_exists('JsonSerializable')) {
interface JsonSerializable {
public function jsonSerialize();
}
}
That wasn't so hard, was it? Now we can implement JsonSerializable without worrying about what version of PHP we are using!
class Visits_Model_Visit implements JsonSerializable {
...
// Only put properties here that you want serialized.
public function jsonSerialize() {
return Array(
'day' => $this->day,
'date' => $this->date,
'target' => $this->target,
'id' => $this->id,
'status' => $this->status,
'obj' => $this->obj->jsonSerialize(), // example for other objects
'time' => $this->time
);
}
...
}
Now you can just call jsonSerialize() to get an associative array that you can encode with json_encode().
...
$entry = new Visits_Model_Visit();
$entry->setId($row->visit_id)
->setDay($row->day)
->setDate($row->date)
->setTarget($row->target)
->setStatus($row->visit_status)
->setTime($row->visit_time);
$visitsEntries[] = $entry->jsonSerialize();
...
You may then call json_encode($visitsEntries) to get your desired result.
[
{
"day":"sunday",
"date":"2012-03-06",
"target":"\u0634\u0633\u064a",
"id":1,
"status":0,
"time":"12:00:00"
},
{
"day":"sunday",
"date":"2012-03-06",
"target":"clinnics",
"id":4,
"status":0,
"time":"00:00:00"
},
...
]
Are the properties private or protected for the object? If so, json encode can't see them inside the object. I get around this by creating a 'toJson' method in my objects that I need to serialize into json. In this method, I walk the objects properties and manually construct a generic object, that I pass to json_encode. Then I return the Json string from this method.
Do not just make all your object properties public!!!!
For those who are looking for simple answer, unlike other complicated answers my is piece of art:
json_encode(array(
Protocol::PARAM_CODE => Protocol::CODE_SUCCESS,
Protocol::PARAM_USER => (object)$user->jsonSerialize()
));
Even when $user->jsonSerialize() outputs stdObject, json_encode is so dumb, it has no idea it is object so you have to state that explicitly with casting it to (object) - don't you love PHP for it's simplicity?

Modify PHP Object Property Name

In PHP is it possible to change an Objects property key/name? For example:
stdClass Object
(
[cpus] => 2
[created_at] => 2011-05-23T01:28:29-07:00
[memory] => 256
)
I wish to change the key created_at to created in the Object leaving an object that looks like:
stdClass Object
(
[cpus] => 2
[created] => 2011-05-23T01:28:29-07:00
[memory] => 256
)
$object->created = $object->created_at;
unset($object->created_at);
Something like an adapter class may be a more robust choice though, depending on where and how often this operation is necessary.
class PC {
public $cpus;
public $created;
public $memory;
public function __construct($obj) {
$this->cpus = $obj->cpu;
$this->created = $obj->created_at;
$this->memory = $obj->memory;
}
}
$object = new PC($object);
No, since the key is a reference to the value, and not a value itself.
You're best off copying the original, then removing it.
$obj->created = $obj->created_at;
unset(obj->created_at);
Its similar to #deceze adapter, but without the need to create an extra class
$object = (object) array(
'cpus' => $obj->cpus,
'created' => $obj->created_at,
'memory' => $obj->memory
);

Get a PHPActiveRecord result as simple array, not array of objects

I would like to have a simple a method, that can give back PHP Activerecord results as simple/associative arrays, not an array of ActiveRecord Objects.
In Ruby I believe this is done perhaps with .map() method. (I am not a Ruby guy...)
What I want is a simple method call, like toArray() in Zend_DB_Table, not a foreach, or something like that, but I can't seem to find it in their docs.
In PHP ActiveRecord getting a result is really easy:
$settings = SystemSettings::all();
But it gives back something like this:
[0] => SystemSettings Object
(
[errors] =>
[attributes:ActiveRecord\Model:private] => Array
(
[param] => author
[value] => Hawle
)
[__dirty:ActiveRecord\Model:private] => Array
(
)
[__readonly:ActiveRecord\Model:private] =>
[__relationships:ActiveRecord\Model:private] => Array
(
)
[__new_record:ActiveRecord\Model:private] =>
)
[1] => SystemSettings Object
(
[errors] =>
[attributes:ActiveRecord\Model:private] => Array
(
[param] => base_url
[value] => example.com
)
[__dirty:ActiveRecord\Model:private] => Array
(
)
[__readonly:ActiveRecord\Model:private] =>
[__relationships:ActiveRecord\Model:private] => Array
(
)
[__new_record:ActiveRecord\Model:private] =>
)
While this is really great in many cases, here, I would just like to have a simple array, like this:
Array
(
[author] => Hawle
[base_url] => example.com
)
I had a similar issue hopefully this can help someone else who stumbles on it. Obviously, this is specific to phpactiverecord.org.
In /lib/Model.php I added the following function:
public function to_array(array $options=array())
{
return $this->serialize('array', $options);
}
In /lib/Serialization.php I added the following class
class arraySerializer extends Serialization
{
public static $include_root = false;
public function to_s()
{
return self::$include_root ? array(strtolower(get_class($this->model)) => $this->to_a()) : $this->to_a();
}
}
I can then call ->to_array() and get an array back.
Hope this helps!
I was searching for the answer to this question in order to produce an array of results that could be easily json-encoded and sent as the response to an ajax call. I wanted only the attributes of each object in the array of results.
Unfortunately, you can't just call to_json() on each result and then json-encode the entire thing; you end up with double-encoding. Fortunately, though, the function and class posted by #willwashburn to solve this problem have now been included in php-activerecord, though they don't seem to have made it into the online documentation.
To return an array of results, do the following:
$data = MyModel::find('all');
foreach ($data as &$result) {
$result = $result->to_array();
}
Your entire result set will now be an array of arrays, containing only the attributes of each object. You can then do something like
echo(json_encode($data));
if you want to send it as the response to an ajax call.
This is my solution:
$posts = Post::find('all');
$arrayResult = array_map(function($res){
return $res->attributes();
}, $posts);
printf('<pre>%s</pre>', print_r($arrayResult, true));
class MyPHPActiveRecord extends PHPActiveRecord {
public function toJSON() {
return json_encode(get_object_vars($this));
}
}
You could do it like this:
funciton ar2array($settings){
$arr = array();
foreach($settings as $fieldObj){
$fieldName = $fieldObj->attributes["param"];
$fieldValue = $fieldObj->attributes["value"];
$arr[$fieldName] = $fieldValue;
}
return $arr;
}
$resultAsYouWant = ar2array($settings);
Hope this helps. Cheers
PS: If ->attributes is private use its accesor method (there must be one) as ->getAttributes() or equivalent.
I found this looking for solution of the same problem that I encountered using Yii framework - there is simplier way to do this in Yii.
$posts = Posts::model()->findAll();
foreach($posts as $result)
{
print_r($result->attributes);
}
It prints simple array as requested:
Array
(
[id] => 1
[title] => Title
[text] => Text
)
Hope it helps somebody.
My solution:
Added the following method to the utils class found in lib\Utils.php
public static function results_to_json($data)
{
$arr = array();
if(count($data)>0){
foreach($data as $row){
array_push($arr, $row->to_array());
}
}
return json_encode($arr);
}
Call by:
echo \ActiveRecord\Utils::results_to_json($dataobject);
Obviously this is no longer relevant to the OP; however, considering that it still took me over an hour to find a solution for this (no thanks to php-activerecords docs), this may help someone else.
$r = Model::find('$id')->attributes();
$a = [];
foreach ($r as $k => $v)
{
$a[$k] = $v;
}
Perhaps not the most elegant, but works perfectly.

Creating an object from a dynamic database call

I'm trying to make a class that takes some unspecified data from a database query (passed to my class as an array) and turns it into a PHP object with properties that are created dynamically from the data passed to it.
Like so:
class myLibrary_Item extends myLibrary
{
private function __construct($vars)
{
foreach($vars as $var => $val)
{
$this->$var => $val;
}
}
private function __set($var, $val)
{
$this->$var => $val;
}
private function __get($var)
{
return $this->$var;
}
}
$myArray = array(
'firstName' => 'Joe',
'lastName' => 'Carrington'
);
$myObject = new myLibrary_Item($myArray)
echo $myObject->firstName;
//Hopefully will output Joe
So, my question, is this a good idea at all? Am I missing the point of OOP here? Should I learn about PDO instead? It seems like this could be a big help, but I don't want to hammer out the bugs if this is going to bite me in the butt later.
I would say this is indeed redundant to PDOStatement->fetchObject.
php > $dbh = new PDO("mysql:host=localhost;dbname=test", "guest");
php > $stat = $dbh->prepare("select * from books");
php > $stat->execute();
php > while($row = $stat->fetchObject())
php > print_r($row);
stdClass Object
(
[title] => Hitchhiker's Guide
[author] => Douglas Adams
[isbn] => 0345391802
[publisher] => Del Rey
[year] => 1995
[summary] => Arthur Dent accidentally saves the world.
)

Categories