Faking Dual Inheritance Classes in PHP

Object oriented coding in PHP can save a lot of time in your projects, but currently, PHP only allows classes to have a single parent. Unfortunately, many Internet classes are ripe for using dual inheritance — particularly if you want to have one class serve your visitors, while a child class serves your administrators. Faking dual inheritance is not easy, nor does it have all the advantages of true dual inheritance, but I’ve already done most of the work, so read on with confidence that this will be a real time saver!

I’ve split this tutorial into a number of sections, so you can go straight to what you want. All the files mentioned here are available below. If you "only" write scripts in PHP, don’t have a firm grasp on classes, or just don’t see the point of this article, keep reading. Those who already use classes, but don’t know why they would want a dual inheritance class, should probably just skip to "But Why Dual Inheritance?" If on the other hand, you have already found yourself wishing for dual inheritance classes in PHP, just head straight for "How to Fake It."

The Files

The archive below contains universalcontent.php, universalcontentadmin.php, pagecontent.php, pagecontentadminpre.php , pagecontentadmin.php, universalupdater.php, their documentation files, and a few "demo" files to help users figure out the details. You’ll need all these files to complete the tutorial.

Download the Code Archive and get started!

Why OO?

Object oriented programming is a wonderful method to reduce time on projects, and keep your work much more organized. If there’s any task that you need to repeat between projects, if you’ve ever scavenged code from one of your old projects to use on a new one, or if you have ever needed to write more than just "a little" code, then you really should consider using object oriented programming to simplify your work.

Classes are the main component of object oriented programming in PHP (and more than a few other languages). While they may initially seem excessive for your scripts — even as one with a background in C++, I wasted a lot of time by not using classes when I started with PHP — classes will save you a lot of time in the long run.

So, what really is a class? Classes are a collection of variables and the functions that interact with them. They are very easy to define:

class myclass    {     var myclassvariable;     var . . . etc. 

function myfunction ();     function . . . etc.   }

Now that you have a class, you don’t need to keep track of its inner workings. You simply create a new instance each time you want to use it:

myobject = myclass (some_arguments);   myobject->myfunction (); 

One of the great things about classes is these little "packages" of code are so easy to recycle both within a project, and between projects. A little foresight combined with PHP’s extremely flexible variables can save you a tremendous amount of time. In my own programming I use a class called universalcontent. It handles all the basic calls to any MySQL table I might need in a project. While the class is simple, it forms a building block for many of my more complex classes.

Time for Children

While classes themselves are useful for keeping your code clean, and helping you recycle some of it, child classes really make object oriented programming what it is. Consider the previous universalcontent class — by itself I can recycle some code, but if that’s all I accomplish, I’m not really helping myself that much by having a class that’s only capable of simple queries to a site’s database.

Child classes extend a parent so that you can create a new, more specific class. This way, you create "common denominator" parent classes, and then either add new functions and variables, or overload, that is: create a new version of, old functions. The child class automatically has all the functions and variables of the parent. This is called single inheritance.

My universalcontent class now has its child universalcontentadmin — a powerful class combination. Now, not only can I easily access any basic database table for my visitors, but I can also create an administrative interface for that table with almost no coding.

Faking Dual Inheritance Classes in PHP

This kind of parent-child relationship allows me to reuse code, and at the same time makes debugging and future changes much easier. Why are future changes easier? Say that I manage all my database connections through universalcontent functions, and that universalcontentadmin exclusively uses those functions when it accesses the database. If, later, I change from MySQL to some other SQL database, I merely have to make changes to universalcontent. After that, it and all of its children will automatically be upgraded to support the new database.

But Why Dual Inheritance?

So, why would anyone want dual inheritance? The simplest answer is: because you’ve found a situation where you want the abilities of two classes to form the basis of one new class. In the previous sections, I talked about my parent universalcontent and its child universalcontentadmin, which form the basis of my database interactions for visitors and administrators. I also have a class, pagecontent, which I use to access page content that has been stored in a database, and also to drive dynamic menus. Of course, there is another class, called pagecontentadmin. Where should it go in the family tree?

