With "magic quotes" disabled, why does PHP/WordPress continue to auto-escape my POST data?

I think I found it. Problem (bug): http://core.trac.wordpress.org/ticket/18322

Solution: http://codex.wordpress.org/Function_Reference/stripslashes_deep

    $_GET       = array_map('stripslashes_deep', $_GET);
    $_POST      = array_map('stripslashes_deep', $_POST);
    $_COOKIE    = array_map('stripslashes_deep', $_COOKIE);
    $_SERVER    = array_map('stripslashes_deep', $_SERVER);
    $_REQUEST   = array_map('stripslashes_deep', $_REQUEST);

Note: As suggested by @Alexandar O'Mara, you might want to reconsider overwriting the superglobals like this. If it's appropriate for your situation, for example, you might just "strip locally" using an alternative like $post = array_map('stripslashes_deep', $_POST);

Also see @quickshiftin's excellent answer.


Expanding on @rinogo's answer with a deeper explanation, and offering another workaround.


In wp-settings.php there's an unconditional call to wp_magic_quotes

// Add magic quotes and set up $_REQUEST ( $_GET + $_POST )
wp_magic_quotes();

WordPress escapes quotes no matter what

function wp_magic_quotes() {
    // If already slashed, strip.
    // Escape with wpdb.
    // Force REQUEST to be GET + POST.
}

What's interesting though is this call is made after plugins have been loaded, before the theme is loaded. Sooo, at the top of your plugin

// A hack to cope with un-configurable call to wp_magic_quotes
// E.G. Make the original $_POST available through a global $_REAL_POST
$_REAL_GET     = $_GET;
$_REAL_POST    = $_POST;
$_REAL_COOKIE  = $_COOKIE;
$_REAL_REQUEST = $_REQUEST;

Then you can freely use $_REAL_POST et al. in place of $_POST (remembering it's a global, not a superglobal) where you need to. Also remember that while your plugin has loaded before the theme, if the theme calls down into one of the plugin functions which uses $_POST, it should read from $_REAL_POST to get the unescaped values.