How to access object property of indeterminate depth - php

I have an object with data stored at multiple levels ( a JSON-decoded document ) like this:
$db = (object) array(
'simple_property' => 'value',
'complex_property' => (object) array(
'key' => 'value',
'nested' => (object) array(
'key' => 'value'
)
)
);
I want to be able to access and update data at any depth via reference. Example:
$db->{ $key } = $new_value
If $key is equal to 'simple_property', that works. But if $key is equal to 'complex_property->nested->key', it doesn't. Is there a way to accomplish what I want to, or am I looking at it incorrectly?

I don't think you can get it to work that way. You'll have to create a function (or class method) to do something like that. As an example:
function getRecursiveProperty($object, $path)
{
$array = explode('->', $path);
if (empty($array))
{
return NULL;
}
foreach ($array as $property)
{
if (!isset($object->$property))
{
return NULL;
}
if (!is_object($object->$property))
{
return $object->$property;
}
$object = $object->$property;
}
return $object->$property;
}
function setRecursiveProperty($object, $path, $value)
{
foreach (explode('->', $path) as $property)
{
if (!isset($object->$property))
{
return FALSE;
}
if (!is_object($object->$property))
{
$object->$property = $value;
return TRUE;
}
$object = $object->$property;
}
return FALSE;
}
$key = 'complex_property->nested->key';
echo getRecursiveProperty($db, $key); // value
setRecursiveProperty($db, $key, 'new_value');
echo getRecursiveProperty($db, $key); // new_value

Why you don't use $db = json_decode($json, true); instead of $db = json_decode($json);?
In this way you return an associative array instead of an object and you will not have these problems anymore.
json_decode ( string $json , bool $assoc)
json
The json string being decoded.
This function only works with UTF-8 encoded strings.
assoc
When TRUE, returned objects will be converted into
associative arrays.
More info: http://php.net/manual/en/function.json-decode.php

Related

Convert a nested Array to Scalar and then reconvert the scalar to nested array