Faking Dual Inheritance Classes in PHP

Ideally, it would be a child of both pagecontent and also universalcontentadmin, as it will surely need access to the pre-existing administration code and, to a lesser extent, it needs access to some of the pagecontent functions, and constants.

So, what options do we have? Unfortunately, there aren’t many. We can copy-and-paste either the universalcontentadmin or pagecontent functions into our new child class, but we were trying to avoid just that very situation when we decided to use object oriented programming. Another option is to simply change the structure of our classes — to make universalcontent and universalcontentadmin one class. This is not a particularly attractive option, as adding universalcontentadmin to universalcontent would more than quadruple the parent class’s size (thereby slowing the server down) and offer no benefits for visitors to the site. Single inheritance is really not ideal here.

How to Fake It

So, how can I make a pagecontentadmin class, which effectively has two parents (pagecontent and universalcontentadmin)? Unfortunately, PHP does not allow for multiple inheritance, nor does it allow for strategically placing "include" statements inside a class bracket — you can use them as parts of functions, but not as part of the class. Either one of those would give us a solution with either two parents, or a pretty close approximation of one.

Instead, I am forced to create a "pre-class" to get all the functions from universalcontentadmin into the new class pagecontentadmin.

Faking Dual Inheritance Classes in PHP

This would be a very bleak place to end our story, as we have enjoyed almost none of the advantages of object oriented programming. If later I improve universalcontentadmin, I will have to manually make changes to the prepagecontentadmin class — a process which promises not only to be tedious, but potentially will introduce bugs as I haphazardly copy and paste to every pre-code I can find.

Happily, though, it does not end there. By creating a new class, universalupdater, we can eliminate most of the disadvantages of this method. This class will search through a directory and all its subdirectories looking for any PHP file with the proper comment tags. Then, it will take the source class (in my examples, universalcontentadmin), modify it, and insert the class into the target files. Universalupdater is designed to work with a single source class per file and a single "target class" per file. While you can have other classes in the same file without a problem, I have designed the script to work only with one "active" class per file.

The steps to use universalupdater are quite simple:

  1. Create the parent functions and organize your files
  2. Prepare the source file
  3. Prepare the intermediate file
  4. Create an instance of universalupdater and call the function "Execute". This step shoule repeated any time you update the source class
  5. Use the newly created intermediate class as a parent of your "dual inheritance" child class

General Preparations

Hopefully, it goes without saying that before you can have a dual inheritance class, you need to have two parent classes already prepared. What may need to be mentioned is that in order for the dual inheritance class to be effective, you will need to make sure the classes are compatible. In my example, pagecontent and universalcontent are sibling classes (i.e. the share the same parent universalcontent). Not only do these two not have any conflicting functions (or functions with the same name which might accidentally get overloaded), but they also share the same basic data structure.

Once you have the files (both your own files and universalupdater), you will need to make sure you have them organized in a way that will allow universalupdater to function efficiently. On my own system, all my projects, and the classes that are shared between various projects, are in subdirectories of "c:htdoc" (the directory that Apache serves). This makes it really easy for the universalupdater to do its work: I just tell it to update anything in its parent directory and subdirectories. Automatically, it will scan all my projects and all my general classes for the files that need to be edited.

Prepare the Source File

Prepping the source file takes very little time at all (merely insert comment tags to indicate where universalupdater should start and end its copying). The relevant parts of the universalcontentadmin class are shown below. The two tags needed for the universalupdater are in blue — the first appears right before I defined the constants that will be used in the universalcontentadmin, and the second occurs right after I finish defining the class. I have added two descriptions in green. Hopefully, it’s self-evident that they don’t actually appear in the class itself.

<?     //Class UniversalContentAdmin   

include_once ((isset($phplocation)? $phplocation.'universalcontent.     php' : 'universalcontent.php'));   

//Replacement Code Starts Here   

