Language Features

Magic Quotes

Magic quotes may have been one of the biggest sins of the past in PHP, and we should celebrate the fact that now, ultimately, it is gone from PHP. Magic quotes is a perfect example of how long it can take to undo a broken feature that was introduced into a programming language.

The basic idea of magic quotes, as always, was a good one. As we all know, user input is to be considered dangerous, at least from a security perspective. In fact, a healthy level of security paranoia is to consider every request an attack until the application manages to prove otherwise. With regard to security, we all have heard about the basic rule “filter input, escape output”.

SQL injection attacks, also well-known, can be extremely dangerous. Not only can they enable attackers to read arbitrary data from our database, they might even be able to modify existing data. One textbook example for an SQL injection, for example, is to use the following username in a login screen:

' OR 1=1; --

If the code builds an SQL query to retrieve the user’s ID from a MySQL database like this:

$sql = 'SELECT id FROM user WHERE username=' . $username .
' AND password=' . $password;

the resulting SQL will be:

SELECT id FROM user WHERE username='' OR 1=1; -- AND password=...

It does not really matter which password we enter, we have just gained access to the system without providing proper credentials, and are impersonating the first user listed in the user table. To make things worse, this is often the system administrator.

Magic quotes was conceived to fix this problem, and it utterly failed in trying to do so. The idea was to automatically escape incoming data to protect against SQL injections. With magic quotes enabled, single quotes were escaped with a backslash, rendering the described attack useless, because the SQL statements now expands to:

SELECT id FROM user WHERE username='\' OR 1=1; -- AND password=...

This SQL statement is syntactically incorrect, and cannot be executed. While, depending on how the application deals with such an error, there still may be annoying side effects, at least the application is not vulnerable to an SQL injection attack.

Or so it would seem. One big problem with magic quotes is that they could be enabled or disabled in php.ini. This means that application developers cannot safely rely on the fact that data has been escaped. So in fact, magic quotes made our world insecure because either application developers relied on PHP to automatically escape data, or it would lead to broken data because the application would escape the data again.

But there are even more problems with magic quotes. The SQL standard requires a single quote to be escaped with another single quote rather than a backslash. MySQL traditionally also accepts a backslash as the escape character, but newer versions of MySQL are stricter in following the SQL standards, so this is not guaranteed to work anymore. Current MySQL versions, for example, come with strict mode enabled by default, which uses the SQL-compliant single quote character rather than a backslash for escaping.

The main conceptual problem with magic quotes, however, is that this approach escapes the data too early. Correct escaping depends on the context. In the SQL context, single quotes are dangerous and must be escaped. In HTML context, the greater than and less than sign are dangerous and must be escaped. In other contexts, yet other characters must be escaped. Escaping too early without knowing the output context can never be secure.

Since an application could not safely rely on magic quotes to be enabled, it had to escape data explicitly by itself. To avoid double escaping, however, the application would have to detect whether magic quotes were enabled. And to be able to escape data correctly according to the output context, the application would in fact have to undo any escaping that PHP had magically done. This was necessary because it was not possible to disable magic quotes at runtime.

To sum it up, magic quotes is a feature that, in hindsight, nobody ever should have used. In fact, most of what made up the functionality has already been deprecated in PHP 5.3 and 5.4. The function set_magic_quotes_runtime and its alias magic_quotes_runtime, which has been deprecated since PHP 5.3, have finally been removed in PHP 7. Starting with version 5.4, PHP has raised an E_CORE_ERROR when this function was called. Trying to use this function in PHP 7 yields a fatal error:

Fatal error: Uncaught Error:
Call to undefined function set_magic_quotes_runtime() in ...

The two php.ini settings that control the magic quotes behavior, magic_quotes_gpc and magic_quotes_runtime have already been removed in PHP 5.4. Trying to set them in php.ini will lead to a fatal error:

PHP Fatal error:  Directive 'magic_quotes_runtime' is
no longer available in PHP in Unknown on line 0

Error messages “in Unknown on line 0” usually relate to parsing php.ini on PHP startup.

