Porto

Class Hierachies And Overriding

Class Hierachies And Overriding

by Kevin Waterson
  1. Abstract
  2. Abstract Classes
  3. Further Abstraction
  4. Overriding
  5. Flexibility
  6. Overriding Built-In Classes
  7. Summary

Abstract

The PHP Object Oriented method of programming brings many exciting possiblities to application code. Many of the theories surrounding PHP Object Oriented code comes from some simple concepts. To the new comer, some of these concepts seem a little abstract, and with good reason. Abstraction is a key concept on Object Oriented code, but to the un-initiated, may seem rather vague.

Overriding should not be confused with overloading. Overloading is the process of having 2 (or more) class methods in the same class but with differing signatures

Overriding is the process of having 2 (or more) methods in a parent class and a method in a child class with the same signature. Overriding allows the user to define similar processes for different object types. eg: creating an array from a csv file or from a database result set.

This tutorial attempts to bring to light, the concept of abstration, class hierachies, and polymorphism. But rather than boggle the mind with pages of theory, lets look at how it works.

Overriding should not be

Abstract Classes

An abstract class is a class that other classes will inherit from. An class defined as abstract, may not be instantiated on its own, can can only be inherited other child classes.


<?php
    
abstract class Lister
    
{
        protected 
$path;

        abstract function 
listing();

        abstract function 
logAccess$message );

    } 
/*** end of class ***/

    
$object = new Lister;
?>

The abstract class above, contains a definition, or a blueprint for how the structure of other classes should be. In this example, all classes that extend the Lister class must contain the property $path and the two methods Listing() and logAccess(). The logAccess method, must also have an arguement named $message.

Any attempt to instantiate this class on its own, as in the code above, will result in a fatal error.

Fatal error: Cannot instantiate abstract class Lister in /www/test.php on line 15

An abstract class benifits over an interface as member variables are not permitted in interfaces.

Further Abstraction

The class above could now be used as a base for other classes to extend from. Providing the class was structured as provided, all would be well. But wait there's more. Not only can the method definitions be provided, but also a default behaviour, that is, flesh out the class methods to do something.


<?php

    
abstract class Lister
    
{
        abstract function 
listing();

        abstract function 
logAccess$message );

    } 
/*** end of class ***/


    
abstract class baseLister extends Lister
    
{
        
/*** a few variables ***/
        
public $path$message;

        
/*** define default behaviors ***/
        
public function listing()
        {
            echo 
'Default Listing Behaviour';
        }

        
/*** define default log message ***/
        
public function logAccess$message )
        {
            echo 
'default log message';
        }
    } 
/*** end of class ***/

?>

Within the second abstract class, the class melthods are fleshed out a little to provide some simple default behaviours, in this case, simply to echo some text to say what those behaviours are. It is these class methods that will be overridden by classes which inherit from this class.

The process of having child class methods named the same as the parent class methods is know as Overriding. When a parent class method is overridden, the child class is basically saying "lets do it my way instead". The child class in this instance, is not declared abstract, and can be instantiated on its own.

If the default methods, were the behaviour that an application required, a simple class is all that is needed, not even the method definitions need be implemented.


<?php
    
class fileLister extends baseLister
    
{
        
/*** no methods are required ***/
    
}

    try
    {
        
$obj = new fileLister('./');
        
$obj->listing();
        
$obj->logAccess('File Listing');
    }
    catch(
Exception $e)
    {
        echo 
$e->getMessage();
    }

<?php
    
abstract class Lister
    
{
        abstract function 
listing();

        abstract function 
logAccess$message );

    } 
/*** end of class ***/


    
abstract class baseLister extends Lister
    
{
        
/*** a few variables ***/
        
public $path$message;

        
/*** define default behaviors ***/
        
public function listing()
        {
            echo 
'list all directories';
        }

        
/*** define default log message ***/
        
public function logAccess$message )
        {
            echo 
'default log message';
        }
    } 
/*** end of class ***/


    
class fileLister extends baseLister
    
{
        
/*** a few variables ***/
        
public $path$message;

        public function 
__construct$path )
        {
            
/*** set the directory name ***/
            
$this->path $path;
        }

        
/*** override  the parent listing method with our own ***/
        