if (!defined ('UNIVERSALADMIN_CONSTANTS'))     {       define ('UNIVERSALADMIN_CONSTANTS', 'defined');       define ('UCA_NO_VALUE', '@#784pdjas');       define ('UCA_BLANK_VALUE', '*^@adfds');       define ('UCA_EDIT', 'ucedit');       define ('UCA_DUPLICATE', 'ucdup');       define ('UCA_DELETE', 'ucdelete');       define ('UCA_CONFIRM', 'ucconfirm');       define ('UCA_UPDATE_ITEM', 'ucupdate');       define ('UCA_DISPLAY_NAME_SPLITTER', '||');     }   

class universalcontentadmin extends universalcontent     {       var $tableformat;       var $comments;       var $itemcount;       var $defaultpadding;   

function universalcontentadmin ($paramtablename)       .       .       .       <THE REST OF THE CLASS'S FUNCTIONS>       .       .       .       function ProcessMinorSpecialItem ($variablename, $value, &$warning,        &$reporttext)       {         <LAST FUNCTION IN THE CLASS>       }     }   

//Replacement Code Ends Here     ?>

Prepare the Intermediate File

The code that creates the new intermediate class is almost as simple. You can include this intermediate class either in the same file as your final class, or as a separate file. For this example, I’ve made the pre-class a separate file. It’s easier to demonstrate that way. It’s also easier to edit the final class when there is not a bunch of stuff above it, not to mention that my editor starts to slow down slightly if it has to edit 1000+ line PHP files. As this is code for an administrator, and as such, will not be used often, I don’t mind if the server needs to fetch one additional file.

Here’s how my intermediate file looked before I ran the updater.

<?     //PrePageContentAdmin Class     //This class extends PageContent with the code from UniversalAdmin.       //It is maintained automatically by universalupdater.   

include_once ((isset($phplocation)? $phplocation.'pagecontent.php' :      'pagecontent.php'));   

//parent class= pagecontent     //child class= prepagecontentadmin     //source class= universalcontentadmin     //Replacement Code Starts Here     //Replacement Code Ends Here     ?>

As you can see, it’s simply a single include_once statement (loading its parent, pagecontent) and a number of comments. So what are all the comments? The five last comments are all you need to make the universalupdater class work. Code is copied from the source class (in this case universalcontentadmin) and pasted between the two replacement code tags. When that’s done, universalupdater will make two changes to the class. First, the source file’s class definition will be changed to "class my_child extends my_parent" ("my_child" and "my_parent" are taken from the respective "//child class" and "//source class" tags).

The second change is that, if the source file has a constructor function, it will be duplicated and renamed to match the child class. The "//source class" tag lets universalupdater know which source file should be used with a given file. If it doesn’t match the file with which universalupdater is working, then no replacements will be made. The equal signs in the parent, child, and source classes can optionally have spaces before or after them, so "//child class = prepagecontentadmin", "//child class =prepagecontentadmin", "//child class= prepagecontentadmin", and "//child class=prepagecontentadmin" are all equally valid.

Execute Universalupdater

While I designed universalupdater to allow a user to customize the tags it seeks, as long as you don’t need to change the tag names, it’s quite simple to execute the script. The following is all you need:

<?       error_reporting(E_ALL);      include ('universalupdater.php');      $update = new universalupdate('my_source_file.php',       'my_target_directory');    

$update->Execute();      ?>

You don’t actually need error_reporting (E_ALL), but I like to have all warnings displayed so that if something does go wrong, I know about it. The function Execute() does give a simple progress report, but error reporting is still useful.

When the function Execute() is called, universalupdater will load the source file into memory, and then scan the target directory for any PHP files that contain the proper tags. If your source file is not in the same directory as the script calling universalupdater, make sure you specify the directory in the file path name as well.

If you are using universalupdater with more than one source file, or you need it to sort through a number of directories, simply create new instances of the class with the additional source files or target directories.

Once the universalupdater has been run, the intermediate class file, which was originally just comment tags, now appears as:

include_once ((isset($phplocation)? $phplocation.'pagecontent.php' :       'pagecontent.php'));    

//parent class= pagecontent      //child class= prepagecontentadmin      //source class = universalcontentadmin      //Replacement Code Starts Here    

if (!defined ('UNIVERSALADMIN_CONSTANTS'))      {        define ('UNIVERSALADMIN_CONSTANTS', 'defined');        define ('UCA_NO_VALUE', '@#784pdjas');        define ('UCA_BLANK_VALUE', '*^@adfds');        define ('UCA_EDIT', 'ucedit');        define ('UCA_DUPLICATE', 'ucdup');        define ('UCA_DELETE', 'ucdelete');        define ('UCA_CONFIRM', 'ucconfirm');        define ('UCA_UPDATE_ITEM', 'ucupdate');        define ('UCA_DISPLAY_NAME_SPLITTER', '||');      }    

class prepagecontentadmin extends pagecontent      {        var $tableformat;        var $comments;        var $itemcount;        var $defaultpadding;    

function prepagecontentadmin ($paramtablename)        .        .        .        <ETC>        .        .        .      }      //Replacement Code Ends Here      ?>

As you can see, the class definition has been altered from "class universalcontentadmin extends universalcontent" to "class prepagecontentadmin extends pagecontent". Likewise, the constructor function has been renamed.

Use the New Intermediate Class

Hopefully, this final step will be the easiest. In the example, my final class is pagecontentadmin. Here is the beginning of the new class:

include_once ((isset($phplocation)? $phplocation.'pagecontentadminpre.php'       : 'pagecontentadminpre.php'));    

define ('DEFAULTMODELPAGE', 'blank');      define ('PCA_SIMPLE_STRUCTURE', 's');      define ('PCA_SUBDIRECTORY_STRUCTURE', 'd');    

class pagecontentadmin extends prepagecontentadmin      {        var $modelpage;        var $directorystructure;     

function pagecontentadmin ($extrafields = array (''),         $paramtablename = 'pages', $modelpage = DEFAULTMODELPAGE,         $recordstructure = PCA_SIMPLE_STRUCTURE)        .        .        .        <ETC>        .         .        .      }

You may notice while the class name is prepagecontentadmin, I saved the file as pagecontentadminpre.php. While it is a little inconsistent, I did this so the class name would make sense in English, while the file name would allow the file to appear alphabetically with pagecontent.php and pagecontentadmin.php.

Now, pagecontentadmin has all the functions and constants of both pagecontent and universalcontentadmin. When using universalupdater, you will need to be careful about the class constructor. Remember that universalupdater will use the class constructor of the source file automatically. You may need to specify a new constructor in the final class.

Winding Up

Just as a quick review, to use universalupdater, go through the following steps:

  1. Take the files and create the two parent classes. In my example, the parent classes are sibling classes. While this is not necessary, remember that the parent classes need to be compatible with each other.
  2. Prepare the source file by adding the start and ending comment tags to it (by default they are "//Replacement Code Starts Here" and "//Replacement Code Ends Here").
  3. Prepare the intermediate file by adding five comment tags to it (by default the necessary tags are: "//parent class = my_parent", "//child class = my_child", "//source class = my_source", "//Replacement Code Starts Here" and "//Replacement Code Ends Here").
  4. Run a script that creates an instance of universalupdater, and calls the function Execute (my_source_file, my_target_directory).
  5. Use the newly-created intermediate class as a parent for your final dual inheritance class.
  6. Any time you need to update the source class, just run universalupdater again, and the changes will automatically be made to all the appropriate files.

Does this perfectly duplicate dual inheritance? No. While it is much more convenient that doing the copying and pasting yourself, you still have to run the script to update all the intermediate classes. Also, it’s not as efficient for the server as true dual inheritance would be. Those disadvantages aside, it does truly allow you to maintain many dual inheritance children without fear of them becoming out of date.


Category: programming Time: 2002-10-15 Views: 3

Related post

iOS development

Android development

Python development

JAVA development

Development language

PHP development

Ruby development


Front-end development


development tools

Open Platform

Javascript development

.NET development

cloud computing


Copyright (C) avrocks.com, All Rights Reserved.

processed in 0.292 (s). 12 q(s)