3

Conform to the php docs, the PDO method fetch() returns the value FALSE both when no records are found AND on failure (e.g. when something goes wrong regarding the database access).

Let's suppose, I set the PHP error reporting system to throw exceptions on failure:

PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION

I need a case, a situation in which the fetch() method will throw an exception. Why? Because I want to check, to be 100% sure that fetch() throws an exception on failure, and doesn't just return FALSE on failure.

If that would be the case, then I would indeed consider the FALSE returned by fetch() as the result of not finding any records in the db table.

So, my question would be: Do you know a way to simulate a failure situation for the fetch() method?

Thank you.

P.S.: The answer to my question will help me find the answer for my other question: PHP PDO fetch returns FALSE when no records found AND on failure


Edit 1:

I also prepared an example, to show how I handle the exceptions. It's about a simple sql query, fetching a user from a users table:

<?php

// Activate error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1);

try {

    // Create a PDO instance as db connection to a MySQL db.
    $connection = new PDO(
            'mysql:host=localhost;port=3306;dbname=mydb;charset=utf8'
            , 'myuser'
            , 'mypass'
            , array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => FALSE,
        PDO::ATTR_PERSISTENT => TRUE
            )
    );

    // Define the sql statement.
    $sql = 'SELECT * FROM users WHERE name = :name';

    /*
     * Prepare and validate the sql statement.
     * 
     * --------------------------------------------------------------------------------
     * If the database server cannot successfully prepare the statement, PDO::prepare() 
     * returns FALSE or emits PDOException (depending on error handling settings).
     * --------------------------------------------------------------------------------
     */
    $statement = $connection->prepare($sql);

    if (!$statement) {
        throw new UnexpectedValueException('The sql statement could not be prepared!');
    }

    // Bind the input parameter to the prepared statement.
    $bound = $statement->bindValue(':name', 'Sarah', PDO::PARAM_STR);

    // Validate the binding of the input parameter.
    if (!$bound) {
        throw new UnexpectedValueException('An input parameter can not be bound!');
    }

    /*
     * Execute the prepared statement.
     * 
     * ------------------------------------------------------------------
     * PDOStatement::execute returns TRUE on success or FALSE on failure.
     * ------------------------------------------------------------------
     */
    $executed = $statement->execute();

    if (!$executed) {
        throw new UnexpectedValueException('The prepared statement can not be executed!');
    }

    /*
     * Fetch and validate the result set.
     * 
     * =========================================================
     * Note:
     * =========================================================
     * PDOStatement::fetch returns FALSE not only on failure,
     * but ALSO when no record is found!
     * 
     * Instead, PDOStatement::fetchAll returns FALSE on failure,
     * but an empty array if no record is found. This is the
     * natural, desired behaviour.
     * =========================================================
     */
    $resultset = $statement->fetch(PDO::FETCH_ASSOC);

    if ($resultset === FALSE) {
        throw new UnexpectedValueException('Fetching data failed!');
    }

    // Display the result set.
    var_dump($resultset);
    echo '<pre>' . print_r($resultset, TRUE) . '</pre>';

    // Close connection.
    $connection = NULL;
} catch (PDOException $exc) {
    echo '<pre>' . print_r($exc, TRUE) . '</pre>';
    exit();
} catch (Exception $exc) {
    echo '<pre>' . print_r($exc, TRUE) . '</pre>';
    exit();
}

I used the following create table syntax:

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=124 DEFAULT CHARSET=utf8;

and the following table values:

INSERT INTO `users` (`id`, `name`)
VALUES
    (1,'Sarah'),
    (2,'John');

So, the table looks like this:

id  name
--------
1   Sarah
2   John
  • Enable exception based error reporting – tereško Sep 21 '17 at 11:54
  • The manual says: "The return value of this function on success depends on the fetch type. In all cases, FALSE is returned on failure.". I expect it to return `NULL` when there are no results. So two different tests are possible: `if ($result === FALSE) ...` to test for failure and `if (!isset($result)) ....` or `if (is_null($result))` to test for no records. – KIKO Software Sep 21 '17 at 11:56
  • Thanks, @teresko. I have it already. But, since php.net says, that on failure a FALSE is returned, I don't really know, if an exception is thrown on failure, instead of FALSE. –  Sep 21 '17 at 11:57
  • Thank you for your comment, @KIKOSoftware. If `NULL` would be returned, then it would beatiful. But it's not... `fetch()` returns always `FALSE` when no records are found - regardless of the argument(s) I pass to the function. –  Sep 21 '17 at 12:01
  • Possible duplicate of [How to handle PDO exceptions](https://stackoverflow.com/questions/2104481/how-to-handle-pdo-exceptions) – Masivuye Cokile Sep 21 '17 at 12:01
  • 1
    Well, in that case handling exceptions is the way to go. Try it. It will work. – KIKO Software Sep 21 '17 at 12:06
  • If you run a malformed query using PDO, then it would throw an exception. Therefore, to simulate a bad query you would have to throw the exception yourself. – tereško Sep 21 '17 at 12:24
  • @MasivuyeCokile Let me please check the duplicate links thoroughly. Thanks. –  Sep 21 '17 at 12:37
  • @tereško Let me test something based on your last comment. –  Sep 21 '17 at 12:38
  • @MasivuyeCokile I reedited my question to be more clear. So, in the new context (which I didn't presented properly earlier - sorry), I don't consider your links as duplicates. –  Sep 21 '17 at 13:08
  • Cool, will check out the updated question @aendeerei – Masivuye Cokile Sep 21 '17 at 13:10
  • @MasivuyeCokile Thanks, I appreciate. I re-updated 5 seconds ago, so please read it starting of now. –  Sep 21 '17 at 13:13
  • @tereško I reedited my question to be more clear. The problem is, that in the `fetch()` situation I can not do what you adviced me - at least I think so, giving the fact that `fetch()` returns `FALSE` both in "no records found" **AND** failure situation. I don't exactly know, if it's clear what I'm trying to achieve. –  Sep 21 '17 at 13:21
  • @tereško In short: in the code provided in my question I make a validation on the returned value of `fetch()`. If it's `FALSE`, then throws an exception. But what if it's `FALSE` because no records where found? That's what I want exactly: to find out and test, if PHP `fetch()` throws an exception by **ITSELF** on failure, so that I can consider the `FALSE` returned value as the one signalizing **ONLY** that no records were found. –  Sep 21 '17 at 13:27
  • @tereško I finally found a case, which allowed me simulate the failure situation of `PDOStatement::fetch()`. I posted an answer for it. Thanks again for your comments! –  Sep 22 '17 at 14:19
  • @KIKOSoftware I finally found a case, which allowed me simulate the failure situation of `PDOStatement::fetch()`. I posted an answer for it. Thanks again for your comments! –  Sep 22 '17 at 14:19
  • 1
    @MasivuyeCokile I finally found a case, which allowed me simulate the failure situation of `PDOStatement::fetch()`. I posted an answer for it. Thanks again for your comments! –  Sep 22 '17 at 14:20
  • @aendeerei great m looking at ur answer below – Masivuye Cokile Sep 22 '17 at 14:21

3 Answers3

3

Finally, I found a case, which allowed me to test, if PDOStatement::fetch would indeed throw an exception on failure.

Credits:

The article Taking advantage of PDO’s fetch modes presents such a situation. It is based on the use of PDOStatement::fetchAll with PDO::FETCH_KEY_PAIR constant passed as argument.

Test:

So, I ran a test myself. But I used the PDOStatement::fetch method instead. Per definition, the PDO::FETCH_KEY_PAIR constant requires that the data source table contains only two columns. In my test I defined three table columns. PDOStatement::fetch has recognized this situation as a failure and had thrown an exception:

SQLSTATE[HY000]: General error: PDO::FETCH_KEY_PAIR fetch mode requires the result set to contain extactly 2 columns.

Conclusion:

  • PDOStatement::fetch returns FALSE, if no records are found.
  • PDOStatement::fetch throws - indeed - an exception in case of failure.

Notes:

  • Instead, PDOStatement::fetchAll returns an empty array, if no records are found.
  • The PDO::FETCH_KEY_PAIR constant is not documented on the PDOStatement::fetch official page.

P.S:

I want to thank to all the users who tried to help me finding the answer to my question. You have my appreciation!


The code used for testing:

<?php

// Activate error reporting.
error_reporting(E_ALL);
ini_set('display_errors', 1);

try {

    // Create a PDO instance as db connection to a MySQL db.
    $connection = new PDO(
            'mysql:host=localhost;port=3306;dbname=tests;charset=utf8'
            , 'root'
            , 'root'
            , array(
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => FALSE,
        PDO::ATTR_PERSISTENT => TRUE
            )
    );

    // Define the sql statement.
    $sql = 'SELECT * FROM users WHERE name = :name';

    /*
     * Prepare the sql statement.
     * 
     * --------------------------------------------------------------------------------
     * If the database server cannot successfully prepare the statement, PDO::prepare() 
     * returns FALSE or emits PDOException (depending on error handling settings).
     * --------------------------------------------------------------------------------
     */
    $statement = $connection->prepare($sql);

    // Validate the preparation of the sql statement.
    if (!$statement) {
        throw new UnexpectedValueException('The sql statement could not be prepared!');
    }

    // Bind the input parameter to the prepared statement.
    $bound = $statement->bindValue(':name', 'Sarah', PDO::PARAM_STR);

    // Validate the binding of the input parameter.
    if (!$bound) {
        throw new UnexpectedValueException('An input parameter can not be bound!');
    }

    /*
     * Execute the prepared statement.
     * 
     * ------------------------------------------------------------------
     * PDOStatement::execute returns TRUE on success or FALSE on failure.
     * ------------------------------------------------------------------
     */
    $executed = $statement->execute();

    // Validate the execution of the prepared statement.
    if (!$executed) {
        throw new UnexpectedValueException('The prepared statement can not be executed!');
    }

    // Fetch the result set.
    $resultset = $statement->fetch(PDO::FETCH_KEY_PAIR);

    // If no records found, define the result set as an empty array.
    if ($resultset === FALSE) {
        $resultset = [];
    }

    // Display the result set.
    var_dump($resultset);

    // Close connection.
    $connection = NULL;
} catch (PDOException $exc) {
    echo '<pre>' . print_r($exc->getMessage(), TRUE) . '</pre>';
    exit();
} catch (Exception $exc) {
    echo '<pre>' . print_r($exc->getMessage(), TRUE) . '</pre>';
    exit();
}

Create table syntax:

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  `phone` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

Insert values syntax:

INSERT INTO `users` (`id`, `name`, `phone`)
VALUES
    (1,'Sarah','12345'),
    (2,'John','67890');

Table values:

id  name    phone
-----------------
1   Sarah   12345
2   John    67890
0

With PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION fetch will always throw an exception if there is an error. You can handle those in your catch block and it doesn't matter what it returned. Therefore, if you didn't catch an exception and it returned false you can safely assume that it was due to an empty set. This is a very effective way have handling PDO errors. To answer your question, there are many ways of simulating an error. The most basic is improper query syntax. You can also try to bind a parameter that isn't there, bind the wrong number of parameters etc. The longer you use this scheme the more types of errors/exceptions you will see. It is very effective since each error includes a detailed message to help you debug it.

pucky124
  • 1,489
  • 12
  • 24
  • Thank you, pucky. And sorry for my late answer. You described the ways of simulating `prepare` and `bind()`. An `execute()` failure can also be simulated. These all methods throw exceptions on failure, indeed. Now, the mistery is: does `fetch()` throws an exception on failure, too? >> –  Sep 21 '17 at 22:01
  • >> This is what I'm trying hard to find out. Basically, I want to simulate exactly a situation like this... You said "__If there is an error it will throw an exception and not return at all_". Can you 100% confirm that to me? And, if yes, can you tell me just one example? I would, and I will, give my whole 385 reputations to find out one example. –  Sep 21 '17 at 22:01
  • I also posted a third version of my question: [Does PDO fetch() method throw an exception on failure?](https://stackoverflow.com/questions/46345649/does-pdo-fetch-method-throw-an-exception-on-failure). To which I received an answer, stating that no exception is thrown on failure. What's even interesting: it seems that there are no failure situations in the case of `fetch()`. –  Sep 21 '17 at 22:20
  • 1
    I updated the answer slightly to be more clear about the exception. It depends how you handle the exception if it returns false but it is a 100% effective way to differentiate among the 2 cases. – pucky124 Sep 21 '17 at 22:51
  • Thank you, pucky. Please allow me to read it tomorrow. Right now I'm very tired. I also saw, that you answered to my other question too. So, until tomorrow then. –  Sep 21 '17 at 23:47
  • Hello, pucky. I finally found a case, which allowed me simulate the failure situation of `PDOStatement::fetch()`. I posted an answer for it, too. I would have with pleasure accept your answer. But, in this case, I really needed a situation which would let itself be simulated. Thank you very much for your efforts! I really appreciate it! –  Sep 22 '17 at 14:27
-1

Unless you need the PDOStatement for a loop, you can use fetchAll in a method/function to get the result you are looking for. Just do a fetchAll instead of a fetch, check for false, and return as necessary. In this case, your query should make sure only 1 row is returned.

Something like this;

function fetch(\PDOStatement $pdo_stmt)
{
    // use fetchAll as an empty result set is returned by PDO as false using fetch()
    $result = $pdo_stmt->fetchAll(\PDO::FETCH_ASSOC);
    if ($result !== false) {
        return !empty($result) ? $result[0] : [];
    }
    return false;
}

function fetchColumn(\PDOStatement $pdo_stmt)
{
    // this will return false if no rows or not found...
    $result = $pdo_stmt->fetchColumn();
    if (empty($pdo_stmt->errorInfo())) {
        return $result !== false ? $result : null;
    }
    return false;
}

Note that fetchColumn has a similar issue.

The above will return;

  • false on query failure
  • an empty array for fetch if no rows found
  • appropriate result set for fetch if found
  • null for fetchColumn if not found
  • column value for fetchColumn if found

Scanning your code sample, you could implement like so;

$sql  = 'SELECT * FROM users WHERE name = :name';
$stmt = $connection->prepare($sql);
$stmt->bindValue(':name', 'Sarah');
$executed = $stmt->execute();

if (!$executed) {
    throw new UnexpectedValueException('The prepared statement can not be executed!');
}

$result_set = fetch($stmt);
if ($result_set === false) {
    throw new UnexpectedValueException('Fetching data failed!');
}
// handle result set

There is more you can do to improve the above, but hopefully you get the idea.

user2430012
  • 106
  • 1
  • 5
  • Your post does not answer the question, nor the previous versions of the question. – pucky124 Sep 21 '17 at 20:30
  • My post was in response to his comments in the question above. Specifically, the last 2 he made. – user2430012 Sep 21 '17 at 20:34
  • @user2430012 Thank you very much for your answer! In the context of the `fetch()` problem (and of my last 2 comments) I, personally, consider your answer an eligible one. –  Sep 21 '17 at 22:32
  • @user2430012 Now, based on a 3rd version of my question: [Does PDO fetch() method throw an exception on failure?](https://stackoverflow.com/questions/46345649/does-pdo-fetch-method-throw-an-exception-on-failure), I tend to 99% consider, that there are no failure situations in the case of `fetch()` :-) I really appreciate your effort for giving me a good answer! –  Sep 21 '17 at 22:34
  • 1
    You're welcome. To add, fetch() does not throw an exception no matter what PDO::ATTR_ERRMOD is used. It will always return false on failure. Even if you wrap your stmt in a try/catch block, and call fetch() after, it will still return false. fetchAll() will return false on failure also, but I've never actually got it to return false. Even in this comment's example, it will return an empty array. – user2430012 Sep 21 '17 at 22:41
  • @user2430012, that is not at all what the documentation says. Do you have any data to backup your statement: fetch() does not throw an exception no matter what PDO::ATTR_ERRMOD is used. Also, you're correct that fetch (or any other method) could throw an exception and still return false if you designed your try/catch logic to do that. I've edited my answer to be more clear on that point. – pucky124 Sep 21 '17 at 23:20
  • @user2430012 Please allow me to answer to you tomorrow, because I'm very tired right now. Thanks, I appreciate. –  Sep 21 '17 at 23:43
  • @pucky124 You're right, I should have been clearer. I was referring to the use of PDO::FETCH_ASSOC, as that is what he was using. FETCH_CLASS, FETCH_INTO, and a few others can throw exceptions. As far as using FETCH_ASSOC, it always returns false. I'll update the comment...or not...guess I can't update it – user2430012 Sep 21 '17 at 23:57
  • @user2430012 Hi :-) Until today I was confused. As you, I tended to consider, that `fetch()` throws no exceptions, but only returns `FALSE`. Well, I finally found a case, which allowed me simulate the failure situation of `PDOStatement::fetch()`. Conclusion: `fetch()` THROWS EXCEPTIONS! I posted an answer for it. Thanks again for everything! :-) –  Sep 22 '17 at 14:32
  • It depends on the fetch_style actually. Using PDO::FETCH_ASSOC, it does not. But using, lets say PDO::FETCH_CLASS, it can throw an exception for a missing class name or class not found. You can see it here in the extension -> https://github.com/php/php-src/blob/dce52dd963824c693cd44ed07c7e7fca1b611c5f/ext/pdo/pdo_stmt.c#L924 – user2430012 Sep 22 '17 at 15:17
  • I just saw your answer above. Searching for "pdo_raise_impl_error" in the extension will show different cases where an exception may be thrown by PDOStatement -> https://github.com/php/php-src/blob/master/ext/pdo/pdo_stmt.c – user2430012 Sep 22 '17 at 15:21