I co-organise the BrisPHP Meetup and at the start of every meetup I give a quick talk on recent news and other interesting things that have been happening in the PHP world. The biggest news recently is the development of PHP 7.4 and 8.0!

Language News

PHP 7.4 and 8.0 are both being developed in parallel with a number of really great features already implemented.

PHP 7.4 Features

There is quite a lot of features going into PHP 7.4, some of them I mentioned in my last PHP news post but here are some rew ones:

Covariance and contravariance

Currently, PHP is missing some really core object-oriented behaviours:

  • Contravariance
    • replacing an object with it’s parent without breaking anything
    • widening type parameters
  • Covariance
    • replace an object with it’s children without breaking anything
    • tightening type parameters

Fortunately, this feature is coming in 7.4, here are some examples from the rfc:

We can widen the type for method parameters, because anything that expects to be using the Concatable interface will successfully be able to use the Collection class when passing in Iterator types:

<?php

interface Concatable {
    function concat(Iterator $input); 
}
 
class Collection implements Concatable {
    // accepts all iterables, not just Iterator
    function concat(iterable $input) {/* . . . */}
}

We can tighten the return type because anything expecting the Factory interface can successfully use the UserFactory because it returns a subtype of object:

<?php

interface Factory {
    function make(): object;
}
 
class UserFactory implements Factory {
    function make(): User;
}

Weak References

Weak References allow you to keep a reference to an object without preventing the garbage collector from destroying it. Solutions for this already exist but have been rendered unusable by PHP 7.3. They’re useful for cache like structures, allowing the garbage collector to uncache things for you. Here’s an example:

<?php
function checkReference(WeakReference $ref): void
{
    if (null !== $ref->get()) {
        echo "Object still exists!\n";
        var_dump($ref->get());
    } else {
        echo "Object is dead!\n";
    }
}

$foo = new Foo();

$ref = WeakReference::create($foo);

checkReference($ref); // Object still exists!

// Somewhere else our object is removed
unset($foo);

checkReference($ref); // Object is dead!
// Cache miss, we'll have to re-create it

mb_str_split

We already have mb_split which allows splitting by a regex, but this allows splitting a multibyte string into set chunk sizes. Here’s an example from the rfc:

<?php 
print_r(mb_str_split("победа", 2));
 
--EXPECT--
 
Array
(
    [0] => по
    [1] => бе
    [2] => да
)

__serialize() and __unserialize()

We get two new magic methods which we can use instead of the current ways to serialize objects. These methods are added because the current ways being the Serializable interface and the __sleep() and __wakeup() methods are difficult to use and easily introduce bugs. See the rfc for more info.

<?php

// Returns array containing all the necessary state of the object.
public function __serialize(): array;
 
// Restores the object state from the given data array.
public function __unserialize(array $data): void;

PHP 8.0 features

Arrays starting with negative index

Currently, all the following result in unexpected behaviour:

<?php

$a = array_fill(-2, 3, true);

$b = [-2 => true, true, true];

$c = []
$c[-2] = true;
$c[] = true;
$c[] = true;

The keys following the negative number start at 0 and increment from there, ie. they skip the next negative numbers:

array(3) {
  [-2]=>
  bool(true)
  [0]=>
  bool(true)
  [1]=>
  bool(true)
}

Post 7.4 they will work as you would expect:

array(3) {
  [-2]=>
  bool(true)
  [-1]=>
  bool(true)
  [0]=>
  bool(true)
}

See the rfc for more info.

Consistent type errors for internal functions

Most of the internal PHP functions will throw a warning and return null (or some other ridiculous value) when you pass in parameters of an illegal type. This rfc will bring internal functions into alignment with user-defined functions and make them throw a TypeError.

JIT compiler

The performance improvements in PHP 7 were initiated by attempts to implement a JIT compiler, but were ultimately not released because the improvements made without it were much more substantial. However, we are now in a place where the performance of PHP can no longer be improved without JIT so this rfc will bring it into 8.0.

