I started building my own website in 2008 when I was still in secondary school using PHP and (X)HTML. At that time, the PHP version was only 5.2, without modern features like namespaces, late static bindings, closures, type safety, etc. There was also no PSR, no composer, etc. at that time. Procedural code with mixed application logic and presentation was widespread, there was no separation of concern, etc.
The earliest trace of my website captured by the Internet Archive was in 2009, at that time, the primary purpose of my website is to show off my bus photos, searchable by key words without using image sharing websites with great restrictions like size and bandwidth at that time.
I added functionality into my website afterwards, including fleet list, about me, bilingual support, etc. However, at time, what I did was only using a text editor to directly edit the PHP files on the live server without any backups and version control (an automatic backup system on my web server was only added around 2014). Although new PHP versions came out, I didn’t update the existing code much except adding new functionalities.
By 2012, my website contained bus photos, bus fleet information, light rail service information and also this blog. Here is two screenshots of my website at that time, retrieved from the Internet Archive. The copyright footer and the light rail photo search form was already existed then.
As shown in the above screenshots, my website didn’t have much styling, like a legacy Web 1.0 site in the 1990s. However, the HTML and CSS was complete valid without quirks which I added validation links at the bottom of the website. Despite this, the back-end PHP code was in a legacy manner at that time, the language links at the top and the copyright at the bottom was just include
d by every page.
In the few years after, I added various features into my website. For example, I wrote an autoloader, some exception classes, detailed error pages and threw exceptions when unexpected thing occurs in my website. I also experimented with various methods to separate presentation from application logic. My early attempt was to move the presentation into separate XHTML files, and use the built-in PHP XML DOM functions, like finding elements by DOMDocument::getElementById()
in the XML and insert the dynamic stuff inside, but this was very cumbersome and didn’t make full use of PHP as a template language.
In 2016, I rewrote the front end of my personal website completely with a new design using HTML 5 and modern CSS features compiled from SASS, and it looked much more beautiful. The following is a screenshot of my website in 2017, which was identical to my website now in 2018 except some contents.
However, the back-end at that time was still mostly legacy with code left from years before. Although the presentation was confined to be inside a single function (written as a closure stored in $output_main
), it still did business logic, used superglobals like $_GET
and $_SERVER
, and even queried the database directly! The only major difference was that, the shell was included in every page which outputs the shell, giving the website a consistent look, and called the page-specific $output_main()
for the main content. I was still editing the PHP file on the server by a text editor without proper deployment and versioning methodology, but it was slightly better than before because there was automatic backup on my web server, which I could restore when something went wrong.
In 2018, after 10 years of launching my website, PHP as a web application programming language improved a lot, including the following key features:
- Anonymous functions (PHP 5.3): Functional programming in PHP!
- Late static binding (PHP 5.3): Static polymorphism possible!
- Namespace support (PHP 5.3): No further worries about name clashes between module!
- Constant expressions and array constants (PHP 5.6)
- Strict type checking and primitive type declaration (PHP 7.0): Finally a type-safe language!
- Abstract syntax tree (PHP 7.0): No more quirks in the syntax!
- Nullable type (PHP 7.1): NULL handling becomes easy! Fixes the billion dollar mistake!
Closure::fromCallable()
: class methods passable as callable outside!
Moreover, there are also a lot of modern frameworks like Symfony and Laravel with proper MVC, however I think that using these full-featured frameworks is a overkill for my website, and I don’t like some of the design decisions in the framework (especially those involving dynamic and magic which make IDE static analysis difficult or outright impossible).
At this time, the PSR specifications come handy. They are designed for interoperability between PHP libraries. The key specifications include PSR-4 for easy installation of modules, PSR-7 and PSR-17 for implementation independent request and response handling, and most importantly, PSR-15 for request handler and middleware. I actually implemented a micro-framework containing only minimal functionality (a PSR-15 application shell, error and exception handlers and a few view classes) which can serve as a starting point for a modern web application in MVC.
I then rewrote the back-end of my whole website on my framework with clear separation between models, views and controllers, version controlled in git, application code organised in classes outside the document root, developed and tested locally before deploying to my web server using SFTP. The following screenshot shows my website source code as a project in my IDE, clearly organised into models, views and controllers.
The work of modernising a website cost me about 15 hours, but the down time was minimal because nearly everything was done in my workstation, only at the end of the process I pushed the “upload to live” button in my IDE, and only very few bugs (mainly related to path handling) had to be fixed afterwards.
To conclude, my website has evolved for 3 generation:
1st generation (from 2010): HTML 4.01 / XHTML 1.0 frontend and only simple CSS served from legacy PHP
2nd generation (from 2016): Beautiful modern responsive design in HTML 5 and CSS compiled from SASS served from legacy PHP
3rd generation (from 2018): Beautiful modern responsive design in HTML 5 and CSS compiled from SASS served from a PHP application using MVC framework
Most of the technical debt of my website has been repaid, and the main remaining thing to do is to decouple my models from my database, because in the rewriting process I just moved the database queries into the model as named constructors for temporary use since I am planning to rewrite my database as well.