This year PHP turned 25 and, as with all things, the hope is that with age comes wisdom and maturity. Often derided as a great way to write bad (and insecure) code, PHP is hard to ignore completely when it is used in nearly eight out of ten websites. With PHP…

Welcome to LWN.net
The following subscription-only content has been made available to you
by an LWN subscriber. Thousands of subscribers depend on LWN for the
best news from the Linux and free software communities. If you enjoy this
article, please consider accepting the trial offer on the right. Thank you
for visiting LWN.net!
Free trial subscription
Try LWN for free for 1 month: no payment
or credit card required. Activate
your trial subscription now and see why thousands of
readers subscribe to LWN.net.

This year PHP turned 25 and, as with all things, the hope is that with age comes
wisdom and maturity. Often derided as a great way to write bad (and
insecure) code, PHP is hard to ignore completely when it is used in nearly
eight out of ten
websites. With PHP 7.4.5 released in
April, it’s worthwhile to take a
look at modern PHP, how it has evolved to address the criticisms of the
past, and what lies ahead in its future.
The evolution of OOP in PHP
PHP’s object model has had some pretty rough patches in the past. When
objects were first introduced in PHP 4, the implementation was essentially
an array with function references and was horribly problematic. The
fundamental problems of that implementation were thankfully addressed with
the release of PHP 5, featuring an entirely rethought object
implementation. In the versions that have come since, up through version
the current 7.4 releases, numerous interesting language features
for object-oriented programming (OOP) have emerged.
While still a dynamically typed language, PHP does now support a robust
typing mechanism including the typing
of function parameters and return
values along with the use of scalar types. New in
PHP 7.4 is typing for object
properties. Combining those features with namespaces,
interfaces,
traits,
and iterables
results in a well-rounded and modern
object-oriented language.
If you still think PHP lacks an appropriate object model, you might be
pleasantly surprised taking a look again.
For example, consider this brief example encapsulating many of the now-available PHP OOP constructs:
<?php
namespace App\LWN;
use App\LWN\Database;
use App\LWN\Contracts\Collection
use App\LWN\Filter as MyFilter;
use App\LWN\PrettyArray;
class Example extends Database implements Collection
{
use PrettyArray;
protected array $data;
public function filter(?MyFilter $filter) : iterable
{
/* … filter $this->data based on the $filter object’s logic … */
return $filtered;
}
}
This snippet defines a class called Example, which extends
from the base class Database, and that implements the Collection
interface. The fully qualified class name — including namespace —
would be App\LWN\Example. Four pieces are
imported: the Database class,
Collection interface, Filter type (aliased as
MyFilter), and
PrettyArray trait. In the body of the class, we usePrettyArray in order for the class to take advantage of
the properties and methods defined in the trait.
The class also
defines a single array property $data (scoped as
protected) and adds a single
class method filter() scoped for public
access. The filter() method accepts
a single optional parameter $filter that must either be an
instance of App\LWN\Filter
(aliased to MyFilter) or NULL. The method returns
an iterable pseudo-type, which
indicates that the method must either return a PHP array or any other object
that implements the built-in PHP Traversable
interface. This allows callers to know that the results can be traversed using
other language constructs such as
foreach.
Proper dependency management and third-party extensions
In the early days of PHP, two repositories, PEAR and PECL, were created to catalog both reusable
PHP code and non-bundled, C-based extensions, respectively. PECL
continues to be a valuable collection with over 400 extensions, from authentication
to data
structures, however it has failed to become a widespread tool allowing PHP
developers to use those extensions because they introduce system-level
dependencies that are generally unavailable on cheaper hosting providers. PEAR,
being pure PHP code, was more successful in adoption yet struggled to
become the tool it was intended to be. Unfortunately code age and quality proved
to be too big of a hurdle to overcome and, because of this,
PEAR was officially disabled by default as of PHP 7.4.
In lieu of PEAR, the PHP community has adopted Composer. Similar to Node’s
npm or Python’s pip, Composer has been adopted by
nearly every major PHP-based software package to create a community of
well-built, well-maintained, and dependency-conscious PHP modules
that can be added simply into an application. For example, installing
the Stripe payment API is now similar to what you might find in other
languages:
$ composer require stripe/stripe-php
This command finds the package in the Packagist repository (by default),
installing it in the vendor directory (in the current
directory) along with any necessary dependencies. Using it requires the
existence of a composer.json
file in the project to define the project’s specific version dependencies
for both PHP and needed packages. From this, Composer generates a
composer.lock file for the project to track exactly what is
installed and should be kept in the project. To use the components managed
by Composer, the ./vendor/autoload.php file can be loaded
using require_once in your application; it will do the
necessary bootstrapping and class resolution on instantiation using PHP’s
autoloading
mechanisms.
Most modern PHP frameworks and PHP-based applications today rely on
Composer, yet one notable holdout is the ever-popular WordPress blogging package. Efforts in
the WordPress community continue to move toward adoption, but it
appears to be a massive undertaking that will not be completed soon.
PHP gets proper FFI
One interesting new feature that has come with the release of
PHP 7.4 is a foreign
function interface (FFI). This has been an obvious omission when
comparing PHP to other languages and has the potential to make a big impact
in the PHP arena. To understand the potential impact we need to revisit
PECL alongside a bit of PHP release and architecture background.
PHP has always favored a “batteries
included” approach. Various extensions that are not really considered
part of the language by internals developers (such as various database drivers) have
been bundled as part of the official release since PHP 3. Other languages
have struggled with this
topic from time to time, but for PHP the problem is particularly acute
due to external factors. Since it is a language that is typically deployed as
part of a web server, the vast majority of PHP deployments are simply
provided to end-users as part of hosting packages without
the ability to add extensions. For example, most cost-effective
WordPress hosting services do not provide any third-party extension
functionality.
While the bundling strategy succeeds in ensuring that a wide range of
consistent functionality exists across hosting providers, it comes at the
cost of application support nightmares for anything beyond that even for
self-managed servers. For example, if a non-standard PECL extension
existed that did exactly what was needed, the process of loading it and
maintaining it on server farms and across PHP versions can easily become
unnecessarily painful to system administrators everywhere.
Moreover the bundling approach also creates a bottleneck for PHP
releases themselves, as there is much more code (features, bug fixes,
etc.) that has to go into every release.
FFI in PHP 7.4 has a lot of potential benefits. Where currently C-based
extensions provide the glue to map the functionality of shared libraries
into PHP APIs, a baked-in FFI allows those PHP API implementations to be
done in PHP itself moving forward.  As PHP code, this new form of
extension is manageable by the widely-adopted Composer, reducing a major
barrier to the use of shared library APIs currently
only available as extensions in PECL (or possibly not available at all).
This all leads to what may
ultimately be the first step away from the “batteries included” approach
taken to date while broadening the capacities of the overall PHP
community. Of course, the hosting providers will need to cooperate by
installing the underlying development packages for the shared libraries on their servers.
If successful, this would be a wonderful win for the internals
community and certainly is enabling to the community at large. The
internals community would gain a path forward to solving the “batteries
included” problem by replacing C-based extensions with PHP-based
FFI-enabled ones, without sacrificing consistency across hosting providers.
For the general PHP community, FFI enables the use of shared system
libraries with only the need to understand and implement the aspects
needed for their specific problem and, importantly, without any specific
PHP internals knowledge. Composer packages (and PHP applications) are
ultimately a large composite of various API bindings, so simply being
able to use what is needed via FFI is a major improvement over needing
someone to implement a full binding for general-purpose use in C. An
example of how the FFI works can be
seen in the TensorFlow
bindings for PHP, written by PHP core developer and author of the FFI
implementation Dmitry Stogov.
Although FFI is a relatively straightforward feature taken by itself, it will
be worth watching how it melds into the hosting provider and bundling
situation. As part of the core, it may enable hosting providers a mechanism to
expand their abilities securely and enable the community in a lot of
positive ways.
Making security less obscure
As PHP has matured, so has its focus on security. PHP gets
a bad reputation on security, due in no small part to various ill-conceived
language features of the past, such as the notorious
register_globals and safe_mode
settings. Thankfully it has been ten years since register_globals
and safe_mode were removed in PHP 5.4. Most PHP-related
security issues don’t come from the implementation of the language these days but
rather from insecurely written code. To address this, a lot of effort has been put in to make
handling complicated security issues as straightforward for PHP developers
as possible.
This has taken the form of a couple of key technologies that have
appeared in more recent versions of PHP. Starting with PHP 7.2, the
popular libsodium
cryptographic library has been included as part of the PHP tool set,
providing high-quality cryptographic services to developers. For
hashing critical data such as passwords, PHP also bundles
the Argon2 password-hashing
algorithm (the winner of a multi-year password-hashing competition).
Of course, there have been continuous improvements and additions to the
built-in filtering
and sanitization tools that were first included in
PHP 5.2. These provide the facilities to
consistently and reliably ensure external input is what developers expect
it to be.
Through the widespread adoption of proper dependency management, key
security-related technologies, and modern frameworks that implement all of
this intelligently, PHP has made welcome and significant security-minded
improvements over the years.
Performance increases
PHP has continuously worked to improve the per-request performance of
its code and has made serious improvements as time has gone on. PHP 7.4
can handle three
times as many requests per second as PHP 5.6 and is roughly 18% faster
than even PHP 7.0.
Adding to these overall performance improvements is the new PHP 7.4
preloading feature. According
to Stogov, who is the original feature proposer:
On server startup before any application code is run we may load a
certain set of PHP files into memory and make their contents permanently
available to all subsequent requests that will be served by that
server. All the functions and classes defined in these files will be
available to requests out of the box, exactly like internal entities.
Enabling preloading is easily done using the
opcache.preload configuration directive and specifying the
path to a PHP script. All classes and functions referenced within that
script will be permanently loaded into memory and referenced in each server
request as needed. In exchange for this additional memory usage on the
server, resources used every request are universally available resulting in
considerably faster execution times. Unfortunately, it also means that
changes to the preloaded
files will require a server restart to take effect.
Last but not least is just-In-time
(JIT) compilation to machine code for PHP, which is provided as an experimental
feature in PHP 7.4 and is slated for official release in
PHP 8. Described as “extremely
simple” in its present form compared to implementations such as V8
and PyPy, PHP’s forthcoming JIT implementation will be powered by DynASM (developed for the LuaJIT
project). It will likely serve as the bedrock for future execution-speed
improvements.
The future of PHP
Based on past history, PHP is likely to continue with releases in the
7.x branch, adding incremental improvements, features and bug fixes while
PHP 8 is still on the horizon. It is difficult to predict which new
features may be rolled into 7.x, and which will be reserved for 8.0.
Historically, PHP 5.x was the beneficiary of many improvements
originally slated for the never-released PHP 6 when development stalled on that
effort.
It is worth noting that it is unlikely PHP 8 will suffer the
same fate as PHP 6, however, as developer Derick Rethans has provided a rough schedule leading to a
December 2020 launch for PHP 8 (with early alphas being available in
June). Since significant features for that release are already committed, it
seems likely that timeline will more or less hold. According to the
available RFCs being considered we can expect JIT and
improvements to the object model, various consistency tweaks, improvements
to syntax, and more.

Did you like this article? Please accept our
trial subscription offer to be
able to see more content like it and to participate in the discussion.

(Log in to post comments)