php xpath returns Array(0) - php

I read the following XML:
http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml
controller:
$ECB_rates = array();
$currencies = explode(',', 'GBP,USD,RUB,AUD');
foreach($currencies as $currency) {
$ECB_rates[$currency] = $ECB_XML->xpath('//Cube/Cube/Cube[#currency="' . $currency . '"]/#rate');
}
$this->set('ECB_rates', $ECB_rates);
view:
var_dump($ECB_rates);
and I get the following:
array(4) { ["GBP"]=> array(0) { } ["USD"]=> array(0) { } ["RUB"]=> array(0) { } ["AUD"]=> array(0) { } }
I can't figure out why the rates are returned as empty array.

The document has a default namespace, which can commonly confuse XPath expressions if not done correctly. The other part is that xpath() returns an array of SimpleXMLElements which also doesn't help your cause.
The following code first registers the default namespace with the prefix 'def' and then (I've simplified the expression as well) uses this as a prefix to find the <Cube> element with the currency you want. Then it takes the first result (there should only be one anyway) and casts it to a string to make it more useful.
$ECB_XML->registerXPathNamespace("def", "http://www.ecb.int/vocabulary/2002-08-01/eurofxref");
$ECB_rates = array();
$currencies = explode(',', 'GBP,USD,RUB,AUD');
foreach($currencies as $currency) {
$ECB_rates[$currency] = (string)$ECB_XML->xpath('//def:Cube[#currency="' . $currency . '"]/#rate')[0];
}
var_dump($ECB_rates);
Which gives...
array(4) {
'GBP' =>
string(7) "0.87295"
'USD' =>
string(6) "1.2234"
'RUB' =>
string(7) "70.8270"
'AUD' =>
string(6) "1.5934"
}
Update:
You could (if the XML format is stable) just use SimpleXML element/attribute access. Checking if the currency is in the array of currencies your after...
foreach($ECB_XML->Cube->Cube->Cube as $rate) {
$currency = (string)$rate["currency"];
if ( in_array($currency, $currencies)) {
$ECB_rates[$currency] = (string)$rate["rate"];
}
}
This may give you the items in a different order, but this might not be an issue.

Related

PHP: Access Object Properties by String

Okay, Lets say I have an object with properties like this:
$testObj->wind->windi // this returns a value like 100 mph
$testObj->wind->windii // this returns a value like 110 mph
I need to access a property but I don't know what property explicitly, Instead I have a string like this:
$str = 'wind->windii';
Calling predefined values works:
echo $testObj->wind->windii; //results in 110 mph
The Problem: I need a way to get the values without hard coding the actual properties:
echo $testObj->$str; //and have it result in 110 mph
I thought this was a variable variables situation but I had no luck getting that to work and after a few hours of frustration I'm asking for help
*edit:
I also need to mention that the object name changes as well so we can't hard code $testObj but rather be able to take any object and any string and get it's properties
Also, bracket notation isn't working, it results in "Undefined property: stdClass::$wind->elevation"
For those following at home, this is the var_dump of $testObj
object(stdClass)#299 (2) {
["snow"]=>
object(stdClass)#315 (3) {
["elevation"]=>
string(5) "365.4"
["results"]=>
string(1) "6"
["status"]=>
int(1)
}
["wind"]=>
object(stdClass)#314 (5) {
["elevation"]=>
string(5) "365.4"
["windi"]=>
string(7) "100 mph"
["windii"]=>
string(7) "110 mph"
["windiii"]=>
string(7) "115 mph"
["status"]=>
int(1)
}
}
you can change it to this :
$str1 = 'wind';
$str2 = 'windii';
echo $testObj->{$str1}->{$str2};
In PHP you can access properties dynamically such as:
`
class test {
public testvar;
}
$newtest = new test();
$property = "testvar"
$newtest->{"testvar"} //or $newtest->{$property};
`
Depending on what you are doing you can you a foreach loop to get the key value pair as well.
`
foreach($newtest as $key=>$val){
}
`
Finally I have working code.
With mental support from #NigelRen
Emotional support from #FunkFortyNiner
And most of the code from this question about arrays
Test Object:
$obj = json_decode('{"snow":{"elevation":"365.4","results":"6","status":1},"wind":{"elevation":"365.4","windi":"100 mph","windii":"110 mph","windiii":"115 mph","status":1}}');
Test Directory:
$path = 'wind:windii';
Getter:
function get($path, $obj) {
$path = explode(':', $path);
$temp =& $obj;
foreach($path as $key) {
$temp =& $temp->{$key};
}
return $temp;
}
var_dump(get($path, $obj)); //dump to see the result
Setter:
function set($path, &$obj, $value=null) {
$path = explode(':', $path);
$temp =& $obj;
foreach($path as $key) {
$temp =& $temp->{$key};
}
$temp = $value;
}
//Tested with:
set($path, $obj, '111');

