-1

I've been scratching my head over this for a few days now and I've still got nowhere. I'm trying to pull a small set of values from a MySQL database via PHP PDO; I know PDO works as I am using it else where and I have based my code around te previously working code.

function custom_sub_cat_list($db_details, $cat_id) { //ln21
$subcat = NULL;
try {
    $h = new PDO("mysql:host=".$db_details['host'].";dbase=".$db_details['db'],$db_details['user'],$db_details['pass']);
    $h->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch(PDOException $ex) {
    return false;
}
try {
    $q = $h->prepare("SELECT category FROM :tbl WHERE parentid = :catid;");
    $q->bindValue(":tbl", $db_details['tbl'], PDO::PARAM_STR);
    $q->bindValue(':catid', $cat_id, PDO::PARAM_STR);
    $q->execute();
    while($row = $_query->fetch()) {
        $subcat['id'][] = $row['categoryid'];
        $subcat['name'][] = $row['category'];
    };
    return $subcat;
} catch(PDOException $ex) {
    return false;
}
}//ln49

I am getting "Call to a member function bindValue() on a non-object" on the bindValue's and being called up like below.

$cat_id     = 123;
$db_details = array(
    "host"  => $sql_host,
    "db"    => $sql_db,
    "user"  => $sql_user,
    "pass"  => $sql_password,
    "tbl"   => $sql_tbl['categories']
);
custom_sub_cat_list ($db_details, $cat_id)

I'm sure it's something glaringly obvious but I can't see the problem and would like a fresh pair of eyes.

WORKING VERSION BELOW

Thank You Very Very Much! to everyone who helped, I've learnt a few bits too :-) There was some silly mistakes in there that I had overlooked, I just blame looking at it for two days solid.

function custom_sub_cat_list($db_details, $cat_id) {
$subcat = NULL;
try {
    $h = new PDO("mysql:host=".$db_details['host'].";dbname=".$db_details['db'].";charset=utf8",$db_details['user'],$db_details['pass']);
    $h->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $h->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $q = $h->prepare("SELECT category, categoryid FROM ".$db_details['table']." WHERE parentid = :cid;");
    $q->bindParam(':cid', $cat_id, PDO::PARAM_INT);
    $q->execute();
    while($row = $q->fetch()) {
        $subcat['id'][] = $row['categoryid'];
        $subcat['name'][] = $row['category'];
    };
    $h = NULL;
    return $subcat;
} catch(PDOException $ex) {
    print_r($ex->getMessage());
    print_r($ex->getTrace());
    //return false;
}
}
Adam
  • 17
  • 4

3 Answers3

1

You don't need a fresh pair of eyes

You are not painter but a programmer (supposedly).
So, instead of watching your code you have to run it. And enable error reporting.

Oh, just spotted it
And of course, you shouldn't gag error messages!

} catch(PDOException $ex) {
    return false;
}

a modern version of @ operator.
Please get rid of ALL try..catch blocks in your code and start using them only after learning what are they for.

So, in order to solve this very problem as well as many other problems in the future

  1. Get rid of all try..catch blocks in your code.
  2. Enable error reporting for PDO as described in tag wiki I linked to in the comments.
  3. Do not use placeholders for the identifiers but format them as described in the tag wiki I linked to
  4. Turn off display_errors setting if you don't want errors to be displayed (the only reason for suppressing error messages I can think of).

Also, you shouldn't open separate connection in every function call.
Create one connection in the beginning of your script and then use if in the function, using

global $h;
Community
  • 1
  • 1
Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • I am; I am getting the error 'PHP Fatal error: Call to a member function bindValue() on a non-object on line 31' – Adam Mar 08 '13 at 15:26
  • Even without the try..catch it still has a fatal error; error messages are suppressed for a reason. – Adam Mar 08 '13 at 15:30
0
$q = $h->prepare("SELECT category FROM :tbl WHERE parentid = :catid;");

You can't use a bound parameter for a table name in SQL.

When you try to prepare an invalid SQL statement like this, prepare() returns false, not a PDOStatement object.

$q->bindValue(":tbl", $db_details['tbl'], PDO::PARAM_STR);

Then you try to call a bindValue() method on $q, but the scalar value false has no methods, so it's a fatal error.

You're also trying to catch exceptions, but you didn't configure PDO to throw exceptions on error. So the PDO functions default to reporting errors simply by returning false.

As @Arkh shows, you need to tell PDO to throw exceptions:

$h->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Bill Karwin
  • 538,548
  • 86
  • 673
  • 828
-1

So, your error tells you that $q is not an object. Which means that the $h->prepare("SELECT category FROM :tbl WHERE parentid = :catid;"); failed.

When reading the PDO::prepare doc, you can read this:

Return Values

If the database server successfully prepares the statement, PDO::prepare() returns a PDOStatement object. If the database server cannot successfully prepare the statement, PDO::prepare() returns FALSE or emits PDOException (depending on error handling).

As you have not set PDO to throw exceptions, you should either do it to get a good error message or check the value of $q and get the last reported error. First method:

function custom_sub_cat_list($db_details, $cat_id) { //ln21
$subcat = NULL;
try {
    $h = new PDO("mysql:host=".$db_details['host'].";dbase=".$db_details['db'],$db_details['user'],$db_details['pass']);
    $h->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $h->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $q = $h->prepare("SELECT category FROM :tbl WHERE parentid = :catid;");
    $q->bindValue(":tbl", $db_details['tbl'], PDO::PARAM_STR);
    $q->bindValue(':catid', $cat_id, PDO::PARAM_STR);
    $q->execute();
    while($row = $_query->fetch()) {
        $subcat['id'][] = $row['categoryid'];
        $subcat['name'][] = $row['category'];
    };
    return $subcat;
} catch(PDOException $ex) {
    echo $ex->getMessage();
    return false;
}
}//ln49

Which gives the error:

SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? WHERE parentid = ?' at line 1

Second method:

function custom_sub_cat_list($db_details, $cat_id) { //ln21
$subcat = NULL;
    $h = new PDO("mysql:host=".$db_details['host'].";dbase=".$db_details['db'],$db_details['user'],$db_details['pass']);
    $h->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    $q = $h->prepare("SELECT category FROM :tbl WHERE parentid = :catid;");
    if ($q === false) {
        print_r($h->errorInfo());
        return false;
    }
    $q->bindValue(":tbl", $db_details['tbl'], PDO::PARAM_STR);
    $q->bindValue(':catid', $cat_id, PDO::PARAM_STR);
    $q->execute();
    while($row = $_query->fetch()) {
        $subcat['id'][] = $row['categoryid'];
        $subcat['name'][] = $row['category'];
    };
    return $subcat;
}//ln49

Result:

Array ( [0] => 42000 1 => 1064 [2] => You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? WHERE parentid = ?' at line 1 )

Your problem is that you cannot use a bound value for :tbl (or other non values parts of your SQL string). So you have to do this:

$q = $h->prepare("SELECT category FROM ".$db_details['tbl']." WHERE parentid = :catid;");
Community
  • 1
  • 1
Arkh
  • 8,416
  • 40
  • 45
  • 1. `try catch` for displaying errors should never be used. 2. Same goes for the connect in the every function. 3. identifiers have to be properly formatted before adding them to the query. – Your Common Sense Mar 08 '13 at 15:51
  • Yes, try catch should be use to do more meaningful things. But I like to explain to people how to get to the first error they get. And well, error messages are good at that. – Arkh Mar 08 '13 at 15:55