Major flaws breaking JSON data, suggested fix.
-
Hello!
This is a great plugin, however I’ve unfortunately found some concerning major issues when updating from version 3.3.7 to 3.3.9.
When updating posts with JSON stored in the post_content column escaping stashes are removed. For example I am using a plugin called TablePress which stores content that may look like this:
[["test cell 1","test cell 2"],["test cell 3","<a href=\"/uploads/2020/02/example.png\">Link</a>"]]
After doing a replace and update all links the data looks like this:
[["test cell 1","test cell 2"],["test cell 3","<a href="/uploads/2020/02/new-example.png">Link</a>"]]
Where the back slashes for the href quotes are removed.
The problem lies in classes/replacer.php.
First the function isJSON (line 567) will ALWAYS return false. This is what it looks like:
private function isJSON($content) { if (is_array($content) || is_object($content)) return false; // can never be. $json = json_decode($content); $json = is_string($content) && json_decode($content); return $json && $content != $json; }
So the $json variable is being overwritten as a boolean and will always be not equal to $content. I think it should look like this:
private function isJSON($content) { if (is_array($content) || is_object($content)) return false; // can never be. $json = json_decode($content); $isjson = is_string($content) && json_decode($content); return $isjson && $content != $json; }
Next in the replaceContent function (line 503) at the bottom there is a check that does
$content = json_encode($content);
. However since everyif
statement before it has areturn
it will never reach that part of the function if the content is JSON.I think this:
private function replaceContent($content, $search, $replace) { //$is_serial = false; $content = maybe_unserialize($content); $isJson = $this->isJSON($content); if ($isJson) { $content = json_decode($content); } if (is_string($content)) // let's check the normal one first. { return str_replace($search, $replace, $content); } elseif (is_wp_error($content)) // seen this. { return $content; // do nothing. } elseif (is_array($content) ) // array metadata and such. { foreach($content as $index => $value) { $content[$index] = $this->replaceContent($value, $search, $replace); //str_replace($value, $search, $replace); } return $content; } elseif(is_object($content)) // metadata objects, they exist. { foreach($content as $key => $value) { $content->{$key} = $this->replaceContent($value, $search, $replace); //str_replace($value, $search, $replace); } return $content; } if ($isJson) // convert back to JSON, if this was JSON. Different than serialize which does WP automatically. { Log::addDebug('Value was found to be JSON, encoding'); $content = json_encode($content); } return $content; }
Should look like this:
private function replaceContent($content, $search, $replace) { //$is_serial = false; $content = maybe_unserialize($content); $isJson = $this->isJSON($content); if ($isJson) { $content = json_decode($content); } if (is_string($content)) // let's check the normal one first. { $content = str_replace($search, $replace, $content); } elseif (is_wp_error($content)) // seen this. { // do nothing. } elseif (is_array($content) ) // array metadata and such. { foreach($content as $index => $value) { $content[$index] = $this->replaceContent($value, $search, $replace); //str_replace($value, $search, $replace); } } elseif(is_object($content)) // metadata objects, they exist. { foreach($content as $key => $value) { $content->{$key} = $this->replaceContent($value, $search, $replace); //str_replace($value, $search, $replace); } } if ($isJson) // convert back to JSON, if this was JSON. Different than serialize which does WP automatically. { Log::addDebug('Value was found to be JSON, encoding'); $content = json_encode($content); $content = wp_slash($content); } return $content; }
Finally you removed your custom insert SQL around line 474 and replaced
$wpdb->query(...)
withwp_update_post(...)
. It seems that thewp_update_post
function uses stripslashes or something of that sort. This brings us back to our main problem of slashes being removed from our JSON content.To fix this I added this as a workaround on line 541 in the replaceContent function after the json_encode is called:
$content = wp_slash($content);
Note this also happens for other WordPress functions such as update_metadata() if you happen to be using those anywhere – didn’t have time to check myself. (See this WordPress Stackexchange question as reference: WordPress is stripping escape backslashes from JSON strings in post_meta)
Anyway I am not sure if this is the correct solution or how it may affect other use cases. I doubt I am anywhere as familiar with the plugin as you the Authors are but it seems to have resolved the issue for me.
I hope this helps and that there is a patch in the next update. Again thanks for the awesome work you’ve put into this plugin ??
- The topic ‘Major flaws breaking JSON data, suggested fix.’ is closed to new replies.