Magic Quotes in WordPress, and How To Get Rid of Them

One thing that many developers are not aware of is that WordPress automatically adds magic quotes to request variables. This means that all quotes, backslashes and null-byte characters will be escaped with a backslash.

Even if you disable magic quotes in php.ini, WordPress will apply them to $_GET, $_POST, $_COOKIE and other superglobals anyway. If you ever notice unexpected slashes showing up in your input data, this is why.

The function that enables magic quotes is called wp_magic_quotes(). It is declared in /wp-includes/load.php and called in /wp-settings.php, shortly after WordPress finishes loading plugins.

/**
 * 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 );
}

So how do you access the clean, un-escaped data?  Unfortunately, it is not possible to completely turn off WordPress magic quotes without editing core files. However, since WordPress adds magic quotes after all plugins have been loaded, you can simply grab the unmodified variables while your plugin is loading (or in the “plugins_loaded” action) and store a local copy for your own use. Here’s an example:

class MyExamplePlugin {
	//Local copies of the PHP superglobals.
	private $get;
	private $post;
	private $cookie;

	public function __construct() {
		/* ... Other plugin code ... */

		add_action('plugins_loaded', array($this, 'captureRequestVars'));
	}

	public function captureRequestVars() {
		//Store the original GET and POST data + cookies.
		$this->get = $_GET;
		$this->post = $_POST;
		$this->cookie = $_COOKIE;

		//If magic quotes are actually enabled in PHP,
		//we'll need to remove the slashes.
		if ( get_magic_quotes_gpc() ) {
			$this->get    = stripslashes_deep($this->get);
			$this->post   = stripslashes_deep($this->post);
			$this->cookie = stripslashes_deep($this->cookie);
		}
	}

	public function doSomethingWithInput() {
		/* ... */

		//Use the internal copy of $_GET.
		$something = $this->get['something'];

		/* ... */
	}
}

$myExamplePlugin = new MyExamplePlugin();

Now you might be wondering: why go to all this trouble with copying built-in PHP variables when you could continue using them as normal and just run stripslashes() on any input data? There are several reasons:

  • Sooner or later, you’re going to forget to call stripslashes()somewhere. This can lead to hard-to-find bugs.
  • Code duplication. Why spread the backslash-stripping code through-out your entire codebase when you could put it in one place?
  • Forward compatibility. If the WordPress team ever decides to stop emulating this deprecated PHP misfeature, plugins and themes that indiscriminately apply stripslashes() to all input data will suddenly break.

That said, I wouldn’t count on WordPress dropping this “feature” any time soon. It’s a matter of backwards compatibility, and sometimes it seems WordPress considers compatibility more important than encouraging good programming practices.

Related posts :

10 Responses to “Magic Quotes in WordPress, and How To Get Rid of Them”

  1. Frank says:

    I use often the a function for useful work with Arrays, like

    $this->get = array_map( stripslashes_deep($this->get) );

    or more stable variant array_map_recursive() as alternative to array_map():

    	/** 
    	 * Deeper array_map() 
    	 * 
    	 * @param   string $callback Callback function to map 
    	 * @param   array, string $value Array to map 
    	 * @source  http://www.sitepoint.com/blogs/2005/03/02/magic-quotes-headaches/ 
    	 * @return  array, string
    	 */
    	static function array_map_recursive( $callback, $values ) {
    		
    		if ( is_string( $values ) ) {
    			$r = $callback( $values );
    		} elseif ( is_array( $values ) ) {
    			$r = array(); 
    			
    			foreach ( $values as $k => $v ) { 
    				$r[$k] = is_scalar($v) 
    					? $callback($v) 
    					: self::array_map_recursive( $callback, $v ); 
    			}
    		}
    		
    		return $r; 
    	}
    
  2. Jānis Elsts says:

    stripslashes_deep() already applies stripslashes() recursively, so it’s not necessary to do it manually. It actually uses array_map() internally.

  3. Frank says:

    Ah, ok and thanks for this feedback. Maybe is a great point for, that I check this.

  4. Ryan McCue says:

    This is actually planned for core, but in a way that’s backwards compatible: https://core.trac.wordpress.org/ticket/22325

  5. Jānis Elsts says:

    Good catch Ryan, I wasn’t aware of that ticket. Though it looks like it might be a while before it gets implemented.

  6. Ryan McCue says:

    The plan is to aim for ~3.9 or 4.0, I believe.

  7. Jānis Elsts says:

    Yep, that’s more or less what I thought.

    For what it’s worth, I’d personally prefer something like $currentRequest->get('key.subkey', $default) as a way to retrieve $_GET['key']['subkey'].

  8. Magie says:

    Ich finde diese Seite wirklich sehr Informativ, ich beschäftige mich selbst auch damit und freue mich dadurch umso mehr diesen Blog gefunden zu haben.

  9. Majid Ahmad says:

    Thanks for sharing this information. Keep up the work.

  10. enertser says:

    Thanks for giving this information

Leave a Reply