I have this example code:
$array = array(
'GiamPy' => array(
'Age' => '18',
'Password' => array(
'password' => '1234',
'salt' => 'abcd',
'hash' => 'whirlpool'
),
'Something' => 'Else'
)
);
echo json_encode($array, JSON_PRETTY_PRINT);
I have seen in the PHP documentation that, since PHP 5.5.0 (thus recently), json_encode allows a new parameter which is the depth.
What is its purpose?
Why would I ever need it?
Why has it been added in PHP 5.5.0?
The option limits the depth that will be processed (d'uh). The depth of an array is measured by how deep it is nested. This is an array of depth 1:
array(
'foo',
'bar',
'baz'
)
This is an array of depth 2:
array(
array(
'foo'
),
array(
'bar'
),
array(
'baz'
)
)
// ------ depth ------>
If the input surpasses the maximum depth (by default 512), json_encode will simply return false.
Why you may use this is debatable, you may want to protect yourself from inadvertent infinite recursion or too much resource use. An array which is deeper than 512 levels probably has infinitely recursive references and cannot be serialised. But if you are sure your array is not infinitely recursive yet is deeper than 512, you may want to increase this limit explicitly. You may also want to lower the limit as a simple error catcher; say you expect the result to have a maximum depth but your input data may be somewhat unpredictable.
Related
I'm saving some JSON and I don't know if this might later nib me in the butt if the JSON gets too big(The size depends on user input), though from my reading the max depth of 512 seems to be quite a high number and not really practical, but it triggered something in my mind and I've only now realized that I don't quite understand the meaning of "depth" of an array or how to count it (visually)...
I checked out this question while trying to get a clear understanding of depth: What is the purpose of using depth in JSON encode?
And the example given was:
array(
'foo',
'bar',
'baz'
)
// Has depth of 1
array(
array(
'foo'
),
array(
'bar'
),
array(
'baz'
)
)
// Has depth 2
But what about...
array(
array(
'foo'
),
array(
'bar'
),
array(
array(
'baz' => 'caz'
)
)
Or
array(
array(
'foo'
),
array(
'bar'
),
array(
array(
'baz' => 'caz'
),
array(
'tree' => array(
'tree-a' => array(
'car-a' => 'car-b'
)
)
)
)
I know this might be obvious to some but it's just one of those things I never thought much about. I did check out the documentation for json_decode() and it's depth parameter but still couldn't wrap my head around it. Maybe there's some rule of thumb i can use?
I'm trying to understand this in the context of when using json_decode();
I just conducted this very interesting experiment and the results came out quite surprising.
The purpose of the test was to determine the best way, performance-wise, to get an element of an array. The reason is that I have a configuration class which holds settings in an associative mufti-dimensional array and I was not quite sure I was getting these values the best way possible.
Data (it is not really needed for the question, but I just decided to include it so you see it is quite a reasonable amount of data to run tests with)
$data = array( 'system' => [
'GUI' =>
array(
'enabled' => true,
'password' => '',
),
'Constants' => array(
'URL_QUERYSTRING' => true,
'ERRORS_TO_EXCEPTIONS' => true,
'DEBUG_MODE' => true,
),
'Benchmark' =>
array(
'enabled' => false,
),
'Translations' =>
array(
'db_connection' => 'Default',
'table_name' =>
array(
'languages' => 'languages',
'translations' => 'translations',
),
),
'Spam' =>
array(
'honeypot_names' =>
array(
0 => 'name1',
1 => 'name2',
2 => 'name3',
3 => 'name4',
4 => 'name5',
5 => 'name6',
),
),
'Response' =>
array(
'supported' =>
array(
0 => 'text/html',
1 => 'application/json',
),
),]
);
Methods
function plain($file, $setting, $key, $sub){
global $data;
return $data[$file][$setting][$key][$sub];
}
function loop(...$args){
global $data;
$value = $data[array_shift($args)];
foreach($args as $arg){
$value = $value[$arg];
}
return $value;
}
function arr(){
global $data;
return $data;
}
Parameters (when calling the functions)
loop('system', 'Translations', 'table_name', 'languages');
plain('system', 'Translations', 'table_name', 'languages');
arr()['system']['Translations']['table_name']['languages'];
Leaving aside any other possible flaws and focusing on performance only, I ran 50 tests with 10000 loops. Each function has been called 500000 times in total. The results are in average seconds per 10000 loops:
loop: 100% - 0.0381 sec. Returns: languages
plain: 38% - 0.0146 sec. Returns: languages
arr: 23% - 0.0088 sec. Returns: languages
I was expecting loop to be quite slow because there is logic inside, but looking at the results of the other two I was pretty surprised. I was expecting plain to be the fastest because I'm returning an element from the array and for the opposite reason arr to be the slowest because it returns the whole array.
Given the outcome of the experiment I have 2 questions.
Why is arr almost 2 times faster than plain?
Are there any other methods I have missed that can outperform arr?
I said this in the comment, but I decided it's pretty close to an answer. Your question basically boils down to why is 2+2; not faster than just plain 2;
Arrays are just objects stored in memory. To return an object from a function, you return a memory address (32 or 64 bit unsigned integer), which implies nothing more than pushing a single integer onto the stack.
In the case of returning an index of an array, that index really just represents an offset from the base address of the array, so everytime you see a square bracket-style array access, internally PHP (rather the internal C implementation of PHP) is converting the 'index' in the array into an integer that it adds to the memory address of the array to get the memory address of the stored value at that index.
So when you see this kind of code:
return $data[$file][$setting][$key][$sub];
That says:
Find me the address of $data. Then calculate the offset that the string stored in $file is (which involves looking up what $file is in memory). Then do the same for $setting, $key, and $sub. Finally, add all of those offsets together to get the address (in the case of objects) or the value (in the case of native data types) to push on to the stack as a return value.
It should be no surprise then that returning a simple array is quicker.
That's the way PHP works. You expect, that a copy of $data is returned here. It is not.
What you acutaly have, is a pointer (Something like an ID to a specific place in the memory), which reference the data.
What you return, is the reference to the data, not the data them self.
In the plain method, you search for the value first. This cost time. Take a look at this great article, which show, how arrays are working internal.
Sometimes Code say's more then words. What you assume is:
function arr(){
global $data;
//create a copy
$newData = $data;
//return reference to $newData
return $newData;
}
Also you should not use global, that is bad practise. You can give your array as a parameter.
//give a copy of the data, slow
function arr($data) {
return $data;
}
//give the reference, fast
function arr(&$data) {
return $data;
}
This is primarily a PHP answer, but the methodology may in fact be language agnostic.
After running a CSV file through a simple parser, I was given a multidimensional array that resembled something akin to this:
array( 'estimated' =>
array(
array( "TITLE" => 'MAIN', "CURR_PERF" => 100, "POT_PERF" => 75 ),
array( "TITLE" => 'HEAT', "CURR_PERF" => 90, "POT_PERF" => 60 ),
array( "TITLE" => 'CO2', "CURR_PERF" => 56, "POT_PERF" => 40 ),
),
'actual' =>
array(
array( "TITLE" => 'MAIN', "CURR_PERF" => 100, "POT_PERF" => 75 ),
array( "TITLE" => 'HEAT', "CURR_PERF" => 89 , "POT_PERF" => 75),
array( "TITLE" => 'CO2', "CURR_PERF" => 40, "POT_PERF" => 20 ),
);
);
Now, horrific data structure to one side, without refactoring of the underlying parser - how would be the best way to ensure that you can access these in a specific order? Without necessarily touching the underlying parser?
If you loop through using a for()/foreach() loop you're only going to be able to read them in a linear order - increasing or decreasing down the elements. You're not necessarily going to be able to drill down and get the specific value required.
For instance, the CSV file could express the values for estimated in a different order to the values for actual; and it may be required that they are output in yet another order.
For example, here are three different orders off the top of my head:
-> MAIN HEAT CO2
-> HEAT MAIN CO2
-> CO2 HEAT MAIN
Furthermore, as is quite usual, the label names in the CSV file are not exactly user-friendly - so they need to be 'translated' (if you like) in to something more human-friendly. Naturally, without the use of lots of if() statements if preferable!
Given it's a very specific use case, but it's something I've seen before with regards to arrays being serialised - and often finding out that they are actually nested.
I've posted one possible solution, but would be happy to see others. (In the past when I've done similar I've never accepted my own answer ;)) I'm sure there must be a more elegant way than the one I've devised..!
This was the quickest (and neatest) solution I could come up with on the spot at the time, but it's not one that I take very much pride in..! It involves multiple loops, the use of several arrays and seems to be an over-engineered mechanism for doing something which should surely be pretty simplistic?
First of all I've created an associative array to use as a dictionary to look up translations for text strings found in the CSV file:
$tbl = array( "MAIN"=>"Main Costs",
"TOTAL"=>"Total Costs",
"CO2"=>"Gas expended" );
Next up, I created an array to use as an 'index' - I entered the key values here in the order I would like them to be accessed in the application:
$index = array( "MAIN", "TOTAL", "CO2" );
I then created two blank arrays and populated them with the data from the child arrays, by using a loop I was able to make them associative - allowing me to specify use the TITLE fields as keys:
$estimated = array();
$actual = array();
foreach( $bills['estimated'] as $bill ){
$estimated[ $bill['title'] ] = $bill;
}
foreach( $bills['actual'] as $bill ){
$actual[ $bill['title'] ] = $bill;
}
By doing this I could loop through them in a specific order, regardless of the order they were parsed in, like so:
for($i=0; $i<3; $i++){
$bill = $estimated[ $index[ $i ] ];
printf(" %s: %d ", $tbl[ $index[ $i ] ], $bill['CURR_PERF'] );
}
Which would output the following, in the order I specified:
// 1. Main Costs: 100
// 2. Total Costs: 90
// 3. Gas Expended: 56
Naturally, this order could easily be changed if required. It does however:
require using an array specifically to act as an index
require using two loops simply to initialise
uses a total of four extra arrays
If this array was assigned to variable $ary like:
$ary = array(
'estimated' =>
array(
array('TITLE' => 'MAIN', 'CURR_PERF' => 100, 'POT_PERF' => 75),
array('TITLE' => 'HEAT', 'CURR_PERF' => 90, 'POT_PERF' => 60),
array('TITLE' => 'CO2', 'CURR_PERF' => 56, 'POT_PERF' => 40),
),
'actual' =>
array(
array('TITLE' => 'MAIN', 'CURR_PERF' => 100, 'POT_PERF' => 75),
array( 'TITLE' => 'HEAT', 'CURR_PERF' => 89 , 'POT_PERF' => 75),
array( 'TITLE' => 'CO2', 'CURR_PERF' => 40, 'POT_PERF' => 20 ),
);
);
Your estimated array could be accessed like:
$newVar = $ary['estimated'][2]['CURR_PERF'];
$newVar would be 56. You can reassign a value as well, like:
$ary['estimated'][0]['POT_PERF'] = 300;
That first array in the estimated array used to be 75 and is now 300.
This is how you get and set specific values in a Multi-dimensional Array.
To sort the arrays, if you need to loop over and maintain their indexes, see PHP's uasort() function. This may take some work, on your part, to develop a cmp_function, but should do what you're looking for in a different way.
See,
http://www.php.net/manual/en/function.uasort.php
and
http://www.php.net/manual/en/function.usort.php ,
which explains cmp_function better.
I'm trying to use array_merge_recursive to merge two data structures.
<?php
$testSite = array(
'name' => 'test site',
'modules' => array(
'foo' => 'true',
'bar' => 'true'
)
);
$testData = array(
'modules' => array(
'bar' => 'false'
)
);
$testSite = array_merge_recursive($testSite, $testData);
Note that I'm using strings instead of booleans for debug printing purposes
I would expect $testSite to be the exact same after this code has ran, except for the modules.bar property, which I'd expect to see being changed to false. What happens instead, as seen in this live example, is that bar is turned into an array containing it's old value and the value false is appended to that.
The documentation page reads that this is what will happen for numeric keys, but these are all strings keys. Can anyone shed some light on this?
I think you want array_replace_recursive.
array_merge_recursive() vs. array_replace_recursive()
I have an array of values in PHP looking something like this:
$test = array (
array( 'val' => 2, 'color' => 'blue' ),
array( 'val' => 5, 'color' => 'green' ),
);
I want to go through all elements of $test and add 1 to all val indices. Now I realize a foreach loop could work, but I am looking for something a little more efficient. The array will potentially have 10's of thousands of elements and hundreds of sub-elements.
I am wondering if there is some type of way that PHP can go through and modify just that index throughout the entire array, based on the argument that I set.
you can use array_walk and a closure to do it
array_walk($yourArray,function(&$col){$col['val']++});