I am trying to generalize some code in a function so that I can read different JSON formatted input and determine system information from each. For brevity, I am not including all of the code. In the actual code, I am retrieving the value of $length from a database.
Here is an example:
function readHostname($json, $length) {
$content = json_decode($json, true);
$hostname = $content[$length];
}
$json = file_get_contents($url1, false, $context);
$length = "[0]['cluster']['nodes'][0][hostName]";
echo readHostname($json, $length);
$json = file_get_contents($url2, false, $context);
$length = "[0]['components']['serviceName']";
echo readHostname($json, $length);
For reference url1 would return JSON such as:
[
{
"cluster": {
"nodes": [
{ "name": "cluster1",
"hostName": "alpha"
},
{ "name": "cluster2",
"hostName": "beta"
}
]
}
},
{
"cluster": {
"nodes": [
{ "name": "prod_cluster1",
"hostName": "oscar"
},
{
"name": "prod_cluster2",
"hostName": "delta"
}
]
}
}
]
and url2 would return json:
[
{
"compenents": {
"serviceName" : "hostname1",
"environment" : "produciton"
}
}
]
You want flexible access to nested array structures. Replace your readHostname function with this:
function readHostname($json, $length) {
$content = json_decode($json, true);
preg_replace_callback('/\[([^]]+)\]+/', function($m) use(&$content) {
$index = $m[1];
$content = $content[preg_match('/[0-9]+/', $index) ? intval($index) : trim($index, "'\"")];
}, $length);
return $content;
}
You can abstract the process and use recursion.
function getHost($payload,$hostName){
$array = is_array($payload) ? $payload : json_decode($payload,true);
if(json_last_error() != JSON_ERROR_NONE){
return;
}
foreach(array_keys($array) as $key){
if($array[$key] == $hostName){
unset($array[$key]);
return $array[key($array)];
}
}
return getHost($array[key($array)],$hostName);
}
echo getHost($payload,"cluster1");
echo getHost($payload,"hostname1");
Related
In my recursive function I can't generate a unique id (via an incremented variable) for my multidimensional array, here is what I tried.
I need to assign a unique ID for each directory name I find: $data[] = ['id'=>$i,'text'=>$dir];
function scandir_rec($root, $i)
{
$data = [];
if (!is_dir($root)) {
return;
}
$dirs = scandir($root);
foreach ($dirs as $dir) {
$i++;
if ($dir == '.' || $dir == '..') {
continue;
}
$path = $root . '/' . $dir;
if (is_file($path)) {
continue;
}
if (is_dir($path)) {
$nodes = scandir_rec($path, $i); // <--- unique ID
if (!empty($nodes)) $chunk['children'] = $nodes;
}
$data[] = [ 'id' => $i, 'text' => $dir ]; // <--- unique ID
}
return $data;
}
$rootDir = '/var/www/html';
$i = 0;
$json_data = json_encode(scandir_rec($rootDir, $i), JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES); // <--- unique ID
file_put_contents('tree.json', $json_data);
Desired result
[
{
"id":1,
"text":"Root folder",
"children":[
{
"id":2,
"text":"Child folder 1"
},
{
"id":3,
"text":"Child folder 2"
}
]
},
{
"id":4,
"text":"Root folder"
},
{
"id":5,
"text":"Root folder",
"children":[
{
"id":6,
"text":"Child folder 1"
},
{
"id":7,
"text":"Child folder 2"
}
]
}
]
Personally I hate returning from recursive functions, it tends to get weird unless you are returning a status for the caller to do something with. Instead, it is often easier to use byref parameters.
I've commented the code below, hopefully it makes sense. We're passing two parameters byref, one is "the current array to add stuff to" and the other is "the global index". We're also keeping track of depth because you are treating the root differently than children.
function scandir_rec(string $root, array &$current_parent, int &$global_index, int $depth = 0): void
{
// Bail early if we don't have a directory
if (!is_dir($root)) {
return;
}
// Get the children
$dirs = scandir($root);
// This is the local child index and resets for successive calls
$local_index = 1;
foreach ($dirs as $dir) {
// Once again, bail early if we can
if ($dir === '.' || $dir === '..') {
continue;
}
// The next path to scan
$next_path = $root . '/' . $dir;
// Make sure it isn't a file
if (is_file($next_path)) {
continue;
}
// Everyone gets this
$current_node = [
// Set the ID and increment so that the next gets a bigger one
'id' => $global_index++,
// Root node is treated differently thant children
'text' => 0 === $depth ? 'Root node' : "Child node ${local_index}",
];
// If the next node is a directory
if (is_dir($next_path)) {
// Create a temporary holder for the contents
$children = [];
// Scan again, incrementing the depth by one
scandir_rec($next_path, $children, $global_index, ++$depth);
// If the above did something to our array, append to our local (the master)
if ($children) {
$current_node['children'] = $children;
}
}
// Append the current node
$current_parent[] = $current_node;
}
}
To call this, remember that it doesn't return so we need to run it by itself, We also want to pass the global index in by ref so we need to create a variable to hold it.
$current_parent = [];
$global_index = 1;
scandir_rec('/var/www', $current_parent, $global_index);
$json_data = json_encode($current_parent, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
This code is written against PHP 7.4 do you might need to tweak a couple of things if you are on an older version.
edit
In hindsight, I was taking your text too literally, I think. You can probably get rid of the $depth parameter completely and just set your text to:
'text' => $root,
I'm running this from Windows and I just ran a quick test folder which produced:
[
{
"id": 1,
"text": "/Dell",
"children": [
{
"id": 2,
"text": "/Dell/25tgw",
"children": [
{
"id": 3,
"text": "/Dell/25tgw/RealtekHDAudio"
},
{
"id": 4,
"text": "/Dell/25tgw/RealtekHDAudio"
},
{
"id": 5,
"text": "/Dell/25tgw/RealtekHDAudio"
},
{
"id": 6,
"text": "/Dell/25tgw/RealtekHDAudio"
}
]
}
]
},
{
"id": 7,
"text": "/Dell",
"children": [
{
"id": 8,
"text": "/Dell/UpdatePackage"
}
]
},
{
"id": 9,
"text": "/Dell"
}
]
The issue is that each recursive call starts it's own count. Let's say $i is 10 inside the loop at a given time. You start a recursive call that increases it up to 15 but when the call ends and returns to parent function, $i there is still 10.
Perhaps the simplest fix is to pass the counter by reference:
function scandir_rec($root, &$i)
{
}
Another solution is to avoid recursion altogether. PHP has builtin iterators that can walk a three without recursion, e.g.:
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($rootDir),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($files as $name => $object) {
echo sprintf("[%s] %s\n", $object->isDir() ? 'DIR' : ' ', $name);
}
I'm trying to set up a test question that will find certain words in a short answer. The words, which will mark the answer as correct, will be stored as values in an object. I was trying to figure out how to do it with strpos(), but every alternative that I come up with gives me a blank screen.
PHP:
$myJSON = file_get_contents('quiz.json');
$json = json_decode($myJSON);
foreach($json as $value) {
foreach($value->answer as $index => $options) {
$findme = "application";
$pos = strpos($options, $findme);
if ($pos === true) {
echo $options;
//echo $value->text->type->answer;
//echo ($index. ' '. $options . '<br>');
//echo current($value);
}
}
}
JSON:
{
"question1": {
"text": "What are the two types of permission lists that DSA concentrates on?",
"type": "short_answer",
"answer": {
"1": "application",
"2": "row-level"
}
},
"question2": {
"text": "What are the building blocks for EmpowHR Security?",
"type": "short_answer",
"answer": {
"1": "permission lists"
}
},
"question3": {
"text": "Who is the bomb?",
"type": "short_answer",
"answer": {
"1": "permission"
}
}
}
Tested the following using your given JSON file.
Important: First I added the , true to $json = json_decode($myJSON); --> $json = json_decode($myJSON, true); This turns the obj into an array
After var_dumping the json encoded I noticed you had mixed string and array types in the level you were trying to parse, so used in_array() to filter out the strings and only iterate through the arrays and was able to locate all instances of the "answers" section in its current build within that obj.
$stmt = NULL;
$find = "application";
$myJSON = file_get_contents('quiz.json');
$json = json_decode($myJSON, true);
foreach( $json as $content ){
foreach( $content as $target){
if(is_array($target)){
// we must find the key of the value within the next level of the array
// and use it as index for the value $target to use in strpos() --> $target[$index]
foreach($target as $index => $value){
if(strpos($target[$index], $find) !== false){
$stmt = '<span>'.$target[$index].': CORRECT</span>';
}
}
}
}
}
echo $stmt;
foreach( $json as $question=>$content ){
foreach( $content as $key=>$value ){
if( strpos( $value, 'applikation' ) !== false
echo $value;
}
}
}
strpos returns the found position and false if not found.
returnvalue === true: allways false
returnvalue == true: false if not found or found on first position (0), true if found after first position (all numbers != 0 are true)
returnvalue !== false: correct result
I have an array like:
$arr = ["pages","homepage","welcomeText"];
$newWelcomeText = "Hello there";
and JSON which looks like:
{
lang: "en",
something: [0, 1, 33],
pages: {
homepage: {
welcomeText: "Hello!",
subHeaiding: "have a nice day"
}
}
}
and I want to find a way how to replace the "welcomeText" with a new value.
I tried something like:
public function findAndReplace ($path, $obj, $data, $index = 0) {
if($index + 1 == sizeof($path)) {
if(!is_array($obj)) {
$obj = json_decode(json_encode($obj), true);
}
$obj[$path[$index]] = $data;
return $obj;
}
return $this->findAndReplace($path, $obj, $data, $index + 1);
}
I never know how the path will look like so I need some kind of function that receives an array and this object as a parameter and returns a modified object.
You could utilize the array_walk_recursive function with some extra bells and whistles to allow referential access to array values.
Create some function array_walk_recursive_referential which will allow referential access to each key/value and pass it to your own function that you send in($function):
function array_walk_recursive_referential(&$array, $function, $parameters = []) {
$reference_function = function(&$value, $key, $userdata) {
$parameters = array_merge([$value], [$key], $userdata[1]);
$value = call_user_func_array($userdata[0], $parameters);
};
array_walk_recursive($array, $reference_function, [$function, $parameters]);
}
And use it with your $arr data:
array_walk_recursive_referential($arr, function($value, $key) {
if(is_string($key))
{
if($key === 'welcomeText')
{
$value = 'My New Welcome Text'
}
}
return $value;
});
Your $arr variable is passed in with a reference so you don't need to re-assign it after function call.
If I want to replace a value in an JSON onbject by path given in an array, I ended up with this:
$json = '{
lang: "en",
something: [0, 1, 33],
pages: {
homepage: {
welcomeText: "Hello!",
subHeaiding: "have a nice day"
}
}
}'
$obj = json_decode($json, true);
$path = ["pages","homepage","welcomeText"]
Function below takes the object and a path to the key in an array and returns a modified object.
function findAndReplace ($obj, $path, $data, $index = 0) {
if($index + 1 == sizeof($path)) {
if(!is_array($obj)) {
$obj = json_decode(json_encode($obj), true);
}
$obj[$path[$index]] = $data;
return $obj;
}
$obj[$path[$index]] = findAndReplaceAndDo($obj[$path[$index]], $path, $data, $index + 1);
return $obj;
}
I have following php array object:
"requests": [
{
"id": 111,
"time: 123123123,
"data": "testc"
},
{
"id": 200 ,
"time: 433123123,
"data": "testb"
},
{
"id": 300 ,
"time: 33123123,
"data": "testb"
}
]
I want to sort it by requests->time only. How to do it?
I don't want sort by id. All data should be sort as per time ASC.
supposing you have that array in a $requests variable, the following should work
<?php
function cmp($a, $b)
{
return strcmp($a["time"],$b["time"]);
}
usort($requests, "cmp");
//print for test
while (list($key, $value) = each($requests)) {
echo "\$requests[$key]: " . $value["time"] . "\n";
}
?>
You have to use your own sort method with usort()
Solution :
<?php
function sortByTimeASC($a, $b)
{
if ($a->time == $b->time) {
return 0;
}
return ($a->time < $b->time) ? -1 : 1;
}
$a = json_decode('[{"id": 111,"time": 123123123,"data": "testc"},{ "id":200 ,"time":433123123,"data":"testb"},{"id":300,"time":33123123,"data":"testb"}]');
usort($a, "sortByTimeASC");
print_r($a);
?>
Live example
I am using PHP 5.5.12.
I have the following multidimensional array:
[
{
"id": 1,
"type":"elephant",
"title":"Title of elephant"
},
{
"id": 2,
"type":"tiger",
"title":"Title of tiger"
},
{
"id": 3,
"type":"lion",
"title":"Title of lion",
"children":[{
"id": 4,
"type":"cow",
"title":"Title of cow"
},
{
"type":"elephant",
"title":"Title of elephant"
},
{
"type":"buffalo",
"title":"Title of buffalo"
}]
}
]
I am iterating this array using foreach loop.
The array key type must be in elephant, tiger and lion. If not, then the result should return false.
How can I achieve this?
So you want to check if your $myArray contains a value or not:
// first get all types as an array
$type = array_column($myArray, "type");
// specify allowed types values
$allowed_types = ["lion", "elephant", "tiger"];
$count = count($type);
$illegal = false;
// for loop is better
for($i = 0; $i < $count; $i++)
{
// if current type value is not an element of allowed types
// array, then both set the $illegal flag as true and break the
// loop
if(!in_array($type[$i], $allowed_types)
$illegal = true;
break;
}
Since you're using PHP5.5.12, you can make use of array_column.
$arr = json_decode($json, true);
//Walk through each element, only paying attention to type
array_walk( array_column($arr, 'type'), function($element, $k) use(&$arr) {
$arr[$k]['valid_type'] = in_array($element, array('lion', 'tiger', 'elephant'));
});
From here, each element in the array ($arr) will have a new key valid_type with a boolean value - 1 if the type is valid, 0 if it isn't.
https://eval.in/350322
Is this something that you are looking for?
foreach($your_array as $item) {
if (!array_key_exists('type', $item)) {
return FALSE;
}
}
function keyExists($arr, $key) {
$flag = true;
foreach($arr as $v) {
if(!isset($v[$key])) {
$flag = false;
break;
}
}
return $flag;
}
Hope this helps :)