Login

Defining an Abstract Class with Restrictive Constructors in PHP 5

In this second part of a four-part series, I rebuild the example application developed in the previous tutorial. As you’ll recall, it used a protected constructor to prevent the instantiation of the base array iterator class. In this case, a better result will be achieved by declaring the iterator abstract; still, the example demonstrates a simple utilization of a restrictive constructor in a concrete situation.

Unquestionably, the introduction of a more robust object model in PHP 5 has provided developers with additional features that allow them to build efficient and thorough object-oriented applications without the need to appeal to the ugly programming hacks used frequently in the bad old days of PHP 4.

As you may know, among those features there’s one in particular, called member visibility, which permits you to explicitly declare methods and properties of classes as public, protected and private respectively. Being used to PHP 5 by now, specifying the level of accessibility (or restriction) that certain members of a class will have in a specific context should be a familiar process that isn’t especially noteworthy.

However, there’s a case where member visibility can be particularly appealing. This happens when you’re working with restrictive constructors, or in other words, when using constructors that have been declared either protected or private.

Indeed, there are a number of situations where restrictive constructors can be of great help, even though the topic seems to be rather irrelevant at first glance. But it is not, trust me. The correct implementation of common design patterns, or even the construction of purely static classes, often require working with protected and private constructors. This implies that the subject deserves a close look.

In keeping with this requirement, in the introductory part of this series I developed a basic array iterator class that involved working with a protected constructor, which was used to prevent the direct instantiation of the iterator in question.

This approach is frankly pretty pointless, as this issue can be addressed simply by declaring the aforementioned class abstract. Even so, this example came in handy for offering a friendly introduction to using protected constructors in a concrete case. However, in the next few lines I’m going to rebuild the example developed in the preceding tutorial, this time using an abstract iterator class.

Now, let’s leave the preliminaries behind us and continue learning more about using restrictive constructors in PHP 5. Let’s jump in!

{mospagebreak title=Review: using a protected constructor within an array iterator class}

As I expressed in the introduction, I left off of the previous article of the series showing an example where a protected constructor was used to prevent the direct instantiation of an array iterator class, as this was originally conceived to be extended by other subclasses.

While this approach certainly isn’t the best one to take when shielding a class from its eventual instantiation, as PHP 5 allows you to define abstract classes easily, it’s good for illustrating the usage of a protected constructor in a particular case.

Below I reintroduced the full source code of this specific example, so you can quickly recall how it was initially developed. First, here’s the definition of the base array iterator class that declares its constructor protected. Take a look at it:

class DataIterator implements Iterator, Countable

{

protected $_pointer = 0;

protected $_data = array();

protected function __construct(array $data)

{

if (empty($data))

{

throw new Exception(‘Input data must be a non-empty array.’);

}

$this->_data = $data;

}

// implement ‘count()’ method required by Countable interface

public function count()

{

return count($this->_data);

}

// implement ‘rewind()’ method required by Iterator interface

public function rewind()

{

$this->_pointer = 0;

}

// implement ‘current()’ method required by Iterator interface

public function current()

{

if (!$this->valid())

{

return FALSE;

}

return $this->_data[$this->_pointer];

}

// implement ‘valid()’ method required by Iterator interface

public function valid()

{

return $this->_pointer < $this->count();

}

// implement ‘next()’ method required by Iterator interface

public function next()

{

++$this->_pointer;

}

// implement ‘key()’ method required by Iterator interface

public function key()

{

return $this->_pointer;

}

}

Definitely, the inner workings of the above “DataIterator” class are quite easy to follow. Apart from declaring its constructor protected, the class implements a few straightforward methods that permit you to go backward and forward across the elements of an inputted array. That’s about it.

Since this array iterator is actually very generic, it should be extended by one or multiple concrete classes, which is done through the following file iterator class:

class FileIterator extends DataIterator

{

private $_file = ‘data.txt';

// override parent constructor

public function __construct($file = ”)

{

if ($file !== ”)

{

if (!file_exists($file))

{

throw new Exception(‘Target file must be an existing file.’);

}

$this->_file = $file;

}

$data = file($this->_file);

parent::__construct($data);

}

}

Simple to code and read, isn’t it? As seen before, the “FileIterator” class is nothing but a simple subclass of the base array iterator that overrides its constructor to give itself the ability to traverse the contents of a specified text file.

Also, if you still have some doubts regarding the way that this last iterator does its thing, below I coded a script that shows how to use the class for walking through the lines of a sample text file. The corresponding code sample is as follows:

// use FileIterator class

try

{

// create instance of FileIterator

$fit = new FileIterator();

// reset iterator pointer

$fit->rewind();

// display current file line

echo $fit->current();

// move iterator pointer forward

$fit->next();

// display current file line

echo $fit->current();

// move iterator pointer forward

$fit->next();

// display current file line

echo $fit->current();

// reset iterator pointer

$fit->rewind();

// display current file line

echo $fit->current();

}

// catch exceptions

catch (Exception $e)