The functions get_magic_quotes_runtime and get_magic_quotes_gpc that allow you to query the respective php.ini settings still exist, but will always return false. In PHP 7.4, those functions have deprecated, so provided that your error_reporting level has been set to a sensible value, PHP will now also yell at you when you ask for magic quote settings:

PHP Deprecated:
Function get_magic_quotes_runtime() is deprecated in ...

PHP Deprecated:
Function get_magic_quotes_gpc() is deprecated in ...

If you upgrade to PHP 7 from a recent version of PHP 5, magic quotes should not be a problem for you. When you upgrade from PHP 5.3, make sure your code does not rely on magic quotes. If your code does, you probably have some serious security issues to deal with before you should proceed with the upgrade.

The proper fix is to escape data where it is used, and escape it for the context it is being used in. When it comes to escaping, you should not give in to a quick fix, but make sure you have a solid solution in place. Security does not come for free, and it cannot just be built into the language.

Assigning the Result of new by Reference

Back in ye olde PHP 4 times, PHP still was a procedural programming language with no proper support for object-oriented programming. Fortunately, this did not keep people from starting to use PHP as an object-oriented language, which in turn motivated building “proper” support for object orientation into PHP 5. Still, looking back to PHP 4, most things related to object-oriented programming were pretty cumbersome.

One particularly annoying “feature” of PHP 4 was to pass scalar values and objects by value, instead of passing objects by reference, as PHP 5 does.

Look at this PHP 4 example:

class Something
{
}

$a = new Something;

$b = $a;

An object is created and stored in $a (remember: we are talking PHP 4 here). The line $b = $a creates a copy of this object instance. PHP 5 would store a reference to the object in $a instead, and make $b another reference to the same instance. Actually, things are a little different inside PHP, but this is the behavior that is exposed.

To prevent PHP 4 from copying the object, we have to add an ampersand to the assignment:

$b =& $a

This forces PHP to make $b a reference instead of copying the value. In fact, PHP 4 even creates a copy after creating an object with new and assigning it to a variable, so we must also add an ampersand in the line that creates Something:

$a =& new Something;

PHP 4 basically forced the developers to sprinkle their code with lots of ampersands. If you forgot one, you would have two copies of the same object, modify one, and then look at the other copy, wondering why the modification had no effect. It usually took you a day or two to find out what was going on. Been there, done that.

In PHP 5, objects were passed by reference, so the ampersands were not necessary anymore. However, nobody kept you from putting them into the code, potentially even with a negative effect on performance. In PHP 7, you cannot assign the result of a new statement by reference anymore. Consequently, the code

class Something
{
}

$object =& new Something;

will result in a compile-time error in PHP 7:

Parse error: syntax error, unexpected 'new' (T_NEW) in ...

Once created, you can still assign objects by reference, if you insist:

class Something
{
}

$object = new Something;

$reference = &$object;

var_dump($object);
var_dump($reference);

This ampersand has no effect, however, since PHP 7 (and 5, for that matter) automatically treats objects by reference. In other words: when dealing with objects, nowadays it is safe to just drop any ampersands. You can confirm this by running the above program, then removing the ampersand, and re-running the program. In both cases, the output is

object(Something)#1 (0) {
}
object(Something)#1 (0) {
}

The two #1 tell us that both variables refer to an identical object instance.

Assigning a new result by reference in PHP 5 is still possible, but earns you an E_DEPRECATED error with the message Assigning the return value of new by reference is deprecated.

If you encounter any syntax error, unexpected 'new' compile-time errors when trying to execute your code in PHP 7, the fix is easy: search the codebase for all instances of =& new and = &new (both versions are possible, and you might have to search for multiple whitespace characters, not only one). Then remove the ampersands. Your code will still work fine – unless you try to execute it with PHP 4, of course.

Static Calls of Instance Methods

In PHP 4, quite a few things were a bit strange. One example is that PHP 4 allowed static calling of instance methods:

