Home arrow PHP arrow Page 2 - Implementing the Countable SPL Interface

Review: implementing the Iterator interface - PHP

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.

TABLE OF CONTENTS:
  1. Implementing the Countable SPL Interface
  2. Review: implementing the Iterator interface
  3. Counting rows in result sets with the Countable SPL interface
  4. The full source code for the MySQLi_ResultWrapper class
By: Alejandro Gervasio
Rating: starstarstarstarstar / 2
March 30, 2010

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

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.      



 
 
>>> More PHP Articles          >>> More By Alejandro Gervasio
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

PHP ARTICLES

- Hackers Compromise PHP Sites to Launch Attac...
- Red Hat, Zend Form OpenShift PaaS Alliance
- PHP IDE News
- BCD, Zend Extend PHP Partnership
- PHP FAQ Highlight
- PHP Creator Didn't Set Out to Create a Langu...
- PHP Trends Revealed in Zend Study
- PHP: Best Methods for Running Scheduled Jobs
- PHP Array Functions: array_change_key_case
- PHP array_combine Function
- PHP array_chunk Function
- PHP Closures as View Helpers: Lazy-Loading F...
- Using PHP Closures as View Helpers
- PHP File and Operating System Program Execut...
- PHP: Effects of Wrapping Code in Class Const...

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: