0

On my server i get this error

Fatal error: Call to undefined method mysqli_stmt::get_result() in /var/www/virtual/fcb/htdocs/library/mysqlidbclass.php on line 144

And I am having a wrapper like this:

<?php

/* https://github.com/aaron-lord/mysqli */

class mysqlidb {
    /**
     * Set up the database connection
     */

    public function __construct($server,$user,$password,$db){
        $this->connection = $this->connect($server, $user, $password, $db, true);
    }


    /**
     * Connect to the database, with or without a persistant connection
     * @param  String  $host       Mysql server hostname
     * @param  String  $user       Mysql username
     * @param  String  $pass       Mysql password
     * @param  String  $db         Database to use
     * @param  boolean $persistant Create a persistant connection
     * @return Object              Mysqli
     */
    private function connect($host, $user, $pass, $db, $persistant = true){
        $host = $persistant === true ? 'p:'.$host : $host;

        $mysqli = new mysqli($host, $user, $pass, $db);

        if($mysqli->connect_error)
            throw new Exception('Connection Error: '.$mysqli->connect_error);

        $mysqli->set_charset('utf8');

        return $mysqli;
    }

    /**
     * Execute an SQL statement for execution.
     * @param  String $sql An SQL query
     * @return Object      $this
     */
    public function query($sql){
        $this->num_rows = 0;
        $this->affected_rows = -1;

        if(is_object($this->connection)){
            $stmt = $this->connection->query($sql);

            # Affected rows has to go here for query :o
            $this->affected_rows = $this->connection->affected_rows;
            $this->stmt = $stmt;
            return $this;
        }
        else {
            throw new Exception;
        }
    }

    /**
     * Prepare an SQL statement
     * @param  String $sql An SQL query
     * @return Object      $this
     */
    public function prepare($sql){
        unset($this->stmt);
        $this->num_rows = 0;
        $this->affected_rows = -1;

        if(is_object($this->connection)){
            # Ready the stmt

            $this->stmt = $this->connection->prepare($sql);

            if (false===$this->stmt)
            {
                print('prepare failed: ' . htmlspecialchars($this->connection->error)."<br />");
            }

            return $this;
        }
        else {
            throw new Exception();
        }
    }

    public function multi_query(){ }

    /**
     * Escapes the arguments passed in and executes a prepared Query.
     * @param Mixed $var   The value to be bound to the first SQL ?
     * @param Mixed $...   Each subsequent value to be bound to ?
     * @return Object      $this
     */
    public function execute(){
        if(is_object($this->connection) && is_object($this->stmt)){
            # Ready the params
            if(count($args = func_get_args()) > 0){
                $types = array();
                $params = array();

                foreach($args as $arg){
                    $types[] = is_int($arg) ? 'i' : (is_float($arg) ? 'd' : 's');
                    $params[] = $arg;
                }

                # Stick the types at the start of the params
                array_unshift($params, implode($types));

                # Call bind_param (avoiding the pass_by_reference crap)
                call_user_func_array(
                    array($this->stmt, 'bind_param'),
                    $this->_pass_by_reference($params)
                );
            }

            if($this->stmt->execute()){
                # Affected rows to be run after execute for prepares
                $this->affected_rows = $this->stmt->affected_rows;
                return $this;
            }
            else {
                throw new Exception($this->connection->error);
            }
        }
        else {
            throw new Exception;
        }
    }

    /**
     * Fetch all results as an array, the type of array depend on the $method passed through.
     * @param  string  $method     Optional perameter to indicate what type of array to return.'assoc' is the default and returns an accociative array, 'row' returns a numeric array and 'array' returns an array of both.
     * @param  boolean $close_stmt Optional perameter to indicate if the statement should be destroyed after execution.
     * @return Array              Array of database results
     */
    public function results($method = 'assoc', $close_stmt = false){
        if(is_object($this->stmt)){
            $stmt_type = get_class($this->stmt);

            # Grab the result prepare() & query()
            switch($stmt_type){
                case 'mysqli_stmt':
                    $result = $this->stmt->get_result();
                    $close_result = 'close';
                    break;

                case 'mysqli_result':
                    $result = $this->stmt;
                    $close_result = 'free';
                    break;

                default:
                    throw new Exception;
            }

            $this->num_rows = $result->num_rows;

            # Set the results type
            switch($method) {
                case 'assoc':
                    $method = 'fetch_assoc';
                    break;

                case 'row':
                    //return 'fetch_row';
                    $method = 'fetch_row';
                    break;

                default:
                    $method = 'fetch_array';
                    break;
            }

            $results = array();
            while($row = $result->$method()){
                $results[] = $row;
            }

            $result->$close_result();
            return $results;
        }
        else {
            throw new Exception;
        }
    }

