I’ve been writing PHP code for almost 15 years, and next year will be the 10 year anniversary of this blog.

Back when I started the blog, PHP 5 had just come out. It occurred to me that it might be interesting to take a quick look how my PHP code has changed in this timespan. It might also not be interesting, in which case: leave now!

So I dug up some old code and created two fictional code samples, representing a class I might have written in 2006 and 2016.

2006

This first example is based off an old open source project that originally had PHP 5.0.4 as the minimum version requirement.

Back then my toolkit contained:

Vim.

Firefox 1 (and installing it on anyone’s computer, whenever they weren’t looking).

A borrowed Powerbook G4 running OS X Tiger, or maybe a custom built PC running XP and two yellowed 15” CRT monitors.

Basically all development in a SSH session on a Slackware Linux box. Or maybe Debian.

PEAR.

Subversion.

A worn down F5 button on my keyboard for debugging.

<?php /** * A music player! * * @package Sabre_MusicPlayer * @version $Id$ * @copyright 2006 Rooftop Solutions * @author Evert Pot <evert@rooftopsolutions.nl> * @licence http://www.freebsd.org/copyright/license.html BSD License (4 Clause) * @uses Sabre_MusicPlayer_Song * @example ../examples/player.php */ class Sabre_MusicPlayer_Player { /** * Some id * * @var int */ public $id ; /** * Secret key * * @var string */ private $key ; /** * Repeat the song x times * * @param int $times * @return bool */ public function repeat ( $times ) { } /** * Adds a new song to the playlist * * @param Sabre_MusicPlayer_Song $song * @throws Sabre_MusicPlayer_DrmException * @return void */ public function addSong ( Sabre_MusicPlayer_Song $song ) { } /** * Returns all songs * * @return array */ public function getSongs () { return array (); } /** * Returns a singleton instance * * @return Sabre_MusicPlayer_Player */ public static function getInstance () { } /** * Secret stuff! */ private function secret () { } } ?>

For some crazy reason all my sources were indented by 4 spaces! Everything!

@version $Id$ was a special tag that subversion could recognize and would automatically insert version information in every file.

was a special tag that subversion could recognize and would automatically insert version information in every file. @licence was misspelled, and it would take me several years to find the mistake in hundreds of files.

2016

It’s almost 2016 now and PHP 7 is out. This is the style I’ll use for new projects:

<?php namespace Sabre\MusicPlayer ; /** * A music player! * * @copyright Copyright (C) fruux GmbH (https://fruux.com/). * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ class Player { /** * Some id * * @var int */ public $id ; /** * Repeat the song x times */ function repeat ( int $times ) : bool { } /** * Adds a new song to the playlist * * @throws DrmException * @return void */ function addSong ( Song $song ) { } /** * Returns all songs */ function getSongs () : array { return []; } /** * Returns a singleton instance */ static function getInstance () : self { } /** * Secret key * * @var string */ private $key ; /** * Secret stuff! */ private function secret () { } }

Starting next year, I’m dropping the copyright years from all source files. As far as I’m aware, there’s no real legal reason to keep them, so it’s vanity. It causes a massive ‘Happy new year’ commit every year in the Git repository though, so 2016 will be the last!

Since PHP 5.3, we got namespaces! This has a pretty major impact on line lengths in lots of places. It also removed the @package declaration and in many cases @use .

declaration and in many cases . A somewhat recent change is that I removed all public function and property modifiers where possible. The public keyword literally does nothing, and I’m unconvinced that it actually helps people understand the source. In fact, I think the opposite. The public keyword pushes the functionname further to the right which means more scanning.

function and property modifiers where possible. The keyword literally does nothing, and I’m unconvinced that it actually helps people understand the source. In fact, I think the opposite. The keyword pushes the functionname further to the right which means more scanning. I’m removing all @param , @var and @return tags where they can be inferred from the source. In the past I’ve always kept an entire block of @param tags for every argument. Now that most types can be typehinted (in PHP7) it’s not really needed anymore.

, and tags where they can be inferred from the source. In the past I’ve always kept an entire block of tags for every argument. Now that most types can be typehinted (in PHP7) it’s not really needed anymore. Before, I would list all properties in a class first, and functions second. Now I’ve started grouping them based on visibility. That means that the public properties come first, public functions next, protected properties, and so on. This makes a lot more sense for people reading the source, as someone who only interacts with the public API of a class, does not need to read about protected or private properties. It will move the things that are more likely to be relevant upward.

PHP 5.4 array syntax. Even though this change was mostly cosmetic, it’s the primary reason I dropped PHP 5.3 support when I reasonabily could.

