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 :
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():
stripslashes_deep() already applies
stripslashes()
recursively, so it’s not necessary to do it manually. It actually usesarray_map()
internally.Ah, ok and thanks for this feedback. Maybe is a great point for, that I check this.
This is actually planned for core, but in a way that’s backwards compatible: https://core.trac.wordpress.org/ticket/22325
Good catch Ryan, I wasn’t aware of that ticket. Though it looks like it might be a while before it gets implemented.
The plan is to aim for ~3.9 or 4.0, I believe.
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']
.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.
Thanks for sharing this information. Keep up the work.
Thanks for giving this information