public function listing()
        {
            
$it = new directoryIterator$this->path );
            while( 
$it->valid() )
            {
                if( 
$it->isFile() )
                {
                    echo 
$it->current().'<br />';
                }
                
$it->next();
            }
        }

        
/*** override  the parent logAccess Message with our own ***/
        
public function logAccess$message )
        {
            echo 
'Logging File Listing<br />';
        }
    }

    try
    {
        
$obj = new fileLister('./');
        
$obj->listing();
        
$obj->logAccess('File Listing');

    }
        catch(
Exception $e)
    {
echo 
$e->getMessage();
}

In the above example, the class methods of the fileLister class have been used rather than the parent default methods. This process is know as polymorphism. Should only one of the methods be implemented, then the other would default to the parent default behaviour.

Flexibility

Why is this a good idea? Because now mutliple classes can be created to list different things, In the above class, the fileLister listed all the files in the current directory. But this is not what is always required. Supposing the application required a listing of all directories also? It is now simply a matter of a second dirListing class to make this possible.

Here two classes are defined to extend the base class baseLister. The fileLister class and the dirLister class vary only slightly, however, give a good idea of how mulitple classes may extend from a parent.


<?php
    
abstract class Lister 
    
{
        abstract function 
listing();

        abstract function 
logAccess$message );
    } 
/*** end of class ***/


    
abstract class baseLister extends Lister
    
{
        
/*** a few variables ***/
        
public $path$message;

        
/*** define default behaviors ***/
        
public function listing()
        {
            echo 
'list all directories';
        }
    
        
/*** define default log message ***/
        
public function logAccess$message )
        {
            echo 
'default log message';
        }
    } 
/*** end of class ***/


    
class fileLister extends baseLister
    
{
        
/*** a few variables ***/
        
public $path$message;

        public function 
__construct$path )
        {
            
/*** set the directory name ***/
            
$this->path $path;
        }

        public function 
listing()
        {
            
$it = new directoryIterator$this->path );
            while( 
$it->valid() )
            {
                if( 
$it->isFile() )
                {
                    echo 
$it->current().'<br />';
                }
                
$it->next();
            }
        }

        public function 
logAccess$message )
        {
            echo 
'Logging File Listing<br />';
        }
    }    

    class 
dirLister extends baseLister
    
{
        
/*** a few variables ***/
        
public $path$message;
        
        public function 
__construct$path )
        {
            
/*** set the directory name ***/
            
$this->path $path;
        }

        public function 
listing()
        {
            
$it = new directoryIterator$this->path );
            while( 
$it->valid() )
            {
                if( 
$it->isDir() )
                {
                    echo 
$it->current().'<br />';
                }
                
$it->next();
            }
        }

        public function 
logAccess$message )
        {
            echo 
'Logging File Listing';
        }
    }

    try
    {
        
$obj = new fileLister('./');
        
$obj->listing();
        
$obj->logAccess('File Listing');

        
$dir = new dirLister('./');
        
$dir->listing();
        
$dir->logAccess('Directory Listing'); 
    }
    catch(
Exception $e)
    {
        echo 
$e->getMessage();
    }

?>

Overriding Built-In Classes

The core PHP distribution, and many extensions, now contain their own classes, SPL and PDO, and the Exception classes are good examples of this. Many of these classes themselves can be extend and class methods overridden. Here the directoryIterator valid() method is overridden to produce a listing of directories only.


<?php
    
class DirectoryReader extends DirectoryIterator
    
{
        public function 
__construct$dir )
        {
            
parent::__construct$dir );
        }

        
/*** return the current filename ***/
        
function current()
        {
            return 
parent::getFileName();
        }

        
/*** members are only valid if they are a directory ***/
        
function valid()
        {
            if(
parent::valid())
            {
                if ( !
parent::isDir() )
                {
                    
parent::next();
                    return 
$this->valid();
                }
                return 
TRUE;
            }
            return 
FALSE;
        }

    }

    try
    {
        
$it = new directoryReader('./');
        while(
$it->valid())
        {
            echo 
$it->current();
            
$it->next();
        }
    }
    catch(
Exception $e)
    {
        echo 
$e->getMessage();
    }
?>

Summary

The PHP Object Oriented approach to programming offers significant power and flexibility in code. The ability to overridden class methods makes class hierachies easy and manageable.