{

echo $e->getMessage();

exit();

}

There you have it. Thanks to the implementation of a simple protected constructor, it was possible to build a hierarchy of iterator classes that together allow you to traverse different data structures in a painless way.

Even though this example isn’t very useful, it should be analyzed as what it really is: an example, and nothing else. Using a protected constructor to prevent the instantiation of a class does work decently, but this approach can be enhanced by declaring the class abstract. That’s much simpler and faster. Thus, in the lines to come I’m going to refactor the previous “DataIterator,” which this time will be defined as an abstract class.

To learn the full details of how this refactoring process will be done, click on the link below and read the following section.

{mospagebreak title=Declaring the DataIterator class abstract}

As you may have guessed, turning the previous “DataIterator” class into an abstract class is only a matter of preceding its name with the keyword “abstract.” So, the simplest way to prevent this base class from being instantiated is by redefining it in the following way:

abstract class DataIterator implements Iterator, Countable

{

protected $_pointer = 0;

protected $_data = array();

public function __construct(array $data)

{

if (empty($data))

{

throw new Exception(‘Input data must be a non-empty array.’);

}

$this->_data = $data;

}

// implement ‘count()’ method required by Countable interface

public function count()

{

return count($this->_data);

}

// implement ‘rewind()’ method required by Iterator interface

public function rewind()

{

$this->_pointer = 0;

}

// implement ‘current()’ method required by Iterator interface

public function current()

{

if (!$this->valid())

{

return FALSE;

}

return $this->_data[$this->_pointer];

}

// implement ‘valid()’ method required by Iterator interface

public function valid()

{

return $this->_pointer < $this->count();

}

// implement ‘next()’ method required by Iterator interface

public function next()

{

++$this->_pointer;

}

// implement ‘key()’ method required by Iterator interface

public function key()

{

return $this->_pointer;

}

}

At this point, since the array iterator class has been declared abstract, there’s no point in defining its constructor protected, at least in the context of this particular example. However, if I still want to create a class capable of traversing text files, its definition would remain exactly the same.

The following code sample demonstrates this in a nutshell:

class FileIterator extends DataIterator

{

private $_file = ‘data.txt';

// override parent constructor

public function __construct($file = ”)

{

if ($file !== ”)

{

if (!file_exists($file))

{

throw new Exception(‘Target file must be an existing file.’);

}

$this->_file = $file;

}

$data = file($this->_file);

parent::__construct($data);

}

}

As you can see, the source code of the above “FileIterator” class remains unchanged, since that the class accesses the parent’s constructor with no restrictions, without worrying too much if the base array iterator is declared abstract or not. Indeed, this subtle modification regarding the way that the base “DataIterator” class has been defined allows you to easily get rid of a protected constructor.

However, there are certain situations where a restrictive constructor might be used for purposes other than preventing the instantiation of its originating class. But I’m getting ahead of myself, since that topic will be covered in depth in a subsequent article of this series.

So, returning to the previous file iterator, it’s time to give it a try and find out if it works the same, now that its parent has been turned into an abstract class. To get the answer to this mystery, you’ll have to go ahead and read the following section.

{mospagebreak title=Traversing the contents of a text file}

In the preceding segment, you just saw that the definition of the file iterator class derived from the base array iterator remained exactly the same, which means that in this particular case, putting it to work wouldn’t be radically different from other code samples shown previously. But is this really true? Well, I have to say yes, and the following code fragment demonstrates that:

// use FileIterator class

try

{

// create instance of FileIterator

$fit = new FileIterator(‘sample_file.txt’);

// reset iterator pointer

$fit->rewind();

// display current file line

echo $fit->current();

// move iterator pointer forward

$fit->next();

// display current file line

echo $fit->current();

// move iterator pointer forward

$fit->next();

// display current file line

echo $fit->current();

// reset iterator pointer

$fit->rewind();

// display current file line

echo $fit->current();

}

// catch exceptions

catch (Exception $e)

{

echo $e->getMessage();

exit();

}

Nothing really unexpected, right? As you can see, iterating over the contents of a text file is a process that doesn’t differ at all from the example that you saw in the introductory installment. In addition, it’s valid to stress that even though turning the base iterator into an abstract class permitted us to get rid of a protected constructor, there are other scenarios where a restrictive constructor can be of great help to implement certain patterns, or even assure that a class will be used out of the object’s context.

However, some of these topics will be discussed in more detail in upcoming parts of the series.

Final thoughts

In this second episode of the series, I rebuilt the example application developed in the previous tutorial, which used a protected constructor to prevent the instantiation of the base array iterator class. In this case, it’s valid to stress that a better result was achieved by declaring the iterator in question abstract, but hopefully the example was useful enough to demonstrate a simple utilization of a restrictive constructor in a concrete situation.

As I said in the introduction of this series, protected and private constructors can be used in a great diversity of scenarios and contexts. Therefore, in the subsequent tutorial I’m going to explain how to work with a private constructor to implement a Singleton database access class.