I have an array that looks like this.
$array = [
0 => 'abc',
1 => [
0 => 'def',
1 => 'ghi'
],
'assoc1' => [
'nassoc' => 'jkl',
'nassoc2' => 'mno',
'nassoc3' => '',
'nassoc4' => false
]
];
The $array can have numeric keys or be an assoc array or a mixed one. The level of nesting is not known. Also the values of the array can also be bool or null or an empty string ''
I need to able to convert this into a scalar array with key value pairs. And then later reconvert it back to the exact same array.
So the scalar array could look like
$arrayScalar = [
'0' => 'abc',
'1[0]' => 'def',
'1[1]' => 'ghi',
'assoc1[nassoc]' => 'jkl',
'assoc1[nassoc2]' => 'mno',
'assoc1[nassoc3]' => '',
'assoc1[nassoc4]' => false
];
And then later be able to get back to the initial $array.
I wrote a parser and it does not currently handle bool values correctly.
I have a feeling this is at best a super hacky method to do what I am after. Also I have been able to test it only so much.
function flattenNestedArraysRecursively($nestedArray, $parent = '', &$flattened = [])
{
$keys = array_keys($nestedArray);
if (empty($keys)) {
$flattened[$parent] = 'emptyarray';
} else {
foreach ($keys as $value) {
if (is_array($nestedArray[$value])) {
$reqParent = (!empty($parent)) ? $parent . '|!|' . $value : $value;
$this->flattenNestedArraysRecursively($nestedArray[$value], $reqParent, $flattened);
} else {
$reqKey = (!empty($parent)) ? $parent . '|!|' . $value : $value;
$flattened[$reqKey] = $nestedArray[$value];
}
}
}
return $flattened;
}
function reCreateFlattenedArray($flatArray): array
{
$arr = [];
foreach ($flatArray as $key => $value) {
$keys = explode('|!|', $key);
$arr = $this->reCreateArrayRecursiveWorker($keys, $value, $arr);
}
return $arr;
}
function reCreateArrayRecursiveWorker($keys, $value, $existingArr)
{
//Outside to Inside
$keyCur = array_shift($keys);
//Check if keyCur Exists in the existingArray
if (key_exists($keyCur, $existingArr)) {
// Check if we have reached the deepest level
if (empty($keys)) {
//Return the Key => value mapping
$existingArr[$keyCur] = $value;
return $existingArr;
} else {
// If not then continue to go deeper while appending deeper levels values to current key
$existingArr[$keyCur] = $this->reCreateArrayRecursiveWorker($keys, $value, $existingArr[$keyCur]);
return $existingArr;
}
} else {
// If Key does not exists in current Array
// Check deepest
if (empty($keys)) {
//Return the Key => value mapping
$existingArr[$keyCur] = $value;
return $existingArr;
} else {
// Add the key
$existingArr[$keyCur] = $this->reCreateArrayRecursiveWorker($keys, $value, []);
return $existingArr;
}
}
}
Is there a better more elegant way of doing this, maybe http_build_query or something else I am not aware of.
Sandbox link -> http://sandbox.onlinephpfunctions.com/code/50b3890e5bdc515bc145eda0a1b34c29eefadcca
Flattening:
Your approach towards recursion is correct. I think we can make it more simpler.
We loop over the array. if the value is an array in itself, we recursively make a call to this new child subarray.
This way, we visit each key and each value. Now, we are only left to manage the keys to assign them when adding to our final resultant array, say $arrayScalar.
For this, we make a new function parameter which takes the parent key into account when assigning. That's it.
Snippet:
$arrayScalar = [];
function flatten($array,&$arrayScalar,$parent_key){
foreach($array as $key => $value){
$curr_key = empty($parent_key) ? $key : $parent_key . '[' . $key . ']';
if(is_array($value)){
flatten($value,$arrayScalar,$curr_key);
}else{
$arrayScalar[$curr_key] = $value;
}
}
}
flatten($array,$arrayScalar,'');
var_export($arrayScalar);
Demo: http://sandbox.onlinephpfunctions.com/code/1e3092e9e163330f43d495cc9d4acb672289a987
Unflattening:
This one is a little tricky.
You might have already noticed that the keys in the flattened array are of the form key1[key2][key3][key4] etc.
So, we collect all these individually in a new array, say $split_key. It might look like this.
array (
'key1',
'key2',
'key3',
'key4',
)
To achieve the above, we do a basic string parsing and added in-between keys to the array whenever we reach the end of the key string or [ or ].
Next, to add them to our final resultant array, we loop over the collected keys and check if they are set in our final array. If not so, set them. We now pass child array reference to our temporary variable $temp. This is to edit the same copy of the array. In the end, we return the result.
Snippet:
<?php
function unflatten($arrayScalar){
$result = [];
foreach($arrayScalar as $key => $value){
if(is_int($key)) $key = strval($key);
$split_key = [];
$key_len = strlen($key);
$curr = '';
// collect them as individual keys
for($i = 0; $i < $key_len; ++$i){
if($key[ $i ] == '[' || $key[ $i ] == ']'){
if(strlen($curr) === 0) continue;
$split_key[] = $curr;
$curr = '';
}else{
$curr .= $key[ $i ];
}
if($i === $key_len - 1 && strlen($curr) > 0){
$split_key[] = $curr;
}
}
// collecting them ends
//add them to our resultant array.
$temp = &$result;
foreach($split_key as $sk){
if(!isset($temp[ $sk ])){
$temp[ $sk ] = [];
}
$temp = &$temp[$sk];
}
$temp = $value;
}
return $result;
}
var_export(unflatten($arrayScalar));
Demo: http://sandbox.onlinephpfunctions.com/code/66136a699c3c5285eed3d3350ed4faa5bbce4b76

PHP - Find Specific Value in Array (multidimensional)

