WordPress’s…Interesting Way of Dealing with Magic Quotes

If you’ve been working with PHP for awhile, you’re probably familiar with one of the worst ideas the language’s developers ever came up with: Magic Quotes.

If not, here’s a brief history lesson. In order to help newbies write functioning MySQL queries, they thought it would be a great idea to automatically escape input data with slashes, overwriting the $_POST, $_GET and $_REQUEST globals. So if someone submitted hello, I'm Steve through a form, it would be immediately converted to hello, I\'m Steve so the apostrophe wouldn’t cause issue if a naive user tried inserting it into a database.

But what if you weren’t going to dump the data into a MySQL database? Too bad, it’s now full of slashes and you have to use stripslashes() on the variable. Also, you could conceivably end up with something like hello, I\\\'m Steve if you try escaping the data yourself before inserting the data into a database. It was a massive headache, and the normal practice ended up being “check to see if magic quotes are enabled at the top of your script, and strip the slashes out if the feature is activated. Then handle database queries with prepared statements or by properly escaping the data.”

Fortunately, the PHP project eventually came to their senses and deprecated magic quotes, finally removing the feature entirely with the new PHP 5.4.

Now…back to WordPress.

I was investigating an issue with a plugin where extraneous slashes were appearing in strings processed by $wpdb->insert() when I found this gem in wp-includes/load.php:

/**
 * Add magic quotes to $_GET, $_POST, $_COOKIE, and $_SERVER.
 *
 * Also forces $_REQUEST to be $_GET + $_POST. If $_SERVER, $_COOKIE,
 * or $_ENV are needed, use those superglobals directly.
 *
 * @access private
 * @since 3.0.0
 */
function wp_magic_quotes() {
	// If already slashed, strip.
	if ( get_magic_quotes_gpc() ) {
	        $_GET    = stripslashes_deep( $_GET    );
	        $_POST   = stripslashes_deep( $_POST   );
	        $_COOKIE = stripslashes_deep( $_COOKIE );
	}

	// Escape with wpdb.
	$_GET    = add_magic_quotes( $_GET    );
	$_POST   = add_magic_quotes( $_POST   );
	$_COOKIE = add_magic_quotes( $_COOKIE );
	$_SERVER = add_magic_quotes( $_SERVER );

	// Force REQUEST to be GET + POST.
	$_REQUEST = array_merge( $_GET, $_POST );
}

It checks to see if the server has Magic Quotes enabled. But instead of stripping the slashes when the feature is active, which is the standard practice, it adds slashes when it’s disabled. If I were working at a desk when I saw that, I would have hit my head on it.

Purportedly, it’s for backwards-compatibility with older, poorly-coded themes and plugins. While I understand the predicament, it’s a backwards solution. It encourages poor development practices, and creates obstacles for those trying to do things properly.

I hope they end up phasing this out sometime, but for now, we’ll just have to make do with this solution:

$post = array_map('stripslashes_deep', $_POST);
$get = array_map('stripslashes_deep', $_GET);
$request = array_map('stripslashes_deep', $_REQUEST);