Extending the Joomla Framework’s Github package

In the Part 8, I looked at to adding a logging service provider with the help of a third-party package called Monolog. In this tutorial I’m going extend Joomla’s Github package to integrate some missing (at the time of writing) functionality.

The source code for this tutorial is available in under v1.2 tag on Github.

Creating a home for extended classes.

At the time of writing this article, the Joomla Framework’s Github package does not yet support references and tags. I need these features to allow me to look up a list of tags that have been defined in a git repository and then work out which pull requests happened between tags.

I could add new features in the appropriate place under the /vendor/, but each time I update composer there is a chance those changes will be lost. Therefore, I need to consider housing this new work in a folder under my /src/ folder.

I want to keep the same look and feel as the real Joomla Framework, so I start by creating a /src/Joomla/ folder in my repository just like there is in the Joomla Framework repository. This will anchor the “Joomla” namespace for my extended classes. You should be able to see why I went to the trouble of putting the application’s code in the /src/Tagaliser/ folder. It allows me, now, to clearly separate my application’s code from custom Joomla code.

In order for the auto-loader to find our custom framework classes, I need to add the namespace to the composer.json file.

{
  "minimum-stability" : "beta",
  "require" : {
    "joomla/application" : "1.0-beta2",
    "joomla/di" : "1.0-beta2",
    "joomla/github" : "1.0-beta2",
    "joomla/model" : "1.0-beta2",
    "monolog/monolog" : "1.6.*"
  },
  "autoload" : {
    "psr-0" : {
      "Tagaliser\\" : "src",
      "Joomla\\" : "src"
    }
  }
}

You can see I’ve added "Joomla\\" : "src" to the PSR-0 autoloader list. Remember, when you make any changes to the composer.json file, you need to run composer update again for them to take effect.

Once that is done, the auto-loader will look in Composer’s /vendor/ folder and /src/Joomla for any classes in the Joomla namespace.

Extending the Github package

Now that I have an anchor point for my custom Joomla Framework classes, it is just a matter of following the convention already established in the Github package. You can see I’ve added the following files and folders:

|- src
  \- Joomla
    \- Github
      \- Package
        |- Refs.php
        \- Tags.php

The Github package has been cleverly designed to pick up new features as long as the fully-qualified class name matches a particular pattern. Let’s have a look at how the Refs.php file is structured.

<?php
namespace Joomla\Github\Package;

use Joomla\Github\Package;

class Refs extends Package
{
    // ...
}

If you didn’t know better, you’d swear this came directly out of the Joomla Framework because we are using exactly the same namespace as the Joomla Framework. This is the beauty of the auto-loader because it can look in multiple locations for the same namespace.

The body of the Refs.php and Tags.php files aren’t that important other than to say I’m following the convention already set up for the existing API.

While the code is not quite ready to contribute back yet (it needs documentation, tests, etc), you should be able to see how easy it would be to drop those files in a fork of the Joomla Framework and contribute it back to the project.

Overriding the Joomla Framework

As a side note, it is possible to actually override core classes by copying files from the /vendor/joomla/... folders to /src/Joomla/.... Unfortunately it requires a minor “hack” that you will need to remember to repair each time you update with Composer.

If you have a local copy of Tagaliser set up, have a look in the /vendor/composer/ folder for a file called autoload_namespaces.php. The file is automatically generated by Composer and will look something like this:

<?php

// autoload_namespaces.php generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    'Tagaliser\\' => array($baseDir . '/src'),
    'Psr\\Log\\' => array($vendorDir . '/psr/log'),
    'Monolog' => array($vendorDir . '/monolog/monolog/src'),
    'Joomla\\Utilities' => array($vendorDir . '/joomla/utilities'),
    'Joomla\\Uri' => array($vendorDir . '/joomla/uri'),
    'Joomla\\String' => array($vendorDir . '/joomla/string'),
    'Joomla\\Session' => array($vendorDir . '/joomla/session'),
    'Joomla\\Registry' => array($vendorDir . '/joomla/registry'),
    'Joomla\\Model' => array($vendorDir . '/joomla/model'),
    'Joomla\\Log' => array($vendorDir . '/joomla/log'),
    'Joomla\\Input' => array($vendorDir . '/joomla/input'),
    'Joomla\\Http' => array($vendorDir . '/joomla/http'),
    'Joomla\\Github' => array($vendorDir . '/joomla/github'),
    'Joomla\\Filter' => array($vendorDir . '/joomla/filter'),
    'Joomla\\Filesystem' => array($vendorDir . '/joomla/filesystem'),
    'Joomla\\Date' => array($vendorDir . '/joomla/date'),
    'Joomla\\Database' => array($vendorDir . '/joomla/database'),
    'Joomla\\DI' => array($vendorDir . '/joomla/di'),
    'Joomla\\Client' => array($vendorDir . '/joomla/client'),
    'Joomla\\Application' => array($vendorDir . '/joomla/application'),
    'Joomla\\' => array($baseDir . '/src'),
);

All this file is doing is mapping namespaces to folders. The thing to realise is that the Composer’s auto-loader searches from first to last. This means that our custom Joomla Framework files won’t be found first. To fix this you just have to move the 'Joomla\\' => ... line above all the Joomla packages like so:

return array(
    'Tagaliser\\' => array($baseDir . '/src'),
    'Psr\\Log\\' => array($vendorDir . '/psr/log'),
    'Monolog' => array($vendorDir . '/monolog/monolog/src'),
    'Joomla\\' => array($baseDir . '/src'),
    'Joomla\\Utilities' => array($vendorDir . '/joomla/utilities'),
    // ...

Now the auto-loader will find your files first before it goes and looks in the usually places. But remember, you’ll have to “hack” this file each time you update with Composer. The caveat is that you need to distribute your application with the “vendor” code rather than allowing a user to update it for themselves.

End of Part 9

In the next tutorial I’m going to look at the modal that handles all the “business logic” for the application.

Advanced Test Driven Development for Node - Part 1

Part 1 of my attempt to port Robert C. Martin's talk '8LU:Advanced Concepts in TDD' to Node. Continue reading

Semantic versioning for retail software

Published on December 11, 2014

Better Grunt files (for organised developers)

Published on December 02, 2014