PHP at EGPHP

Everything we build begins with PHP.

Our team has written and run PHP for twenty-five years, since PHP 4. The servers, the control panel and the sites we look after all start here.

Twenty-five years, release by release

We did not read about these versions. We upgraded real sites through each of them, and every row below is a change we had to deal with on a live server.

  1. PHP 4 2000

    Sessions and output buffering, built in

    The Zend Engine arrived, and sessions and output buffering became part of the language instead of add-ons. A generation of early dynamic sites was written against it.

    • Zend Engine
    • session_start()
    • ob_start()
    What it meant for us

    Where our team started: hand-written sites on shared servers, with register_globals still switched on.

  2. PHP 5.0 2004

    A real object model

    Zend Engine 2 brought classes with visibility, interfaces and exceptions, objects passed by handle instead of copied, and the MySQLi and SimpleXML extensions.

    • private
    • interface
    • try / catch
    • mysqli
    What it meant for us

    Code could be organised into classes a team shares, and the database got a modern driver.

  3. PHP 5.3 2009

    Namespaces and closures

    Namespaces, anonymous functions and late static binding: the ground that Composer and the shared package ecosystem were later built on.

    • namespace
    • function () use ()
    • static::
    What it meant for us

    Libraries from different authors could finally live in one project without their names colliding.

  4. PHP 5.4 2012

    Traits, short arrays and a clean-up Broke old code

    Traits, the short [] array syntax and a built-in development server, while register_globals, magic quotes and safe mode were removed for good.

    • trait
    • [ ]
    • php -S
    What it meant for us

    Old sites that relied on register_globals stopped working, and upgrading them meant fixing them first.

  5. PHP 5.5 2013

    OPcache in the box

    OPcache shipped with PHP itself and kept compiled scripts in memory, alongside generators and password_hash().

    • opcache
    • yield
    • password_hash()
    What it meant for us

    Compiled code kept in memory instead of re-read on every request. It is still the first setting we check on any PHP server.

  6. PHP 6 never released

    The version that never shipped

    PHP 6 was planned around native Unicode strings and abandoned. The next release was numbered 7 so it would not be confused with the books and plans written for 6.

  7. PHP 7.0 2015

    The speed jump Broke old code

    A rebuilt engine, which the PHP project described as up to twice as fast as 5.6 while using less memory, plus scalar and return type declarations, ?? and <=>.

    • int $id
    • : string
    • ??
    • <=>
    What it meant for us

    The old mysql_* functions were removed, a frequent reason an old site broke on this upgrade.

  8. PHP 7.4 2019

    Typed properties and preloading

    Typed properties, arrow functions, the ??= operator, OPcache preloading and FFI.

    • public int $id
    • fn ($x) =>
    • ??=
    What it meant for us

    The last 7.x release, and the one many older applications stayed on longest, which is why we still move sites off it.

  9. PHP 8.0 2020

    JIT, attributes and named arguments Broke old code

    A JIT compiler, attributes, named arguments, union types, match, the nullsafe ?-> operator and constructor property promotion.

    • #[Attribute]
    • match
    • ?->
    • JIT
    What it meant for us

    Many warnings became errors and comparisons between strings and numbers changed, so code that ran quietly on 7.4 could stop on 8.0.

  10. PHP 8.1 2021

    Enums, readonly and fibers

    Native enums, readonly properties, fibers, the never return type and first-class callable syntax.

    • enum
    • readonly
    • Fiber
    • never
    What it meant for us

    Fibers opened the way for asynchronous PHP libraries without changing how an ordinary request runs.

  11. PHP 8.2 2022

    Readonly classes, and a warning for old code

    Readonly classes, DNF types, true, false and null as standalone types, and a new Random extension.

    • readonly class
    • (A&B)|null
    • Random\Randomizer
    What it meant for us

    Dynamic properties were deprecated, and older plugins filled the logs with notices until they were updated.

  12. PHP 8.3 2023

    Typed constants and #[\Override]

    Typed class constants, the #[\Override] attribute, json_validate() and dynamic class constant fetch.

    • const string
    • #[\Override]
    • json_validate()
    What it meant for us

    A quieter release, which makes it an easy step for sites already on 8.2.

  13. PHP 8.4 2024

    Property hooks

    Property hooks, asymmetric visibility, lazy objects, an HTML5-aware DOM parser, and new MyClass()->method() without the extra brackets.

    • get { }
    • public private(set)
    • Dom\HTMLDocument
    What it meant for us

    Getters and setters that used to need a method each now belong to the property itself, so there is less code to maintain.

  14. PHP 8.5 2025

    The pipe operator Current

    The |> pipe operator, a built-in URI extension, clone with, the #[\NoDiscard] attribute, array_first() and array_last(), and a backtrace on fatal errors.

    • |>
    • Uri\Rfc3986\Uri
    • clone($o, [ ])
    What it meant for us

    The current release line, and the one we start new sites on when their code supports it.

Years are those of each first release in the PHP project's own release history.

Follow one request down the stack

A page on a PHP site passes through five layers on its way in, and back up through the same five on its way out. We look after every one of them, and each has its own page.

  1. The visitor

    A browser asks for a page over HTTPS. The certificate, the firewall and the rules in front of the server decide whether the request is let in at all.

  2. The web server

    Nginx or LiteSpeed takes the connection, serves images and files itself, and hands only the PHP work to the pool behind it.

  3. The PHP runtime

    PHP-FPM pools, one per site, each on the PHP version its code needs, with OPcache warm and workers sized for the traffic.

  4. Your code

    A framework or a ready-made platform turns the request into a page: your own Laravel application, or WordPress, Magento, Moodle and the rest.

  5. The data

    MySQL or MariaDB keeps the records, and Redis keeps in memory what is asked for again and again.

Under all five

The server itself

Our own servers, run by the same team, with our own control panel on top.

Moving to a newer PHP without a bad morning

Each site gets its own pool, so one server can run several PHP versions side by side and every site moves when its code is ready, not when the server is.

  1. We list what each site runs

    Its PHP version, its extensions, and the plugins or packages that hold it back.

  2. A copy tries the new version

    The site is copied and run on the next PHP version, and its error log is read line by line.

  3. We fix what breaks

    Removed functions, deprecated calls and plugins with no update are fixed or replaced.

  4. One site at a time

    Each site switches to its new pool on its own, and can be switched back at once if anything was missed.

php-fpmOne server, several versions
SitePHPPoolState
shop.example.com 8.4 fpm-shop live
blog.example.com 8.3 fpm-blog live
crm.example.com 7.4 → 8.3 fpm-crm on a copy
learn.example.com 8.3 fpm-learn live
An example server

Bring us your PHP

An old site that needs a newer PHP, a platform to install, or a server to run it all on. Every job is quoted after a short conversation.