Avoid deep nesting when iterating through many combinations

Is there a more declarative / less horrible way of writing this (deliberately simplified) code?
I realise I can use ->each(), but that still doesn't get rid of the nesting in this case?
Note I do need to generate all the combinations, and I have a mixture of things to loop through - configuration data, ordinary arrays, Eloquent, though obviously I could convert them first...
foreach(config('app.goals') as $goal) {
foreach(config('app.age_groups') as $ages) {
foreach($regions as $region_name => $region_id) {
foreach($interests->pluck('name')->prepend('') as $interest) {
foreach(config('app.devices') as $device) {
$record = new Foo();
$record->goal = $goal;
$record->age = $age;
$record->region = $region_id;
$record->interest = $interest;
$record->device = $device;
$record->save();
}
}
}
}
}
Unclear if there's a Collection method that can help? e.g.
holygrail(config('app.goals'),config('app.age_groups'),$regions...)->each(function($combination) {
// logic for every combination
});
crossJoin() seems to do what I need, it creates an array matrix of every combination.
$matrix = collect($goals)->crossJoin(
$ages,
$regions,
$interests,
$devices
);
So you have an array full of elements, each of them a permutation, e.g:
array(5) {
[0]=>
string(5) "REACH"
[1]=>
string(5) "18-65"
[2]=>
string(9) "Worldwide"
[3]=>
string(11) "Programming"
[4]=>
string(7) "desktop"
}
The Laravel source is in: \Illuminate\Support\Arr::crossJoin

Get all object from a "group" in PHP from JSON array

I'm trying to catch all objects in PHP from a JSON array, I need all the objects that will appear under ["Elements"]. So how would this be possible if I:
1.) Don't know the "name" of the object and don't know the content inside it.
2.) What I would like to achieve is to get the first objects value inside Elements, and then get the "content" inside of it, regardless of the names (there could be multiple objects)
Here is a var_dump of the JSON:
object(stdClass)#1 (1) {
["Canvas"]=>
array(1) {
[0]=>
["Elements"]=>
object(stdClass)#18 (2) {
["textHolder2"]=>
object(stdClass)#19 (1) {
["textContent"]=>
string(12) "Text to edit"
}
["textHolder1"]=>
object(stdClass)#20 (1) {
["textContent"]=>
string(12) "Text to edit"
}
}
}
}
}
Use foreach.
$json = json_decode( $input, true );
$elems = $json['canvas']['Elements'];
foreach( $elems as $key => $value ) {
echo "{$key} is an array/object:\n";
echo var_dump( $value );
}
You could use array_keys() if you need to know what keys are inside $value or you could another foreach loop, but I am assuming you will have at least some clue what keys could be in $value.

How to add (any type )value to an array(by specified index) in php language

As title, I did it like below:
$array=array(0,1,2,3);
$result=array();
function makeArray($array,$result,$value){
$str='$result';
for ($i=0;$i<count($array);$i++){
$str.='['.$i.']';
}
$str.='="'.$value.'";';
eval($str);
return $result;
}
It can realize result when param $result is an empty array,but It report an error when $result is an array.
Error like :
Cannot use a scalar value as an array.
Anyways can realize it?
Thanks first!
Use pass by reference, not eval:
function makeArray($indexes, &$result, $value) {
$here =& $result;
foreach ($indexes as $i) {
if (!(isset($here[$i]) && is_array($here[$i]))) {
$here[$i] = array();
}
$here =& $here[$i];
}
$here = $value;
}
$array=array(0,1,2,3);
$result=array();
makeArray($array, $result, 3);
var_dump($result);
Output:
array(1) {
[0]=>
array(1) {
[1]=>
array(1) {
[2]=>
array(1) {
[3]=>
int(3)
}
}
}
}
Putting & before a function parameter means it will be passed by reference, so modifications to the variable inside the function will affect the original variable that was passed. And using =& in an assignment assigns a reference, so the target variable is an alias for the source.

Convert multidimensional objects to array [duplicate]

