I maintain a common code repository, or "framework", that is used in a large number of unrelated projects. In this repository is some number of "base classes", all of which each constituent project must access/use. So far, so good.

In each project that uses the common code repository, I need the ability to extend any or all of the common classes. Further, I need for each class to be accessible by the same name, regardless of whether or not the class has been extended. In other words, I don't want to have to reference a class by "ApplicationBase" when the class has not been extended, and by "Application" when it has. I want to be able to use "Application" in either case.

It bears mention that some of the common/base classes contain static methods only, some contain non-static methods only, and some contain a mix of both static and non-static methods.

Thus far, the only way I've been able to meet all of the above objectives is to create "skeleton" extender class, for each of the common classes, within each project. Using the skeleton class is necessary so that the class may be accessed with the most-recently-declared class name, regardless of whether or not any method is actually overridden in the extender class.

Here is the basic structure:

[FONT=Courier New]
+ base-classes
-- application-base.class.php
-- utility-base.class.php
-- db-base.class.php

  • project-specific classes
    -- application.class.php
    -- utility.class.php
    -- db.class.php
    [/FONT]

In a project in which there is no need to extend the "Db" class, for example, the "db.class.php" file will contain the following:

<?php

class Db extends DbBase
{
	//Override base methods with optional extender methods here.
}

This works very well, with one exception: it's difficult to maintain. Any time I need to add a base class to the common/framework repository, I need to add the corresponding skeleton class to every single project. Less than ideal.

I can live with the fact that this design pattern limits me to extending each class only once.

Does anyone have a better idea or approach?

I've looked into Traits in PHP 5.4, but it seems that they are intended to make the same method available to different classes (mix-ins), whereas this is the opposite scenario: I want to invoke methods from different classes using the same class name.

Thanks for any help!

    Just a quick brain-storming thought off the top of my head: could namespaces take care of this for you?

      Or always extend the base class (forcing it by declaring the base class as abstract)? There would be a default implementation class that just inherits everything from the abstract base.

        Thanks for the suggestions, NogDog and Weedpacket.

        I read through the Namespaces documentation on php.net, but it's a weighty subject and, admittedly, I have not digested it fully. Although, I think I see where you're going with that suggestion, NogDog. It seems to me that one particular aspect of namespaces will be a deal-breaker here: namespace definitions cannot be block-scoped. Even if they could, the right implementation would probably require great care and quite a bit of work, given the existing code-base's considerable scope.

        So, I did some additional digging and was overjoyed to find the little-known class_alias() function (available in PHP >= 5.3). With this function, it couldn't be simpler or more elegant:

        //db-base.class.php
        
        class DbBase
        {
        
        function query()
        {
        	echo 'Here is a generic querying mechanism';
        }
        
        }
        
        //db.class.php
        
        class Db extends DbBase
        {
        
        function query()
        {
        	echo 'Here is a more specialized querying mechanism';
        }
        
        }
        
        //This file will ALWAYS exist.
        require('db-base.class.php');
        
        //This file MAY exist.
        if (file_exists('db.class.php')) {
        	require('db.class.php');
        }
        
        if (!class_exists('Db')) { 
        	class_alias('Db', 'DbBase');
        }
        
        //Outputs string "Here is a more specialized querying mechanism"
        Db::query();
        

        Pretty slick, right? Eh? Eeeeeh? 😃

          cbj4074 wrote:

          I've looked into Traits in PHP 5.4, but it seems that they are intended to make the same method available to different classes (mix-ins), whereas this is the opposite scenario: I want to invoke methods from different classes using the same class name.

          Why not code to an interface, perhaps by literally and liberally using interface constructs?

            Write a Reply...