Implementing the Countable SPL Interface

In this third part of a seven-part series on the Iterator, Countable and ArrayAccess SPL interfaces, I give the MySQLi_ResultWrapper class developed in the previous part the capability of counting the number of rows contained in a given result set. I’ll do that by implementing the “count()” method declared by the Countable interface. Doing this will make it possible to use an instance of the class to traverse data sets and count records as if they were plain PHP arrays, which is quite appealing to client code that works with the class’s API.

As you possibly know, the Standard PHP Library (also know simply by its acronym, SPL) is comprised of a powerful set of functions, classes and interfaces that permit developers to address problems that occur frequently during the development of web programs. Ranging from autoloading classes on demand and overloading arrays, to recursively traversing directories in the file system, the SPL makes all of these tasks painless.

Among the interfaces included with the library, there are a few that deserve a closer analysis. They can be used to construct classes that allow you to treat distinct data collections as plain PHP arrays. Yes, as this article’s title suggests, in this case I’m talking specifically about the Iterator, Countable and ArrayAccess interfaces. When properly implemented by a class, these interfaces provide it with an intuitive API that can be employed to manipulate text files, database result sets, and so forth by using an array-like notation.

To demonstrate how useful these interfaces can be in a concrete situation, in the two previous parts of this series, I started building a couple of MySQL abstraction classes that performed some common tasks, such as connecting to the database server and running queries, fetching rows and iterating over data sets. I made the last of these classes an implementer of the SPL Iterator interface, which gave it the ability to traverse result sets by way of a simple “foreach()” construct, thanks the implementation of the “rewind(),” “current(),” “key(),” “valid()” and “next()” methods declared by the interface in question.

Although the class in its current state is pretty functional, it lacks an important feature — it doesn’t implement a method that counts the number of records contained in a given result set. To address this issue, in this third installment of the series I’m going to show you how to make the class an implementer of the Countable interface. Doing this will provide the class with a “count()” method, which naturally will be used for performing the aforementioned counting task in data sets.

Ready to learn a few more useful things about the Countable SPL interface? Then, go ahead and begin reading right away!            

{mospagebreak title=Review: implementing the Iterator interface}

Before I start showing how to turn the “MySQLi_ResultWrapper” class built in the preceding installment into an implementer of the Countable SPL interface, it’d be really useful to reintroduce its current definition. This way you can learn (or recall) how this class implemented the methods declared by the Iterator interface.

Having said that, below you’ll find the source code of the class that composes the mentioned “MySQLi_ResultWrapper,” which takes care of connecting to MySQL, running queries and so forth:    

 

class MySQLiWrapper extends MySQLi

{

   

   private static $_instance = NULL;

 

 

   // return Singleton instance of MySQL class

   public static function getInstance(array $config = array())

   {

       if (self::$_instance === NULL)

       {

          self::$_instance = new self($config);

       }

       return self::$_instance;

   }

  

   // private constructor

   private function __construct(array $config)

   {

        if (count($config) < 4)

        {

            throw new Exception(‘Invalid number of connection parameters’);  

        }

       list($host, $user, $password, $database) = $config;

       parent::__construct($host, $user, $password, $database);

        if ($this->connect_error)

        {

            throw new Exception(‘Error connecting to MySQL : ‘ . $this->connect_errno . ‘ ‘ . $this->connect_error);

        }

   }

 

 

    // prevent cloning class instance

    private function __clone(){}

   

    // perform query

    public function runQuery($query)

    {

        if (is_string($query) AND !empty($query))

        {

            if ((!$this->real_query($query)))

            {

                throw new Exception(‘Error performing query ‘ . $query . ‘ – Error message : ‘ . $this->error);

            }

            return new MySQLi_ResultWrapper($this);

        }

    }

   

    // get insertion ID

    public function getInsertID()

    {

        return $this->insert_id;

    }

   

    // close database connection

    public function __destruct()

    {

        $this->close();

    }

}

As I said a moment ago, the above “MySQLiWrapper” class exposes to client code a simple API that allows it to connect to MySQL, perform hard-coded queries and find insertion IDs. Without a doubt, the class’s workhorse method is “runQuery().” This method not only runs a specified SQL statement, but creates an instance of a brand new class called “MySQLi_ResultWrapper,” whose corresponding definition is shown below:

class MySQLi_ResultWrapper extends MySQLi_Result implements Iterator

{

    private $_pointer = 0;

   

   // fetch row as an object

    public function fetchObject()

    {

        if (!$row = $this->fetch_object())

        {

            return NULL;

        }

        return $row;

    }

   

    // fetch row as an associative array

    public function fetchAssocArray()