Arrow functions v2

Arrow functions are finally coming to PHP! They will dramatically improve the syntax of closures from this:

<?php

function array_values_from_keys($arr, $keys) {
    return array_map(function ($x) use ($arr) { return $arr[$x]; }, $keys);
}

To this:

<?php

function array_values_from_keys($arr, $keys) {
    return array_map(fn($x) => $arr[$x], $keys);
}

Other news

PSR 14 accepted

PSR 14 was accepted. It defines a standard for event dispatchers and listeners:

<?php

namespace Psr\EventDispatcher;

interface EventDispatcherInterface
{
    /**
     * Provide all relevant listeners with an event to process.
     *
     * @param object $event
     *   The object to process.
     *
     * @return object
     *   The Event that was passed, now modified by listeners.
     */
    public function dispatch(object $event);
}
<?php

namespace Psr\EventDispatcher;

interface ListenerProviderInterface
{
    /**
     * @param object $event
     *   An event for which to return the relevant listeners.
     * @return iterable[callable]
     *   An iterable (array, iterator, or generator) of callables.  Each
     *   callable MUST be type-compatible with $event.
     */
    public function getListenersForEvent(object $event) : iterable;
}
<?php

namespace Psr\EventDispatcher;

interface StoppableEventInterface
{
    /**
     * Is propagation stopped?
     *
     * This will typically only be used by the Dispatcher to determine if the
     * previous listener halted propagation.
     *
     * @return bool
     *   True if the Event is complete and no further listeners should be called.
     *   False to continue calling listeners.
     */
    public function isPropagationStopped() : bool;
}

PHPUnit 8 released

Containing mainly deprecations and dropping support for PHP 7.1, PHPUnit 8 was released.

Laravel gets Psalm support

The best static analyser (IMO) has released support for Laravel. It’s been a long time coming, mainly because of all the magic and static calls in Laravel, but it’s here and it will drastically improve your codebase.

SQL injection in Laravel

There were a couple of SQL injection vulnerabilities discovered in Laravel. Both of them come down to the developer passing untrusted data into query builders so if you follow best practices and don’t do this, then you’re fine.

The first one has been “fixed” by adding a warning to the documentation to let the developer know not to pass in untrusted data. Here is an example from the laravel blog post on the issue:

Rule::unique('users')->ignore($user->id);

If instead of using $user->id in the above function call, you use some user provided data, then you are vulnerable to an SQL injection.

The second one, described in a stitcher.io post relates to querying JSON data in SQL.

You write this:

Blog::query()
    ->addSelect('title->en');

Laravel converts it to this:

SELECT json_extract(`title`, '$."en"') FROM blogs;

But again, if you get 'title->en' from user supplied input, you’re vulnerable. Although, this particular vulnerability is fixed in Laravel 5.8.11.

It’s important to note, that while it is the frameworks job to help you build fast and secure applications, quite a lot of responsibility still falls on the developer to make sure they’re following best practices.

Remote code execution in TCPDF

A severe security vulnerability was found in TCPDF allowing an attacker to achieve the holy grail of attacks - a remote code execution. While this vulnerability was fixed in version 6.2.20, it was accidentally reintroduced so you have to use version 6.2.22 to be safe.

WordPress minimum PHP version

WordPress has ended support for 5.2 5.5 so you must now be using 5.6 or above. While this is absolutely great news that we’re finally raising the bar, we should keep in mind that version 5.6 has been unsupported since the beginning of the year. Let’s hope they will soon enforce a minimum supported version!

Symfony gets push capabilities

Symfony has released the Mercure component which allows integration with a Mercure Hub - an open protocol for servers to publish updates to clients. You either have to run your own Mercure Hub or use a third party service.

You then subscribe to topics using the vanilla JavaScript EventSource and publish updates to the topics from PHP. EventSource opens a persistent connection to the Mercure Hub and registers an event handler for updates.