I have the an array, in which I store one value from the database like this:
$stmt = $dbh->prepare("SELECT token FROM advertisement_clicks WHERE (username=:username OR ip=:ipcheck)");
$stmt->bindParam(":username",$userdata["username"]);
$stmt->bindParam(":ipcheck",$ipcheck);
$stmt->execute();
$data = array();
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
So, that gives me: array("token","token");
When I print it:
Array ( [0] => Array ( [token] => 677E2114AA26BA4351A686917652C7E1BA67A32D ) [1] => Array ( [token] => C42190F3D72C5BB6BB6B68488D1D4662A8D2A138 ) )
I then have a loop, that loops all the tokens. In that loop, I try to search for a specific token, and if it that token matches, it will be marked as "seen":
function searchForId($id, $array) {
foreach ($array as $key => $val) {
if ($val['token'] === $id) {
return $key;
}
}
}
This is my loop:
$icon = "not-seen";
foreach($d as $value){
$token = $value["token"];
$searchParam = searchForId($token, $data);
if($searchParam == $token){
$icon = "seen";
}
}
However, searchForid() simply returns 0
What am I doing wrong?
Ok, here you can see a PHP fiddle that works
$data = array();
$data[] = array('token'=>'123');
$data[] = array('token'=>'456');
function searchForId($id, $array) {
foreach ($array as $key => $val) {
if ($val['token'] === $id) {
return true;
}
}
return false;
}
$icon = "not-seen";
foreach($data as $value){
$token = $value["token"];
$searchParam = searchForId($token, $data);
if($searchParam){
$icon = "seen";
}
}
echo $icon;
It echos 'seen' which is expected since I compare the same array values, now assuming that your $d variable has different tokens, it should still work this way.
That means that your $d array does not contain what you claim it contains, could you print_r this variable and post it in your answer?

Access JSON through variable path in PHP

In PHP, how to get a JSON value from a variable path ?
Here is a sample json and a function with 2 params: a json variable, and path split into keys.
$myjson = json_decode('{
"myKey1": {
"myKey2": {
"myKey3": {
"myKey4": "myfinalvalue"
}
}
}
}');
function getJSONValue($myjson, $pathkey) {
// split path keys by ";"
$myjson-> ?? $pathkey ??
}
echo getJSONValue($myjson, "myKey1;myKey2;myKey3;myKey4");
// Should display "myfinalvalue"
the static equivalent would be to do:
$myjson->myKey1->myKey2->myKey3->myKey4;
I tried:
$myjson->$pathkey
but unfortunately, it doesn't work...
Actually, your question has nothing to do with JSON. This is how you can get a value from a nested object:
function getValue($obj, $path) {
foreach(explode(';', $path) as $key) {
$obj = $obj->$key;
}
return $obj;
}
As for the JSON part, your example is not a valid JSON. It should be like this:
$myjson = json_decode('{
"myKey1": {
"myKey2": {
"myKey3": {
"myKey4": "myfinalvalue"
}
}
}
}');
Also, php objects are case-sensitive, if you have myKey in the object, it should be myKey (and not mykey) in the path string.
function getJSONValue($myjson, array $pathkey) {
foreach($pathkey as $val){
$myjson = $myjson->{$val};
}
return $myjson;
}
$myjson = json_decode('{"myKey1": {"myKey2": {"myKey3": {"myKey4": "myfinalvalue"}}}}');
echo getJSONValue($myjson, ["myKey1","myKey2","myKey3","myKey4"]);
live example: http://codepad.viper-7.com/1G78Fi
Something like this:
function getJSONValue() {
$args = func_get_args();
$json = array_shift($args);
$key = array_shift($args);
$value = $json->$key;
if(is_object($value)) {
array_unshift($args, $value);
return call_user_func_array('getJSONValue', $args);
}
return $value;
}
No error checking.
Seems like you are mixing PHP and JS. check this out to see how to create a Json string, then getting an onject out of it, and accessing keys:
$myjson = json_encode(array(
'myKey1' => array(
'mykey2' => array(
'mykey3' => array(
'myKey4' => "myfinalvalue"
)
)
)
));
$obj = json_decode($string);
echo $obj->myKey1->mykey2->mykey3->myKey4
// "myfinalvalue"
or pasting the json directly:
$obj = json_decode(
'{
myKey1: {
mykey2: {
mykey3: {
myKey4: "myfinalvalue"
}
}
}
}');
echo $obj->myKey1->mykey2->mykey3->myKey4
// "myfinalvalue"
To understand it betterm, read about JSON_DECODE

How to make first array value to uppercase