    /**
     * Turns off auto-committing database modifications, starting a new transaction.
     * @return bool Dependant on the how successful the autocommit() call was
     */
    public function start_transaction(){
        if(is_object($this->connection)){
            return $this->connection->autocommit(false);
        }
    }

    /**
     * Commits the current transaction and turns auto-committing database modifications on, ending transactions.
     * @return bool Dependant on the how successful the autocommit() call was
     */
    public function commit(){
        if(is_object($this->connection)){
            # Commit!
            if($this->connection->commit()){
                return $this->connection->autocommit(true);
            }
            else {
                $this->connection->autocommit(true);
                throw new Exception;
            }
        }
    }

    /**
     * Rolls back current transaction and turns auto-committing database modifications on, ending transactions.
     * @return bool Dependant on the how successful the autocommit() call was
     */
    public function rollback(){
        if(is_object($this->connection)){
            # Commit!
            if($this->connection->rollback()){
                return $this->connection->autocommit(true);
            }
            else {
                $this->connection->autocommit(true);
                throw new Exception;
            }
        }
    }

    /**
     * Return the number of rows in statements result set.
     * @return integer The number of rows
     */
    public function num_rows(){
        return $this->num_rows;
    }

    /**
     * Gets the number of affected rows in a previous MySQL operation.
     * @return integer The affected rows
     */
    public function affected_rows(){
        return $this->affected_rows;
    }

    /**
     * Returns the auto generated id used in the last query.
     * @return integer The last auto generated id
     */
    public function insert_id(){
        if(is_object($this->connection)){
            return $this->connection->insert_id;
        }
    }

    /**
     * Fixes the call_user_func_array & bind_param pass by reference crap.
     * @param  array $arr The array to be referenced
     * @return array      A referenced array
     */
    private function _pass_by_reference(&$arr){ 
        $refs = array(); 
        foreach($arr as $key => $value){
            $refs[$key] = &$arr[$key]; 
        }
        return $refs; 
    }
}

?>

Is there any way to use another function so that I won't have to rewrite whole app? Please tell me if any.

Davit
  • 1,340
  • 5
  • 20
  • 47
  • why not just inherit the mysqli object and extend it? there's absolutely no need to write your own entire object wrapper class for it, since mysqli already offers an OOP interface. – Marc B Jan 24 '13 at 20:26
  • i already have a whole website made up with this wrapper so I need just solution to my problem – Davit Jan 24 '13 at 20:29
  • results() method looks ugly. Why not to have separate methods for different result types? And make them return scalar and array of rows too – Your Common Sense Jan 24 '13 at 20:32
  • okay, I'm still trying to solve what I asked for. because whole app uses this class and I cant change it for at least for this website – Davit Jan 24 '13 at 20:52

2 Answers2

1

Please read the user notes for this method:

http://php.net/manual/en/mysqli-stmt.get-result.php

It requires the mysqlnd driver. if it isn't installed on your webspace you will have to work with BIND_RESULT & FETCH

http://www.php.net/manual/en/mysqli-stmt.bind-result.php

http://www.php.net/manual/en/mysqli-stmt.fetch.php

Extracted from here

Community
  • 1
  • 1
Techie
  • 42,101
  • 38
  • 144
  • 232
0

The reason for this error is that your server doesn't have the mysqlnd driver driver installed. (See here.) If you have admin privileges you could install it yourself, but there is also an easier way:

I have written two simple functions that give the same functionality as $stmt->get_result();, but they don't require the mysqlnd driver.

You simply replace

$result = $stmt->get_result(); with $fields = bindAll($stmt);

and

$row= $stmt->get_result(); with $row = fetchRowAssoc($stmt, $fields);.

(To get the numbers of returned rows you can use $stmt->num_rows.)

You just have to place these two functions I have written somewhere in your PHP Script. (for example right at the bottom)

function bindAll($stmt) {
    $meta = $stmt->result_metadata();
    $fields = array();
    $fieldRefs = array();
    while ($field = $meta->fetch_field())
    {
        $fields[$field->name] = "";
        $fieldRefs[] = &$fields[$field->name];
    }

    call_user_func_array(array($stmt, 'bind_result'), $fieldRefs);
    $stmt->store_result();
    //var_dump($fields);
    return $fields;
}

function fetchRowAssoc($stmt, &$fields) {
    if ($stmt->fetch()) {
        return $fields;
    }
    return false;
}

How it works:

My code uses the $stmt->result_metadata(); function to figure out how many and which fields are returned and then automatically binds the fetched results to pre-created references. Works like a charm!

Also posted here.

Stefan S.
  • 177
  • 1
  • 8