For the past four(ish) years, I've been making changes to WebPA, the open source peer assessment tool, to ensure it can be used with the latest versions of PHP.

Much of this work has been done behind the scenes. If you download the latest release of WebPA, version 3.1.0, it will not look any different from version 2.0.0.11. However, under the hood, WebPA's internals are being updated to ensure its longevity as a peer assessment tool.

To be more open about these changes, I thought I would start a semi regular blog to chart the progress of these developments, so without further ado, here is the first of hopefully many updates detailing what's changed in this latest version.

Semantic Versioning

Since version 3.0.0 of WebPA, the project has been following semantic versioning. What does this mean in practice?

The version number for WebPA should now have meaning. It is split into three different parts. Version 3.1.0 has three numbers known as the major, minor, and patch.

  • 3 = major
  • 1 = minor
  • 0 = patch

The patch number is increased whenever there are bug fixes in a release and no other changes.

The minor number is increase whenever features are added or something is changed in the system but the system works largely the same as before.

The major number is increased whenever we have a significant change to the system, such as a UI change, workflow change, or database schema change.

You can see what has changed in WebPA by referring to our changelog, which lists the date of new releases and provides a summary of the changes since the last release.

The previous version of WebPA was 3.0.7. The new version number of 3.1.0 means we've not had any significant function changes but we have had some new features added or changes to existing features. We might also have had some bug fixes in this latest release.

Version 3.1.0 Changes

Support for PHP 7.4

In this release, we've updated the codebase and tested it to ensure it is compatible with PHP 7.4, the latest stable release of PHP.

The application should work with earlier versions of PHP but we cannot guarantee this. At the moment, WebPA does not have automated tests so all testing is manual. This also means it is very time consuming to conduct. This is something I aim to change in future releases but for now, we will only officially support the latest stable versions of PHP.

At the time of writing, this is PHP version 7.2, 7.3, and 7.4.

Removed most include and require statements

Most of the WebPA codebase was written when PHP 4 and 5 were still in active development. In PHP 4, the main way to stitch applications together was using require and include statements.

<?php

function login($username, $password) {
	if ($username === 'andy' && $password === 'password') {
    	return true;
    }
    
    return false;
}

login.php

The login.php file above contains a simple login function which returns true if the username provided is andy and the password provided is password.

In earlier version of PHP, if we wanted to use this function in another file, instead of copying and pasting it into that file, we could use a require or include statement to include the login.php files' contents in a different PHP file:

<?php

include 'login.php';

$isLoggedIn = login('andy', 'password');

if ($isLoggedIn === true) {
	echo 'Logged in';
} else {
    echo 'Not logged in';
}
home.php

In the example above, the home.php file includes the login.php file using an include statement. It now is able to call the login function that is defined in a different file.

Modern PHP applications rarely use include and require statements. This is because most applications use what is known as autoloading.

Autoloading Explained

Autoloading was introduced in PHP version 5 to reduce the need for long lists of include statements at the start of a file.  It gives your PHP application instructions to look for a file in a specific location if a class or function is referenced but does not have an associated require or include statement.

<?php
spl_autoload_register(function ($class_name) {
    include $class_name . '.php';
});

$obj  = new MyClass1();
$obj2 = new MyClass2(); 
Example of Autoloading

With the release of version 3.1.0, we've added autoloading and namespaces to the backend of WebPA to remove the need for require and include statements.

If you are wondering what namespaces are, hang tight, I will get to those in a second...

So, previously in WebPA we might have a file with something like this:

<?php

require_once(DOC__ROOT.'includes/functions/lib_common.php');
require_once(DOC__ROOT.'includes/classes/class_dao.php');
require_once(DOC__ROOT.'includes/classes/class_user.php');
require_once(DOC__ROOT.'includes/classes/class_module.php');
require_once(DOC__ROOT.'includes/classes/class_engcis.php');
require_once(DOC__ROOT.'includes/classes/class_ui.php');
Old require statements in inc_global.php

In version 3.1.0 we instead have something like this:

<?php

// Add composer's autoloader
require __DIR__ . '/../../vendor/autoload.php';

use WebPA\includes\classes\EngCIS;
use WebPA\includes\classes\DAO;
use WebPA\includes\classes\UI;
use WebPA\includes\classes\User;
use WebPA\includes\functions\Common;
New Autoloading for inc_global.php

Although it doesn't look like much has changed, it really has. We are now using namespaces and use imports.

If you aren't familiar with namespaces, they are a way of encapsulating items. Rather than coming up with my own explanation, I'm going to shamelessly rip the definition from the PHP documentation:

What are namespaces? In the broadest definition namespaces are a way of encapsulating   items.  This can be seen as an abstract concept in many places. For example, in any   operating system directories serve to group related files, and act as a namespace for   the files within them. As a concrete example, the file foo.txt can   exist in both directory /home/greg and in /home/other,   but two copies of foo.txt cannot co-exist in the same directory. In   addition, to access the foo.txt file outside of the   /home/greg directory, we must prepend the directory name to the file   name using the directory separator to get /home/greg/foo.txt. This   same principle extends to namespaces in the programming world.

Namespaces solve two distinct problems:

  • Name collisions between code you create, and      internal PHP classes/functions/constants or third-party classes/functions/constants.    
  • Ability to alias (or shorten) Extra_Long_Names designed to alleviate the first problem,      improving readability of source code.    

How do we alias items such as classes or functions? We use use statements e.g. use WebPA\includes\classes\EngCis as Eng;

The namespaces and autoloading in WebPA follow the PSR-4 standard which is the de-facto autloading standard for modern PHP applications.

By making these changes, we can use external code more easily. Composer packages will be easier to import into WebPA and we should eliminate the need for long lists of included files.

Almost all...

One final thing to say about this change is that we haven't removed all include and require statements. The inc_global.php file serves as a config file for WebPA but is mixed in with functions and PHP runtime definitions. It is included in every file in WebPA. For now, we've kept it in place but plan to remove it in a later version of WebPA when it is easier to do so.

For this to happen we need a front controller for WebPA. This is a file that all web requests pass through. At the moment, this is not easily achieveable but it is on my todo list for a later version.

Removed All Global Variables

PHP allows you to use global variables. These used to be common in old procedural PHP scripts but their use is now frowned upon. Lets look at an example:

<?php

function getUsers() {
	global $db;
    
    return $db->query('SELECT * FROM users');
}
Global Variable Example

The code above is getting the variable $db from the global scope. The problem is we can't easily tell which $db variable is being used, where it was defined, what other sections of the code could modify it etc.

It makes maintenance and bug fixing in WebPA a lot harder as it is hard for a developer to track how the variable is used and changed.

WebPA had a lot of global variables which have now been removed in version 3.1.0. This should make maintenance of the application much easier. We are using dependency injection instead of global variables:

<?php

function getUsers(Database $db) {
	return $db->query('SELECT * FROM users');
}

We  explicitly pass the $db instance to the function, making it much easier to know where the instance came from and how it is used, making maintenance much easier.

Change the Default Authenticator to Database

In previous versions of WebPA, the default authenticator was listed as SAML. Unfortunately, the SAML authenticator doesn't work properly in WebPA at present.

Because of this, I've updated the application to list the database authenticator as the default authentication method for users logging into WebPA.

I will aim to fix the SAML authentication mechanism in a future version of WebPA.

Future Plans

I will continue to modify WebPA in the backend so that it can be used with the latest versions of PHP. Once the backend is modernised, I will start working on a modern user interface and new features.

This will hopefully be the first of many blog post detailing these modifications.