    {

        if (!$row = $this->fetch_assoc())

        {

            return NULL;

        }

        return $row;

    }

   

    // fetch row as an enumerated array

    public function fetchNumArray()

    {

        if (!$row = $this->fetch_row())

        {

            return NULL;

        }

        return $row;

    }

   

    // fetch all rows

    public function fetchAll($type = MYSQLI_ASSOC)

    {

        if ($type !== MYSQLI_ASSOC AND $type !== MYSQLI_NUM AND $type !== MYSQLI_BOTH)

        {

            $type = MYSQLI_ASSOC;

        }

        if (!$rows = $this->fetch_all($type))

        {

            return NULL;

        }

        return $rows;  

    }

   

    // get definition information on fields

    public function fetchFieldsInfo()

    {

        if (!$fieldsInfo = $this->fetch_fields())

        {

            throw new Exception(‘No information available for table fields.’);

        }

        return $fieldsInfo;

    }

   

    // get definition information on next field

    public function fetchFieldInfo()

    {

        if (!$fieldInfo = $this->fetch_field())

        {

            throw new Exception(‘No information available for current table field.’);   

        }

        return $fieldInfo;

    }

   

    // move pointer in result set to specified offset

    public function movePointer($offset)

    {

        $offset = abs((int)$offset);

        $limit = $this->num_rows – 1;

        if ($limit <= 0 OR $offset > $limit)

        {

            return NULL;

        }

        unset($limit);

        return $this->data_seek($offset);

    }

      

    // reset result set pointer (implementation required by ‘rewind()’ method in Iterator interface)

    public function rewind()

    {

        $this->_pointer = 0;

        $this->movePointer($this->_pointer);

        return $this; 

    }

   

    // get current row set in result set (implementation required by ‘current()’ method in Iterator interface)

    public function current()

    {

        if (!$this->valid())

        {

            throw new Exception(‘Unable to retrieve current row.’);

        }

        $this->movePointer($this->_pointer);

        return $this->fetchObject();

    }

   

    // get current result set pointer (implementation required by ‘key()’ method in Iterator interface)

    public function key()

    {

        return $this->_pointer;

    }

   

    // move forward result set pointer (implementation required by ‘next()’ method in Iterator interface)

    public function next()

    {

        ++$this->_pointer;

        $this->movePointer($this->_pointer);

        return $this;

    }

   

    // determine if result set pointer is valid or not (implementation required by ‘valid()’ method in Iterator interface)

    public function valid()

    {

        return $this->_pointer < $this->num_rows;

    }

} 

The reason to code the “MySQLi_ResultWrapper” class is simply to separate concerns, as the class is only responsible for fetching data from one or multiple tables. However, the most relevant detail to highlight here is that the class implements the aforementioned Iterator interface, a process that allows it to traverse data sets by means of an array-like notation, including “foreach” constructs.

The advantages of doing this should be clear to you at this point. Yet, the class still lacks an important feature that was discussed in the introduction: it doesn’t implement a method that counts the number of records included in result sets. Of course, its “num_rows” property could be used directly to do this, but as you know, in most cases it’s not recommended to access a property directly from the outside.

So, how can the “MySQLi_ResultWrapper” class count data set rows as if they were array elements? Well, this can be easily accomplished by making it an implementer of the Countable interface, which declares a “count()” method, like the one that you use to count elements in your own arrays.

Given that PHP 5 lets a class implement any number of interfaces, in the next section I’m going to modify the definition of the previous “MySQLi_ResultWrapper” to implement the already familiar Iterator interface and the aforementioned Countable at the same time.

To see how this will be done, click on the link below and read the lines to come.      

{mospagebreak title=Counting rows in result sets with the Countable SPL interface}

In the section that you just read, I stated that the previous “MySQLi_ResultWrapper” class was going to implement the Countable SPL interface, right? Well, in keeping with this, it’s mandatory to make the class include a “count()” method. In this case, this method will be simply a getter for the “num_rows” property, and its definition will be as follows:

  // count rows in result set (implementation required by ‘count()’ method in Countable interface)

public function count()

{

    return $this->num_rows;

}

For obvious reasons, the implementation of the “count()” method is extremely simple; all that it does is return to client code the value of the corresponding “num_rows” property, which contains the number of rows contained in a given result set. With the inclusion of this new method, the “MySQLi_ResultWrapper” class now has the ability to count database rows as if they were plain array elements.

Since the logic that drives the previous “count()” method doesn’t bear any further discussion, it’s time to show the complete source code of the “MySQLi_ResultWrapper” class, this time including the implementation of the Countable interface.

This last process will conclude this third part of the series, so if you want to learn more about it, read the following section. 

