Replies: 0
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 every if statement before it has a return 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(...) with wp_update_post(...). It seems that the wp_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 🙂