PHP code in 2006 and 2016
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.@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
. - A somewhat recent change is that I removed all
public
function and property modifiers where possible. Thepublic
keyword literally does nothing, and I’m unconvinced that it actually helps people understand the source. In fact, I think the opposite. Thepublic
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. - 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 intovar $foo
, to really drive home the point that “we don’t needpublic
”. 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:
- There was no
public
,private
andprotected
. Everything was public and you’d use PHPDoc to inform the user of the visibility with@access
. I’m fairly certain that people were so excited for PHP 5 visibility keywords, that that was also the prime reason people placedpublic
in every method signature. - There was also no
static
orabstract
. Any method could be called statically. - Private members were generally prefixed with an underscore. This habit stuck around in many projects for years in the PHP 5 days.
- CVS for the win!
Comments
Beobachter •
* @license http://www.freebsd.org/copyright/license.html BSD License (4 Clause)
w
* @version Release: @package_version@
Ha! Vim novices. :)
Evert •
Busted!
Joshua Gigg •
In your 2016 code, you say you drop public from function definitions, but you still have it as part of `getSongs()`
Evert •
Fixed thanks :) copy paste mistake
Fab G •
secret() has the "function" keyword missing ;)
Evert •
Thanks! Crazy how many little mistakes I ended up having.
aaronbieber •
A lot's changed. When I first learned PHP, it was version 3 and didn't have classes at all! The "register globals" setting was turned on by default and we mashed HTML and PHP code, with database queries, into the same files!
sdwrage •
OCommerce, is that you? :P
Carlos Rodrigues •
Why do you think people will follow PSR-1, which contains "important stuff" if you don't follow PSR-2? People might think that PSR-1 is not "important stuff" as well. Not trying to be an ass, just asking you to think about this.
Evert •
I think if you are a seasoned PHP developer and you read PSR-1 and PSR-2, you will likely land on the same conclusion. Even if you think PSR-2 is a great idea, it's hard to argue that PSR-1 has a greater set of benefits over PSR-2.
am •
You have
private $key;
twice in 2016
Lewis Cowles •
Sabre\MusicPlayer\Player...
Would you ever consider dropping MusicPlayer and switching to Music?
Sabre\Music\Player
I Just think it reads more easily.
Evert •
Yea fair enough =P It was a fictional example though!
Lewis Cowles •
Yeah Sabre is the Dav solution in PHP right?
Evert •
Yea, and a few other libraries: http://sabre.io/
Freddy •
If I'm not mistaken, by droping the public keyword you are actually making changes.
When the public keyword is missing, the method is also automatically a static method.
In most cases this won't be an issue, but just mentioning ;)
Evert •
I had to check to make sure, but this is definitely not true. The public keyword in function declarations does nothing. See here for proof:
https://3v4l.org/AjY42
Freddy •
Oops, your right.
In 5.6 it worked only with this:
error_reporting(E_ALL ^ E_STRICT);
Freddy •
But it also works with the public, so yes your right.
It doesn't do anything ...
NigelGreenway •
Cheers for the article. A good and interesting read in regards to using `var` instead of `public` and dropping `public` from class methods. Always good to see other ways of implementing.
My only concern is the explicitness, in the sense that other people touching the code will know what type of access the method has with an instant glance.
Just wanted to know your thoughts on it really?
Evert •
I believe it's one of those things that if you *know* the default visibility is public, it will be super easy to adjust to. The only category of people I'm a bit worried about are the ones that have never seen a 'naked' function and need to find out first what the behavior is.
But that's not unlike someone running into 'yield' for the first time, and that's something I can live with. I think that as soon as you start using it, adding 'public' really quickly seems very verbose. I hate looking at abstract static public function ;)
Fab G •
If you put "public" before a method or property, it tells me that it was your intention to make it public.
From that point we have an agreement, that you won't make backwards-compatibility breaks on your public api. Surely only as long as you don't release a next major version.
If not, I could possibly think it could be an mistake and will dive deeper into your code, to make sure it was really your intention. I don't have the time to think about your implementation details.
Sometimes coding means convention, even if it doesn't make sense.
mirabilos •
The copyright years are actually necessary, *and* you’re **not** supposed to update them all on a new year, but only when something that actually has content over the “threshold of originality” was committed, and only to those files then.
Evert •
Necessary for what reason? And where do you get these rules? I know US copyright law requires this for works from before 1989, but are there others? Copyright rules are not world-wide the same or universal, so please specify what you're talking about.
Phillip Haydon •
Old post, but I'm curious, why would it cause: "It causes a massive ‘Happy new year’ commit"???
The copyright year would never change so there is never a 'happy new year' commit...
Evert •
I wanted the copyright year to reflect the first and last time work was done on it. So it might be 2005-2018