class Something
{
    function doWork()
    {
        var_dump('doing work');
    }
}

$something = new Something;

Something::doWork();

To keep backwards compatibility with PHP 4, this code works up to PHP 5, even though doWork() is not declared static, and called statically using :: rather than the object operator ->. PHP 5 at least complains with the E_DEPRECATED error:

Deprecated: Non-static method Something::doWork()
should not be called statically in ...

Now things will get really weird. When calling an instance method of another class statically, the $this context would carry over from the caller to the called class. In other words, $this suddenly refers to another object instance:

class Something
{
    public function doWork()
    {
        var_dump($this);
    }
}

class Another
{
    public function run()
    {
        return Something::doWork();
    }
}

$something = new Something;

$something->doWork();

$another = new Another;

$another->run();

While in PHP 5, this used to be an E_STRICT error, PHP 7 will emit an E_DEPRECATED error.

There are two problems with E_STRICT errors. First, E_ALL does not include E_STRICT before PHP 5.4, so in older PHP versions you have to set error reporting to E_ALL & ~E_DEPRECATED (both error levels are bit fields, so you need a bitwise or to combine both). Second, you have to enable this error reporting level in php.ini. Setting it at runtime by calling the error_reporting() function will not work, because E_STRICT error messages are already generated at compile-time.

When the above example gets executed with PHP 5, $this inside Something referred to the instance of Another, which is a really unexpected behavior. PHP 7, on the other hand, will complain about an undefined variable:

Notice: Undefined variable: this in ...

Since $this is undefined, trying to call a method will lead to another error, which in our example turns into a fatal error, since it is not caught:

Fatal error: Uncaught Error:
Using $this when not in object context in ...

One might wonder why such a weird behavior still has not been removed from PHP after more than 10 years. The reasoning is that before something can be removed from PHP, it has to be marked as deprecated first, only then it can be removed in the next major version. PHP upholds backwards compatibility, that is for sure. Even if it feels ludicrous, we will have to wait until PHP 8 to finally get rid of this PHP 4 relic.

Should your application call non-static methods statically, you can fix this by injecting an instance of the class. You can then modify the call to use the object operator -> instead of the :: operator:

class Something
{
    public function doWork()
    {
        var_dump($this);
    }
}

class Another
{
    private $something;

    public function __construct(Something $something)
    {
        $this->something = $something;
    }

    public function run()
    {
        return $this->something->doWork();
    }
}

$something = new Something;

$something->doWork();

$another = new Another($something);

$another->run();

First, we call the doWork() method directly on the instance of Something, then we call it from the instance of Another. The result is

object(Something)#1 (0) {
}
object(Something)#1 (0) {
}

In both cases, $this refers to the Something instance.

For the sake of completeness, we should mention that PHP also allows calling of static methods dynamically:

class Something
{
    public static function doWork()
    {
        var_dump('do work');
    }

    public function __construct()
    {
        $this->doWork();
    }
}

$something = new Something;

Fortunately, in this case, there is no problem with $this context propagation in the first place. This, however, does not argue away the fact that one should not write code like this.

ASP Tags

Originally, PHP was designed to be directly embedded into HTML. Today, it is considered a best practice to clearly separate PHP code from HTML, but for templating, both are often still mixed together.

In addition to the PHP open tag <?php and the short open tag <?, PHP, for historic reasons, also supported the so-called ASP tags <% and %>. In analogy to the <?= tag, a <%= $value %> tag exists, which echos the given value. ASP tags had to be explicitly turned on through the php.ini setting asp_tags.

This ini setting has been removed in PHP 7, together with the support for ASP tags. As you upgrade to PHP 7, you have to remove the ini setting, otherwise a fatal error occurs when starting up PHP:

PHP Fatal error:  Directive 'asp_tags' is no longer
available in PHP in Unknown on line 0

This makes perfect sense, because a project relying on ASP tags might otherwise deliver their source code to the browser by accident. This is not to say that this might have happened in the past, with ASP tags deactivated in php.ini.

