AzizSaleh/mysql

mysql_fetch_array pass by reference / different function signature

thr27 opened this issue · 1 comments

I found a problem with the implementation of mysql_fetch_array.
The implementation in the class changes the function signature of the first argument to pass-by-reference. And then changes the argument from PDO Object to Array. This only works if the calling function holds the actuall variable. In my case the fetch loop was calling subfunction for the actual fetch, therefore the variable was not changed as it was passed by value to the subfunction.

I added a fix in mysql_fetch_array, storing the $result argument in a hash-array and retrieing it in subsequent calls.

test code to see problem:

/**
     * Test mysql_fetch_array
     *
     * @return boolean
     */
    public function __do_fetch($query3, $fetchType) {
        return $this->_object->mysql_fetch_array($query3, $fetchType);
    }
    public function MySQL_Fetch_Array_Test()
    {
        // Select Db
        $this->_selectDb();

        // Get rows (we should have 2 by now)
        $sql = 'SELECT field_id FROM ' . TEST_TABLE . ' ORDER BY field_id ASC LIMIT 2';

        // Results
        $res1 = array();
        $res2 = array();
        $res3 = array();

        // For each fetch type
        foreach (array(MYSQL_ASSOC, MYSQL_NUM, MYSQL_BOTH) as $fetchType) {

            // Query
            $query = mysql_query($sql);
            $query2 = $this->_object->mysql_query($sql);
            $query3 = $this->_object->mysql_query($sql);

            // Must match
            while ($r = mysql_fetch_array($query, $fetchType)) {
                $res1[] = $r;
            }
            while($r2 = $this->_object->mysql_fetch_array($query2, $fetchType)) {
                $res2[] = $r2;
            }
            while($r3 = $this->__do_fetch($query3, $fetchType)) {
                $res3[] = $r3;
            }
        }
...

Proposed Fix

use of '$this->_result_in[$hash]' to restore previous state

/**
     * mysql_unbuffered_query
     * http://www.php.net/manual/en/function.mysql-unbuffered-query.php
     */
    public function mysql_unbuffered_query($query, $link = false)
    {
        return $this->mysql_query($query, $link);
    }

    /**
     * mysql_fetch_array
     * http://www.php.net/manual/en/function.mysql-fetch-array.php
     */
    public function mysql_fetch_array(&$result, $resultType = 3, $doCounts = false, $elementId = false)
    {
        static $last = null;

        if ($result === false) {
            echo 'Warning: mysql_fetch_*(): supplied argument is not a valid MySQL result resource' . PHP_EOL;
            return false;
        }
        $hash = spl_object_hash($result);
        if ($hash !== false){ 
            if( isset($this->_result_in[$hash])) {
                $result = &$this->_result_in[$hash];
            } 
        }
        if ($doCounts === true) {
            return $this->_mysqlGetLengths($last, $elementId);
        }

        $hash = false;

        // Set retrieval type
        if (!is_array($result)) {
            $hash = spl_object_hash($result);
            switch ($resultType) {
                case 1:
                    // by field names only as array
                    $result = $result->fetchAll(PDO::FETCH_ASSOC);
                    break;
                case 2:
                    // by field position only as array
                    $result = $result->fetchAll(PDO::FETCH_NUM);
                    break;
                case 3:
                    // by both field name/position as array
                    $result = $result->fetchAll();
                    break;
                case 4:
                    // by field names as object
                    $result = $result->fetchAll(PDO::FETCH_OBJ);
                    break;
            }
        }

        // Row seek
        if ($hash !== false && isset($this->_rowSeek[$hash])) {
            // Check valid skip
            $rowNumber = $this->_rowSeek[$hash];
            if ($rowNumber > count($result) - 1) {
                echo "Warning: mysql_data_seek(): Offset $rowNumber is invalid for MySQL result (or the query data is unbuffered)" . PHP_EOL;
            }

            while($rowNumber > 0) {
                next($result);
                $rowNumber--;
            }

            unset($this->_rowSeek[$hash]);
        }

        $last = current($result);
        next($result);
        $this->_result_in[$hash] = $result;

        return $last;
    }
...

Hello thr,

Thanks for submitting this issue, I will look into a solution that can work without causing issues to other functionality. Using the fix you submitted, I was able to work it for LIMIT 2, but the LIMIT 1 caused issues for me as well as causing 4 other unit tests to fail.

A quick fix would be to have the custom function you created also do a by reference pass as well:

    public function __do_fetch(&$query3, $fetchType)

Thanks again,