Removing a JSON array element in PHP and re-encoding as JSON

function deleteNews($selected) { $file = file_get_contents('news.json', true); $data=json_decode($file,true); unset($file); foreach($selected as $index){ unset($data[$index]); } $result=json_encode($data); file_put_contents('news.json', $result); echo $result; unset($result); $url="./deleteNews.php"; //redirect($url); } 

The above function should remove the entry from JSON, for example:

 [ { "dummy":"dummy", "dummy1":"dumy" }, { "dummy":"dummy1", "dummy1":"dummy" } ] 

But at the end, the function inserts numeric indices into JSON, which is undesirable.

The result is similar to

 [ "0": { "dummy":"dummy", "dummy1":"dumy" }, "1": { "dummy":"dummy1", "dummy1":"dummy" } ] 

How can I get my original JSON format? I do not want 0 1 2 parts of the index.

NB: it does foreach . Without foreach JSON is created as expected.

+2
source share
3 answers

Try using array_values:

 $result = json_encode(array_values($data)); 
+3
source

TL DR PHP arrays with all numeric keys, starting from zero, sorted and without holes, are the only view that will be displayed in the JSON array [ "square", "brackets" ] . All other views will become JSON dictionaries { "curly": "brackets" } .


The reason you observe the behavior and the reason array_values problem (and in this case is really the case) is the difference between arrays and PHP and JSON dictionaries.

This is a PHP array with continuous numeric keys:

 $a = array( "Apple", "Banana", "Canteloupe" ); 

It's really

 $a = array( 0 => "Apple", 1 => "Banana", 2 => "Canteloupe" ); 

In JSON, this becomes an array :

 [ "Apple", "Banana", "Canteloupe" ] 

Instead, it is a PHP array with text keys (hash):

 $a = array( "a" => "Apple", "b" => "Banana", "c" => "Canteloupe" ); 

In JSON, this is a dictionary (note the curly braces):

 { "a": "Apple", "b": "Banana", "c": "Canteloupe" } 

Some operations change PHP-array-rendered-as-array (ARA) to -rendered-as-dictionary (ARD) .

And these operations:

  • Adding a TEXT key to a numeric array.

  • The presence of the number keys is NOT in a continuous sorted sequence of 0 ... N.

So it looks like these are arrays, but these are actually dictionaries :

 $a = array ( 2 => "Canteloupe", 1 => "Banana", 0 => "Apple" ); $a = array ( 0 => "Apple", 2 => "Canteloupe" ); $a = array ( 1 => "Banana", 0 => "Apple" ); 

And this, finally, is the reason why deleting a key that is not the last will overload your array and make it a dictionary:

 0 1 2 3 => remove 3 => 0 1 2 => still an array! 0 1 2 3 => remove 2 => 0 1 3 => NOT an array! 0 1 2 3 => remove 2 => 0 1 3 => not an array => remove 3 => 0 1 => again an array! 

If you subjected the array to some renumbering operation or array_values , you would again get a purely numerical array:

 /** * delete news items given their index. * * @param array $selected the list of indexes (eg [ 0, 1, 7 ]) * @return nothing */ function deleteNews(array $selected = [ ]) { try { $news = array_values( // Reorder... array_diff_key( // ...all keys... json_decode(file_get_contents('news.json', true), true), // ...in here... array_flip($selected) // ...that are not in $selected. ) ); } catch (\Exception $e) { // TODO handle errors (eg file not found and bad JSON) } try { file_put_contents('news.json', json_encode($news)); } catch (\Exception $e) { // TODO handle errors } // $url="./deleteNews.php"; // redirect($url); } 

So if your code deleted the last indexes of your array, it would seem that everything works. As soon as the reset did not create a hole in the array, or a text key was added ...

The same goes for sorting : [ 1 => "B", 0 => "A" ] is a JSON dictionary. Sort it, keeping key associations, by [ 0 => "A", 1 => "B" ] , and it becomes a JSON array.

One thing worth noting in your code: you read "news.json" with the path to true turned on. But then you save it in the current directory. If you have "news.json" somewhere else in your path besides the current directory, you will end up with two news.json files; Which of these two options will be used depends on the inclusion path itself.

Local files and security

Saving local files, as happens here with news.json , is fine, but some precautions are often ok.

As a general rule, it is best to place such "variable" files in your own directory (for example, "./cache" or "./temp") with a suitable .htaccess to prevent direct reading / execution if this is not necessary, and to allow can be performed more clearly.

For example, you can use the "./data" directory, and PHP files in other places should be read-only for the web server; and finally instruct, say, the Apache web server so that this writable directory by the web server cannot be easily used to operate the system:

 <directory "/var/www/mysite/htdocs/data"> # You CANNOT ask for /data and have a directory listing. Just in case. options -Indexes # You CANNOT save "news.php" and have it executed :-) php_flag engine off </directory> 

Thus, even if someone succeeded in downloading the file to your system, and this file contained executable malicious PHP code, the execution of this code would not be allowed (at least directly).

More about security: a real experience.

Allowing direct access to files under the control of a third party, even indirectly, is always potentially dangerous - for others, if not for you. For example, storing a JSON object with information collected on a third site. I just passed a successful attack test on a client website. Mutatis mutandis,

  • I created a page on my own website with custom data.
  • I logged into WWW.CLIENT with my account (OK, so I left traces ...)
  • I sent WWW.CLIENT to access EVIL.COM and receive data
  • From my own account, I could see what was then displayed on WWW.CLIENT depending on control codes, distorted UTF8 characters, and other tricks.
  • At the end of (1), I was able to enter a command to load a CDN-like Javascript file (located in EVIL.COM) and force it to execute (2) in the security context of WWW.CLIENT
  • After several more frauds, I was able to direct the WWW.CLIENT client (of course, with a test account) to a specially created link in WWW.CLIENT, which will silently supply EVIL.COM with client authentication tokens (3). (4) allow (for example) to modify existing orders by deleting its goods, adding its own and changing the delivery address (5).

As a result, five security flaws were used (for example, 4: the authentication token was not associated with the owner's IP address or was not regenerated with each transaction, and 5: changes in billing or delivery did not cause a request for re-entering credit card information). Although you might think that such security flaws were, in general, security flaws, and the client did not know about them, in fact they were well-known features designed specifically for usability. Deficiency 2 stems from the malicious use of eval() in a helper library that you don't want to change anytime soon. After all, changing the local data store (flaw 1) is likely to be the only intervention that I can really sell to this particular client.

+7
source

function deleteNews ($ selected) {$ file = file_get_contents ('news.json', true);

 $data=json_decode($file,true); unset($file); foreach($selected as $index){ unset($data[$index]); } foreach($data as $value){ $new_data[] = $value; } $result=json_encode($new_data); file_put_contents('news.json', $result); echo $result; unset($result); $url="./deleteNews.php"; 

// redirect ($ URL); }

0
source

Source: https://habr.com/ru/post/951654/


All Articles