This question already has answers here:
Convert a PHP object to an associative array
(33 answers)
Closed 6 months ago.
I'm using amazon product advertising api. Values are returned as a multidimensional objects.
It looks like this:
object(AmazonProduct_Result)#222 (5) {
["_code":protected]=>
int(200)
["_data":protected]=>
string(16538)
array(2) {
["IsValid"]=>
string(4) "True"
["Items"]=>
array(1) {
[0]=>
object(AmazonProduct_Item)#19 (1) {
["_values":protected]=>
array(11) {
["ASIN"]=>
string(10) "B005HNF01O"
["ParentASIN"]=>
string(10) "B008RKEIZ8"
["DetailPageURL"]=>
string(120) "http://www.amazon.com/Case-Logic-TBC-302-FFP-Compact/dp/B005HNF01O?SubscriptionId=AKIAJNFRQCIJLTY6LDTA&tag=*********-20"
["ItemLinks"]=>
array(7) {
[0]=>
object(AmazonProduct_ItemLink)#18 (1) {
["_values":protected]=>
array(2) {
["Description"]=>
string(17) "Technical Details"
["URL"]=>
string(217) "http://www.amazon.com/Case-Logic-TBC-302-FFP-Compact/dp/tech-data/B005HNF01O%3FSubscriptionId%3DAKIAJNFRQCIJLTY6LDTA%26tag%*******-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB005HNF01O"
}
}
[1]=>
object(AmazonProduct_ItemLink)#17 (1) {
["_values":protected]=>
array(2) {
I mean it also has array inside objects. I would like to convert all of them into a multidimensional array.
I know this is old but you could try the following piece of code:
$array = json_decode(json_encode($object), true);
where $object is the response of the API.
You can use recursive function like below:
function object_to_array($obj, &$arr)
{
if (!is_object($obj) && !is_array($obj))
{
$arr = $obj;
return $arr;
}
foreach ($obj as $key => $value)
{
if (!empty($value))
{
$arr[$key] = array();
objToArray($value, $arr[$key]);
}
else {$arr[$key] = $value;}
}
return $arr;
}
function convertObjectToArray($data) {
if (is_object($data)) {
$data = get_object_vars($data);
}
if (is_array($data)) {
return array_map(__FUNCTION__, $data);
}
return $data;
}
Credit to Kevin Op den Kamp.
I wrote a function that does the job, and also converts all json strings to arrays too. This works pretty fine for me.
function is_json($string) {
// php 5.3 or newer needed;
json_decode($string);
return (json_last_error() == JSON_ERROR_NONE);
}
function objectToArray($objectOrArray) {
// if is_json -> decode :
if (is_string($objectOrArray) && is_json($objectOrArray)) $objectOrArray = json_decode($objectOrArray);
// if object -> convert to array :
if (is_object($objectOrArray)) $objectOrArray = (array) $objectOrArray;
// if not array -> just return it (probably string or number) :
if (!is_array($objectOrArray)) return $objectOrArray;
// if empty array -> return [] :
if (count($objectOrArray) == 0) return [];
// repeat tasks for each item :
$output = [];
foreach ($objectOrArray as $key => $o_a) {
$output[$key] = objectToArray($o_a);
}
return $output;
}
This is an old question, but I recently ran into this and came up with my own solution.
array_walk_recursive($array, function(&$item){
if(is_object($item)) $item = (array)$item;
});
Now if $array is an object itself you can just cast it to an array before putting it in array_walk_recursive:
$array = (array)$object;
array_walk_recursive($array, function(&$item){
if(is_object($item)) $item = (array)$item;
});
And the mini-example:
array_walk_recursive($array,function(&$item){if(is_object($item))$item=(array)$item;});
In my case I had an array of stdClass objects from a 3rd party source that had a field/property whose value I needed to use as a reference to find its containing stdClass so I could access other data in that element. Basically comparing nested keys in 2 data sets.
I have to do this many times, so I didn't want to foreach over it for each item I need to find. The solution to that issue is usually array_column, but that doesn't work on objects. So I did the above first.
Just in case you came here as I did and didn't find the right answer for your situation, this modified version of one of the previous answers is what ended up working for me:
protected function objToArray($obj)
{
// Not an object or array
if (!is_object($obj) && !is_array($obj)) {
return $obj;
}
// Parse array
foreach ($obj as $key => $value) {
$arr[$key] = $this->objToArray($value);
}
// Return parsed array
return $arr;
}
The original value is a JSON string. The method call looks like this:
$array = $this->objToArray(json_decode($json, true));

Categories