Objectives:
I am trying to read certain parts of any json file by letting the user input the desired properties.
Say I have an object that looks like this:
"adverts": [
{
"id": "33655628",
"companyInfo": {
"companyName": "Company A",
"homepage": "http://companya.com",
"companyText": null
},
...
]
I want to access the properties by assigning the property name or "path" by a variable.
Accessing the first level ($item->$_id) works fine but how do I access a nested property companyName directly with by a variable such as
$_name = "companyInfo->companyName";
Ex:
$_repeat = "adverts";
$_id = "id";
$_name = ??????
foreach($data->$_repeat as $item){
var_dump($item->$_id);
var_dump($item->$_name);
}
EDIT:
As clarification: I want this to be universal for any JSON object!
PRELIMINARY SOLUTION:
I got the desired results by looping as suggested by #Carlos:
$_title_a = explode(".",$_title);
$current = $document;
foreach($_title_a as $a){
$current = $current->$a;
}
var_dump($current);
If someone has a better suggestion, I would be glad to hear it. Thanks everybody!
Why do you exactly need this?
All you have to do is:
$jsonData = json_decode($jsonString, true);
echo $jsonData['adverts'][0]['companyInfo']['companyName'];
//or
foreach($jsonData['adverts'] as $advert){
echo $advert['companyInfo']['companyName'];
}
It seems that you have json data with you..you can use below code..
$obj= json_decode($yourJsonData);
print_r($obj);
foreach ($obj as $key => $value) {
$companyinfo = $value['companyInfo']['companyName'];
}
In foreach loop you will get require key and values..this is just a example for you..
I'm looking for a sort of reversed func_get_args(). I would like to find out how the parameters were named when function was defined. The reason for this is I don't want to repeat myself when using setting variables passed as arguments through a method:
public function myFunction($paramJohn, $paramJoe, MyObject $paramMyObject)
{
$this->paramJohn = $paramJohn;
$this->paramJoe = $paramJoe;
$this->paramMyObject = $paramMyObject;
}
Ideally I could do something like:
foreach (func_get_params() as $param)
$this->${$param} = ${$param};
}
Is this an overkill, is it a plain stupid idea, or is there a much better way to make this happen?
You could use Reflection:
$ref = new ReflectionFunction('myFunction');
foreach( $ref->getParameters() as $param) {
echo $param->name;
}
Since you're using this in a class, you can use ReflectionMethod instead of ReflectionFunction:
$ref = new ReflectionMethod('ClassName', 'myFunction');
Here is a working example:
class ClassName {
public function myFunction($paramJohn, $paramJoe, $paramMyObject)
{
$ref = new ReflectionMethod($this, 'myFunction');
foreach( $ref->getParameters() as $param) {
$name = $param->name;
$this->$name = $$name;
}
}
}
$o = new ClassName;
$o->myFunction('John', 'Joe', new stdClass);
var_dump( $o);
Where the above var_dump() prints:
object(ClassName)#1 (3) {
["paramJohn"]=>
string(4) "John"
["paramJoe"]=>
string(3) "Joe"
["paramMyObject"]=>
object(stdClass)#2 (0) {
}
}
Code snippet that creates an array containing parameter names as keys and parameter values as corresponding values:
$ref = new ReflectionFunction(__FUNCTION__);
$functionParameters = [];
foreach($ref->getParameters() as $key => $currentParameter) {
$functionParameters[$currentParameter->getName()] = func_get_arg($key);
}
While it's not impossible to do it, it's usually better to use another method. Here is a link to a similar question on SO :
How to get a variable name as a string in PHP?
What you could do is pass all your parameters inside of an object, instead of passing them one by one. I'm assuming you are doing this in relation to databases, you might want to read about ORMs.
get_defined_vars will give you the parameter names and their values, so you can do
$params = get_defined_vars();
foreach ($params as $var=>$val) {
$this->${var} = $val;
}
This is the class that I have created in PHP
class userinfo
{
public $username;
public $totalscore;
public $userid;
}
The code below is in a finite loop, and i is set to 0 before entering the loop. And the variable user_array is defined to be an array using the following code:
$user_array = array();
(some code here...)
$i++;
$user_array[i] = new userinfo();
$user_array[i]->totalscore = $stattotal;
$user_array[i]->userid = $id;
For some reason I cant understand why this wont work. I need to create an array of objects. And each object must hold three variables. How do I go about doing so ?
Thank you in Adv. for your Help !
Worked fine for me, remember the $ when using variables.
http://phpfiddle.org/main/code/muv-yx6
You must have a dollar sign ($i) before all variables in PHP.
You can use get_class_vars method to get all properties of class
$my_class = new myclass();
$class_vars = get_class_vars(get_class($my_class));
foreach ($class_vars as $name => $value) {
echo "$name : $value\n";
}
SOURCE : http://php.net/manual/en/function.get-class-vars.php
So, I have a object with structure similar to below, all of which are returned to me as stdClass objects
$person->contact->phone;
$person->contact->email;
$person->contact->address->line_1;
$person->contact->address->line_2;
$person->dob->day;
$person->dob->month;
$person->dob->year;
$album->name;
$album->image->height;
$album->image->width;
$album->artist->name;
$album->artist->id;
etc... (note these examples are not linked together).
Is it possible to use variable variables to call contact->phone as a direct property of $person?
For example:
$property = 'contact->phone';
echo $person->$property;
This will not work as is and throws a E_NOTICE so I am trying to work out an alternative method to achieve this.
Any ideas?
In response to answers relating to proxy methods:
And I would except this object is from a library and am using it to populate a new object with an array map as follows:
array(
'contactPhone' => 'contact->phone',
'contactEmail' => 'contact->email'
);
and then foreaching through the map to populate the new object. I guess I could envole the mapper instead...
If i was you I would create a simple method ->property(); that returns $this->contact->phone
Is it possible to use variable variables to call contact->phone as a direct property of $person?
It's not possible to use expressions as variable variable names.
But you can always cheat:
class xyz {
function __get($name) {
if (strpos($name, "->")) {
foreach (explode("->", $name) as $name) {
$var = isset($var) ? $var->$name : $this->$name;
}
return $var;
}
else return $this->$name;
}
}
try this code
$property = $contact->phone;
echo $person->$property;
I think this is a bad thing to to as it leads to unreadable code is is plain wrong on other levels too, but in general if you need to include variables in the object syntax you should wrap it in braces so that it gets parsed first.
For example:
$property = 'contact->phone';
echo $person->{$property};
The same applies if you need to access an object that has disalowed characters in the name which can happen with SimpleXML objects regularly.
$xml->{a-disallowed-field}
If it is legal it does not mean it is also moral. And this is the main issue with PHP, yes, you can do almost whatever you can think of, but that does not make it right. Take a look at the law of demeter:
Law of Demeter
try this if you really really want to:
json_decode(json_encode($person),true);
you will be able to parse it as an array not an object but it does your job for the getting not for the setting.
EDIT:
class Adapter {
public static function adapt($data,$type) {
$vars = get_class_vars($type);
if(class_exists($type)) {
$adaptedData = new $type();
} else {
print_R($data);
throw new Exception("Class ".$type." does not exist for data ".$data);
}
$vars = array_keys($vars);
foreach($vars as $v) {
if($v) {
if(is_object($data->$v)) {
// I store the $type inside the object
$adaptedData->$v = Adapter::adapt($data->$v,$data->$v->type);
} else {
$adaptedData->$v = $data->$v;
}
}
}
return $adaptedData;
}
}
OOP is much about shielding the object's internals from the outside world. What you try to do here is provide a way to publicize the innards of the phone through the person interface. That's not nice.
If you want a convenient way to get "all" the properties, you may want to write an explicit set of convenience functions for that, maybe wrapped in another class if you like. That way you can evolve the supported utilities without having to touch (and possibly break) the core data structures:
class conv {
static function phone( $person ) {
return $person->contact->phone;
}
}
// imagine getting a Person from db
$person = getpersonfromDB();
print conv::phone( $p );
If ever you need a more specialized function, you add it to the utilities. This is imho the nices solution: separate the convenience from the core to decrease complexity, and increase maintainability/understandability.
Another way is to 'extend' the Person class with conveniences, built around the core class' innards:
class ConvPerson extends Person {
function __construct( $person ) {
Person::__construct( $person->contact, $person->name, ... );
}
function phone() { return $this->contact->phone; }
}
// imagine getting a Person from db
$person = getpersonfromDB();
$p=new ConvPerson( $person );
print $p->phone();
You could use type casting to change the object to an array.
$person = (array) $person;
echo $person['contact']['phone'];
In most cases where you have nested internal objects, it might be a good time to re-evaluate your data structures.
In the example above, person has contact and dob. The contact also contains address. Trying to access the data from the uppermost level is not uncommon when writing complex database applications. However, you might find your the best solution to this is to consolidate data up into the person class instead of trying to essentially "mine" into the internal objects.
As much as I hate saying it, you could do an eval :
foreach ($properties as $property) {
echo eval("return \$person->$property;");
}
Besides making function getPhone(){return $this->contact->phone;} you could make a magic method that would look through internal objects for requested field. Do remember that magic methods are somewhat slow though.
class Person {
private $fields = array();
//...
public function __get($name) {
if (empty($this->fields)) {
$this->fields = get_class_vars(__CLASS__);
}
//Cycle through properties and see if one of them contains requested field:
foreach ($this->fields as $propName => $default) {
if (is_object($this->$propName) && isset($this->$propName->$name)) {
return $this->$propName->$name;
}
}
return NULL;
//Or any other error handling
}
}
I have decided to scrap this whole approach and go with a more long-winded but cleaner and most probably more efficient. I wasn't too keen on this idea in the first place, and the majority has spoken on here to make my mind up for me. Thank for you for your answers.
Edit:
If you are interested:
public function __construct($data)
{
$this->_raw = $data;
}
public function getContactPhone()
{
return $this->contact->phone;
}
public function __get($name)
{
if (isset($this->$name)) {
return $this->$name;
}
if (isset($this->_raw->$name)) {
return $this->_raw->$name;
}
return null;
}
In case you use your object in a struct-like way, you can model a 'path' to the requested node explicitly. You can then 'decorate' your objects with the same retrieval code.
An example of 'retrieval only' decoration code:
function retrieve( $obj, $path ) {
$element=$obj;
foreach( $path as $step ) {
$element=$element[$step];
}
return $element;
}
function decorate( $decos, &$object ) {
foreach( $decos as $name=>$path ) {
$object[$name]=retrieve($object,$path);
}
}
$o=array(
"id"=>array("name"=>"Ben","surname"=>"Taylor"),
"contact"=>array( "phone"=>"0101010" )
);
$decorations=array(
"phone"=>array("contact","phone"),
"name"=>array("id","name")
);
// this is where the action is
decorate( $decorations, &$o);
print $o->name;
print $o->phone;
(find it on codepad)
If you know the two function's names, could you do this? (not tested)
$a = [
'contactPhone' => 'contact->phone',
'contactEmail' => 'contact->email'
];
foreach ($a as $name => $chain) {
$std = new stdClass();
list($f1, $f2) = explode('->', $chain);
echo $std->{$f1}()->{$f2}(); // This works
}
If it's not always two functions, you could hack it more to make it work. Point is, you can call chained functions using variable variables, as long as you use the bracket format.
Simplest and cleanest way I know of.
function getValueByPath($obj,$path) {
return eval('return $obj->'.$path.';');
}
Usage
echo getValueByPath($person,'contact->email');
// Returns the value of that object path
I need to create a column-system for Wordpress with shortcodes, which is not a problem, but I'm trying to make it with less code.
I have an array with the data needed, I loop through it, create a unique-named function and set it as shortcode-function. The third step is a mystery. How can I create a function from a variable.
Here's an example, how it should be done:
$data[] = "first";
$data[] = "second";
foreach($data as $key => $value) {
function $value($atts,$content) {
return '<div class="'.$value.'">'.$content.'</div>';
}
add_shortcode($value,$value);
}
However, it seems that it's not possible to make it work like that in PHP. Is there any way to make this work, as I would not want to write all the (identical) functions separated. I could make the shortcode something like [col first]text[/col] but the client wants to have different names for every one of them.
you can use the double dollar syntax to use the value of a variable as a variable identifier,
Example:
$variable = "test";
$$variable = "value of test"
echo $test; //or echo $$variable;
I have never tried but you way want to try:
foreach($data as $key => $value)
{
function $$value($atts,$content)
{
}
add_shortcode($value,$value);
}
or a function like create_function
if your using PHP 5.3 or greater then you can do something like so:
$$value = function()
{
}
which should work fine
I'm not sure how WP invocates the functions, but if it uses call_user_func then you might cheat by using an object with virtual methods:
class fake_functions {
function __call($name, $params) {
return '<div class="'.$name.'">'.$params[1].'</div>';
}
}
$obj = new fake_functions();
foreach ($data as $value) {
add_shortcode($value, array($obj,$value));
}