Since many years none of my sources have had a ?> , which probably helped me avoid a bunch of bugs before I used PHPUnit/PHP-CS-Fixer consistently.

I still don’t care about PSR-2. I helped split PSR-1 and PSR-2 to ensure that people can follow the important stuff (PSR-1) while keeping creative freedom with their sources (PSR-2). I’m not about to change that in 2016.

Right now I’m considering two more controversial changes:

Reducing all indentation in classes by 1 more level. This will place every function right on the first column. There’s no super strong argument against this, as you kind of know you’re working inside a class. Change my public $foo declarations back into var $foo , to really drive home the point that “we don’t need public ”. I’m worried that that one doesn’t really carry many benefits other than “nonconformance is fun”.

Other changes

Moved from Google Code Hosting to Bitbucket (briefly) to Github.

Moved from Subversion, to Mercurial to Git.

Moved from PEAR packages on “Pearfarm” to Pirum hosted on free SourceForge hosting to Composer and Packagist.

Moved from PSR-0 to PSR-4.

Really went all-in with PHPUnit, running all my tests on Travis CI.

Went from being really excited about OS X, to kind of hating it.

Last thoughts

Looking back that’s a fair amount of changes. Some of them were painful, either because it was a lot of work (moving to namespaces) or because it meant it’s hard to change habits (for example mercurial. Still sad about that) or because I’m plain stubborn. Aside from that, most of these changes were gradual, and it’s kind of interesting to take a step back and look at them all at once.

What’s also interesting is how forward looking PHP Documentor has been. Many things that were in PHP Docblocks are now core PHP language features, the latest examples being scalar typehints and the splat operator.

I wonder if that’s one day true for the following expressions as well:

/** * @param int|string * @param int[] * @throws Something * @property-read int $myProperty */

Aside from my sources, I also think that architecturally I’ve moved to a more minimal ‘get the job done’ type of approach. In the early days of PHP 5 I think a lot of us copied a lot of things that were happening in the Java-world, but the reality is that that’s a widely inappropriate place to look for architectural inspiration when you’re developing PHP applications.

The reality of PHP is that your entire program will need to initialize, handle the request, serve the response and die again in milliseconds. A Java developer only has to optimize the middle part.

So over time I feel like my source has become more Javascript-like. Less typing, less objects for every bit of data, less configuration and a lot more more convention, assumption, functional and event-based. Whereas before I might have started a new bit of functionality as an abstract class or interface, now it’s likely to be a function until the point where I know polymorphism will pay off. I think many people have seen this and moved from heavy java-inspired frameworks (such as ZF1, Doctrine) to lighter solutions (such as Silex and Slim and using non-relational datastores or plain PDO).

Bonus 2004 version

I also dug up an even older project to look at my style then. My code back then was pretty awful, and I didn’t really bother with many things such as escaping or PHPDoc. So instead, here’s how it might have looked like if I was a developer in 2004 and knew what I was doing. I tried to largely follow PEAR’s coding standards from back then.

<?php /** * A music player! * * @package Sabre_MusicPlayer * @category Music * @author Evert Pot <???@hotmail.com> * @copyright 2004 Rooftop Solutions * @license http://www.freebsd.org/copyright/license.html BSD License (4 Clause) * @version CVS $Id$ * @uses Sabre_MusicPlayer_Song * @since File available since version 2.0 */ /** * A music player! * * @package Sabre_MusicPlayer * @category Music * @author Evert Pot <???@hotmail.com> * @copyright 2004 Rooftop Solutions * @license http://www.freebsd.org/copyright/license.html BSD License (4 Clause) * @version Release: @package_version@ * @uses Sabre_MusicPlayer_Song * @example ../examples/player.php * @since Class available since version 2.0 */ class Sabre_MusicPlayer_Player { /** * Some id * * @var int * @access public */ var $id ; /** * Secret key * * @var string * @access private */ var $_key ; /** * Repeat the song x times * * @param int $times * @access public * @return bool */ function repeat ( $times ) { } /** * Adds a new song to the playlist * * @param Sabre_MusicPlayer_Song $song * @access public * @return true|PEAR_Error */ function addSong ( Sabre_MusicPlayer_Song $song ) { } /** * Returns all songs * * @access public * @return array */ function getSongs () { return array (); } /** * Returns a singleton instance * * @access public * @static * @return Sabre_MusicPlayer_Player */ function getInstance () { } /** * Secret stuff! * * @access private * @return void */ private function _secret () { } } ?>

A few notables here: