2

For debug purpose, I would like to be able to clone a PDOStatement, or fetch data without removing the rows from the statement.

Here's an example to show what I'm trying to do :

public function execute($query)
{
    // Prepare the statement
    if (!($stmt = $this->prepare($query))) {
        return false;
    }

    // Execute the statement
    $stmt->execute($params);

    // Print the query with a custom debug tool
    if( $config->debug ) {
        Debug::print($query);
        Debug::print($stmt->fetchAll(PDO::FETCH_ASSOC));
    }

    // Return the PDOStatement
    return $stmt;
}

The problem is that fetchAll() do its job, remove every result from my statement, and an empty array is returned by the function.

I'm searching a way to print the query result (debug purpose) and return the initial statement (treatments purpose), without querying my database twice !

I tried to clone my statement, without success :

if( $config->debug ) {
    $_stmt = clone $stmt;
    Debug::print($query);
    Debug::print($_stmt->fetchAll(PDO::FETCH_ASSOC));
}

Any idea ?

hjpotter92
  • 78,589
  • 36
  • 144
  • 183
zessx
  • 68,042
  • 28
  • 135
  • 158
  • 1
    Is $stmt clonable? [here](http://php.net/manual/en/book.pdo.php) it says `/* * Like the constructor, we make __clone private so nobody can clone the instance */ private function __clone() {}` – Jack M. Aug 20 '14 at 14:46
  • That was my first attempt, but you're right PDOStatement is not clonable. That's why I'm searching another way. – zessx Aug 20 '14 at 15:18
  • 1
    Why would you debug the statement results within the execute method? Why not handle that in the caller or wherever you normally actually fetch the results. Why not simply provide debug on the execution itself (i.e. the query and true/false result)? You are currently breaking the principle of encapsulation. – Mike Brant Aug 20 '14 at 16:51
  • Maybe [this answer](http://stackoverflow.com/questions/9437214/resetting-array-pointer-in-pdo-results) would be what you looking for. You return a fetched array instead of the $stmt. What you seem to want is a full reset function, but that does not exist. – Jack M. Aug 20 '14 at 18:24
  • @MrJack I thought about this solution, but I must return a PDOStatement object, because this function is used by others ones, like `select()`, `selectOne()`, `selectValue()`... – zessx Aug 20 '14 at 21:57
  • @MikeBrant I would like to show *every* query and its results in my debug tool. This would allow me to quickly find any error (a query could have a right syntax, but still be wrong-made). I agree with you, this work should be made in the caller function, but if I want to show every query, it means I must call the debug function each time. I'm searching for a user-friendly way to show all results. – zessx Aug 20 '14 at 22:02
  • would `$stmt2 = $stmt` copy the executed statement, or will php internally make two referenced variable of the same statement? – Félix Adriyel Gagnon-Grenier Aug 20 '14 at 23:33
  • @zessx I understand what you are after, I am just suggesting that you you only send out debug information around the query itself and the true/false result of the query. All the method itself does is return the PDOStatement object, so why would you want to debug some within the method (working with the result set) that the method itself does not even perform? If the method only runs `PDO::execute()` why not limit your debug efforts only to that? Your question then becomes moot in that you could debug log the result set within whatever function/code calls the result set as you read it out. – Mike Brant Aug 21 '14 at 13:57
  • @zessx I think you will find that there is not convenient way to do what you want to do as you are violating a key principle of object-oriented programming. – Mike Brant Aug 21 '14 at 13:58
  • @MikeBrant Breaking POO principles led me to this situtation, you're perfectly right. I think I'll log the query, and the number of affected rows, as this is allowed by `PDOStatement`. It should be enought to detect a major part of dev bug. – zessx Aug 21 '14 at 14:06

2 Answers2

0

PDOStatement is not cloneable.

The only way to achieve what I wanted was to re-execute the statement:

public function execute($query)
{
    // Prepare the statement
    if (!($stmt = $this->prepare($query))) {
        return false;
    }

    // Execute the statement
    $stmt->execute($params);

    // Print the query with a custom debug tool
    if( $config->debug ) {
        Debug::print($query);
        Debug::print($stmt->fetchAll(PDO::FETCH_ASSOC));
        $stmt->execute($params);
    }

    // Return the PDOStatement
    return $stmt;
}
zessx
  • 68,042
  • 28
  • 135
  • 158
0

Instead of repeating your query, you should extend PDOStatement and make PDO use that class with

$pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, [yourclass]::class);

In that own class you can make PDOStatement seekable, ie. cache the results of the fetch/fetchAll/fetchColumn functions.

Mrten
  • 482
  • 5
  • 12