I am trying to make first array value to uppercase.
Code:
$data = $this->positions_model->array_from_post(array('position', 'label'));
$this->positions_model->save($data, $id);
So before save($data, $id) to database I want to convert position value to uppercase. I have tried by this
$data['position'] = strtoupper($data['position']);
but than it is not storing the value in db with uppercase but as it is what user inputs.
Current output of $data:
Array ( [position] => it [label] => Information Technology )
And I want it in uppercase as IT
Added Model Method
public function get_positions_array($id = NULL, $single = FALSE)
{
$this->db->get($this->_table_name);
$positions = parent::get($id, $single);
$array = array();
foreach($positions as $pos){
$array[] = get_object_vars($pos);
}
return $array;
}
Main MY_Model method
public function array_from_post($fields)
{
$data = array();
foreach ($fields as $field) {
$data[$field] = $this->input->post($field);
}
return $data;
}
This should work:
$data = $this->positions_model->array_from_post(array('position', 'label'));
$data['position'] = strtoupper($data['position']);
$this->positions_model->save($data, $id);
If Its not, then $data array have only read attribute.
The array_from_post() method returns an array with the format below:
$data = array(
'position' => 'it',
'label' => 'Information Technology'
);
So, you could make first value of the array to uppercase, by using array_map or array_walk functions as follows:
$data = array_map(function($a) {
static $i = 0;
if ($i === 0) { $i++; return strtoupper($a); }
else return $a;
}, $array);
Note: This only works on PHP 5.3+, for previous versions, use the function name instead.
Here is the array_walk example, which modifies the $data:
array_walk($data, function(&$value, $key) {
static $i = 0;
if ($i == 0) { $i++; $value = strtoupper($value); }
});
Again, if you're using PHP 5.2.x or lower, you could pass the function name instead.

PHP Dynamic array key [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Dynamic array keys
I have an array $Values
which is set up like this
$Values
1
2
3
a
b
c
4
which is nested.
and I have a key like this: $key = "a"]["b"]["c";
Can I now do: Values[$key], ti get the value in c ?
#Edit
Simply said: I want to get the value from array $Values["a"]["b"]["c"] By doing $Values[$key]. What should my key be then?
No, but you can extract the result:
$Values = array( '1' => 'ONE',
'2' => 'TWO',
'3' => 'THREE',
'a' => array( 'b' => array( 'c' => 'alphabet') ),
'4' => 'FOUR'
);
$key = '"a"]["b"]["c"';
$nestedKey = explode('][',$key);
$searchArray = $Values;
foreach($nestedKey as $nestedKeyValue) {
$searchArray = $searchArray[trim($nestedKeyValue,'"')];
}
var_dump($searchArray);
Will only work if $key is valid.
Now how do you get in a situation with a key like this anyway? Perhaps if you explained the real problem, we could give you a real answer rather than a hack.
No, you only can get individual keys from variables. Depending on what you really want to do you could use references to your array elements.
Nah you can't. This is invalid syntax.
Hover you can do:
$key = 'a,b,c';
// or:
$key = serialize( array( 'a','b', 'c'));
// or many other things
And than implement your array-like class which will implement ArrayAccess or ArrayObject (let's way you'll stick with $key = 'a,b,c';):
class MyArray extends ArrayAccess {
protected $data = array();
protected &_qetViaKey( $key, &$exists, $createOnNonExisting = false){
// Parse keys
$keys = array();
if( strpos( $key, ',') === false){
$keys[] = $key;
} else {
$keys = explode( ',', $key);
}
// Prepare variables
static $null = null;
$null = null;
$exists = true;
// Browse them
$progress = &$this->data;
foreach( $keys as $key){
if( is_array( $progress)){
if( isset( $progress[ $key])){
$progress = $progress[ $key];
} else {
if( $createOnNonExisting){
$progress[ $key] = array();
$progress = $progress[ $key];
} else {
$exists = false;
break;
}
}
} else {
throw new Exception( '$item[a,b] was already set to scalar');
}
}
if( $exists){
return $progress;
}
return $null;
}
public offsetExists( $key){
$exists = false;
$this->_getViaKey( $key, $exists, false);
return $exists;
}
// See that we aren't using reference anymore in return
public offsetGet( $key){
$exists = false;
$value = $this->_getViaKey( $key, $exists, false);
if( !$exists){
trigger_error( ... NOTICE ...);
}
return $value;
}
public offsetSet ( $key, $val){
$exists = false;
$value = $this->_getViaKey( $key, $exists, true);
$value = $val;
}
}
// And use it as:
$array = MyArray();
$array['a']['b']['c'] = 3;
$array['a,b,c'] = 3;
Or implement function:
public function &getArrayAtKey( $array, $key){
// Similar to _qetViaKey
// Implement your own non existing key handling
}

Categories