{mospagebreak title=The full source code for the MySQLi_ResultWrapper class}

Undeniably, the best way to understand how the prior “MySQLi_ResultWrapper” class can be an implementer of both the Iterator and Countable PHP 5 interfaces is by examining its full source code. Thus, below you’ll find the complete definition of this class, including the “count()” method that you saw in the previous segment. Here it is:

class MySQLi_ResultWrapper extends MySQLi_Result implements Iterator, Countable

{

    private $_pointer = 0;

   

   // fetch row as an object

    public function fetchObject()

    {

        if (!$row = $this->fetch_object())

        {

            return NULL;

        }

        return $row;

    }

   

    // fetch row as an associative array

    public function fetchAssocArray()

    {

        if (!$row = $this->fetch_assoc())

        {

            return NULL;

        }

        return $row;

    }

   

    // fetch row as an enumerated array

    public function fetchNumArray()

    {

        if (!$row = $this->fetch_row())

        {

            return NULL;

        }

        return $row;

    }

   

    // fetch all rows

    public function fetchAll($type = MYSQLI_ASSOC)

    {

        if ($type !== MYSQLI_ASSOC AND $type !== MYSQLI_NUM AND $type !== MYSQLI_BOTH)

        {

            $type = MYSQLI_ASSOC;

        }

        if (!$rows = $this->fetch_all($type))

        {

            return NULL;

        }

        return $rows;  

    }

   

    // get definition information on fields

    public function fetchFieldsInfo()

    {

        if (!$fieldsInfo = $this->fetch_fields())

        {

            throw new Exception(‘No information available for table fields.’);

        }

        return $fieldsInfo;

    }

   

    // get definition information on next field

    public function fetchFieldInfo()

    {

        if (!$fieldInfo = $this->fetch_field())

        {

            throw new Exception(‘No information available for current table field.’);   

        }

        return $fieldInfo;

    }

   

    // move pointer in result set to specified offset

    public function movePointer($offset)

    {

        $offset = abs((int)$offset);

        $limit = $this->num_rows – 1;

        if ($limit <= 0 OR $offset > $limit)

        {

            return NULL;

        }

        unset($limit);

        return $this->data_seek($offset);

    }

   

    // count rows in result set (implementation required by ‘count()’ method in Countable interface)

    public function count()

    {

        return $this->num_rows;

    }

      

    // reset result set pointer (implementation required by ‘rewind()’ method in Iterator interface)

    public function rewind()

    {

        $this->_pointer = 0;

        $this->movePointer($this->_pointer);

        return $this; 

    }

   

    // get current row set in result set (implementation required by ‘current()’ method in Iterator interface)

    public function current()

    {

        if (!$this->valid())

        {

            throw new Exception(‘Unable to retrieve current row.’);

        }

        $this->movePointer($this->_pointer);

        return $this->fetchObject();

    }

   

    // get current result set pointer (implementation required by ‘key()’ method in Iterator interface)

    public function key()

    {

        return $this->_pointer;

    }

   

    // move forward result set pointer (implementation required by ‘next()’ method in Iterator interface)

    public function next()

    {

        ++$this->_pointer;

        $this->movePointer($this->_pointer);

        return $this;

    }

   

    // determine if result set pointer is valid or not (implementation required by ‘valid()’ method in Iterator interface)

    public function valid()

    {

        return $this->_pointer < $this->num_rows;

    }

} 

As you can see from the code above, the “MySQLi_ResultWrapper” class has successfully implemented the Iterator and Countable SPL interfaces. While this process seems to be everything but interesting, the truth is that now client code using an instance of this class will be able to traverse and count rows in MySQL data sets by using a “foreach” iterator and the “count()” method respectively.

We’re not done with this sample class, though. It would be good to give it the ability to retrieve a particular row in a result set by specifying its offset. In reality, this can be accomplished by implementing another interface packaged with the Standard PHP Library called ArrayAccess. The details of this process will be covered in the next tutorial.   

Final thoughts

That’s all for now. In this third part of the series I gave the previous “MySQLi_ResultWrapper” class the capability to count the number of rows contained in a given result set by implementing the “count()” method declared by the Countable interface. In doing so, it’s now possible to use an instance of the class to traverse data sets and count records as if they were plain PHP arrays, which is quite appealing to client code that works with the class’ API.

In addition, it’d be desirable to provide the class with the ability to access a particular row in a result set by specifying only its offset. Fortunately, this can be achieved with minor hassles by making the class an implementer of the ArrayAccess SPL interface. As I mentioned before, this topic will be covered in depth in the next article of the series, so don’t miss it!    

Google+ Comments

Google+ Comments