In case your application uses ASP tags, the fix is simple: just replace <% tags with <?php, <%= with <?= and %> with ?>. You should at least run a Lint check on all files that you have modified, to make sure that you did not introduce syntax error by accident. To run a lint check on a PHP source file, call PHP at the command line using the -l command line switch.

call_user_method()

PHP is a very dynamic language. You can, for example, write code like

$method = $_GET('method');

SomeClass::$method();

We are not going to argue about the fact that writing such code is a very bad idea from a security perspective. The example just serves the purpose of showing how flexible PHP can be. Since it is a dynamic language, pretty much everything can be dynamically evaluated at runtime. This is the main reason why some problem can be solved with less code in PHP as compared to languages like Java that are compiled ahead-of-time. On the other hand, PHP gives developers a lot of rope to hang themselves with. With great power comes great responsibility.

PHP is also dynamic about the parameters passed to a dynamically called user-defined function, or method. The two built-in functions call_user_func() or call_user_func_array() perform dynamic function or method calls, allowing to pass arbitrary parameters:

class Something
{
    public function doWork($foo, $bar)
    {
        var_dump($foo, $bar);
    }
}

$something = new Something;

call_user_func([$something, 'doWork'], 'the-foo', 'the-bar');
call_user_func_array(
    [$something, 'doWork'], ['the-foo', 'the-bar']
);

In this example, we call the method doWork() twice, passing the strings the-foo and the-bar as arguments. When calling functions, the name of the function to call must be passed as the first parameter to call_user_func(), or call_user_func_array(), respectively. To call an instance method, we need to pass an array containing an object reference as the first element, and the method name as the second argument.

While call_user_func_array() expects an array of arguments that are passed to the called function, call_user_func() requires individual arguments. Other than that, both functions are exactly the same.

The result, unsurprisingly, is:

string(7) "the-foo"
string(7) "the-bar"
string(7) "the-foo"
string(7) "the-bar"

Before PHP 7, there also were the two methods call_user_method and call_user_method_array(), which basically just differed in the way the object reference and method name to call are specified. Even though those methods had already been deprecated in PHP 4.1, they were not yet removed in PHP 5. But now, finally, they are gone.

In case your code still uses them, the fix is simple. Just replace call_user_method() by call_user_func() and call_user_method_array() by call_user_func_array() and replace the first two parameters by an array like in the example above.

Removed set_socket_blocking()

The function set_socket_blocking(), an alias of stream_set_blocking(), has been removed in PHP 7. Luckily, working around this “problem” is as easy as globally searching and replacing a string in your codebase.

Removed XSL ini Option xsl.security_prefs

When using XSL stylesheets, sometimes additional files need to be read or even written to by the transformation process. To programmatically restrict access to this potentially dangerous feature set, the xsl extension provides the setSecurityPrefs() method.

Previous versions of PHP allowed predefining a security setting using the accompanying ini setting xsl.security_prefs. This configuration setting has been removed in PHP 7, all security settings now need to be set explicitly at runtime using the aforementioned function.

wddx is Gone

Believe it or not, there was a time before everybody used JSON and REST APIs. There is the Simple Object Access Protocol (SOAP) for example, which turned out to be not so simple after all, or XML-RPC, which essentially allows you to represent a remote function (technically: procedure) call and its response as XML documents.

The WDDX (Web Distributed Data eXchange) format, which dates back to 1998, is another XML-based serialization format. PHP used to support WDDX through the wddx extension, which could be compiled into PHP by using the --enable-wddx compile-time option, which in turn required the expat XML parser library to be available.

As of PHP 7.4, wddx is not bundled with PHP anymore, but has allegedly been moved to PECL, the home for C-based PHP extensions. At the time of writing of this chapter, however, no source code seems to be available (yet) at https://pecl.php.net/package/wddx.

We have never seen anybody actively use WDDX in recent years, and given that XML-RPC and SOAP are still valid options, nobody should really miss WDDX in PHP. If you desperately need it, there is always the option to create and parse the WDDX XML by hand.