Call to a member function fetchAll() on boolean - php

I have the following problem.
This error persisting in accompanying me
Fatal error: Uncaught Error: Call to a member function fetchAll() on boolean in C:\xampp\htdocs\certificado\functions.php:49 Stack trace: #0 C:\xampp\htdocs\certificado\index.php(11): get_info_from_email('amanda_pandoka#...') #1 {main} thrown in C:\xampp\htdocs\certificado\functions.php on line 49
In the code below I can not understand the error. Can someone help me?
function connect() {
$socket = new PDO('mysql:host=' . #$host . ';dbname=' . #$nomedobancodedados,
#$usuario, #$senha);
$socket->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $socket;
}
function get_info_from_email($email) {
if (!$email)
return false;
global $db;
$sql = "
SELECT
id,
name,
email,
type,
data,
file
FROM
attendee
WHERE 1=1
AND email = '{$email}'
";
if ($info = $db->query($sql))
return false;
if ($info = $info->fetchAll())
return false;
return $info;
}

if ($info = $db->query($sql))
return false;
This says: if the result of $db->query($sql) can be stored in $info and is not something like false, null or an empty string or array, stop now and return false. So basically, if your query executes successfully and properly returns a PDOStatement with results in it, your function stops here.
if ($info = $info->fetchAll())
return false;
This is where you're getting the error. The fact that this code is reached at all means that the query failed to execute (otherwise, it would have returned false earlier). So basically, you're calling fetchAll() on false. Try to see what the error is here (do a print_r($db->errorInfo()); before this if-statement)
By the way, this if-statement will also cause your function to return false if the fetchAll() call was successful, which is probably not what you want. Additionally, by using $db->query() directly with the email address provided in the function call, you're leaving your code open to possible SQL injection attacks. As a rule, never trust any variable if you don't have 100% control over what's in it. You should use a prepared statement instead.
As another rule, always use curly braces on code blocks (if/elseif/else, for/foreach/while/do, try/catch/finally), because then you don't need to think about them anymore if you someday decide that the code block should do two things instead of one, and it's easier to debug code if you can visually see exactly what your code is trying to do.
This code (not tested) should work the way you want:
function get_info_from_email($email) {
if (!$email) {
return false;
}
global $db;
$stmt = $db->prepare("
SELECT
id,
name,
email,
type,
data,
file
FROM
attendee
WHERE 1=1
AND email = :email
");
// return false if the query cannot be executed
if (!$stmt->execute(array(':email' => $email))) {
return false;
}
// return false if there was an **error** retrieving the query results
if (($info = $stmt->fetchAll()) === false) {
return false;
}
return $info;
}

Continue like this
function get_info_from_email($email) {
if (!$email) {
return false;
}
global $db;
$sql = $db->prepare("
SELECT
id,
name,
email,
type,
data,
file
FROM
attendee
WHERE 1=1
AND email = :email
");
print_r($db->errorInfo());
// return false if the query cannot be executed
if (!$sql->execute(array(':email' => $info))) {
return false;
}
// return false if there was an **error** retrieving the query results
if (($info = $sql->fetchAll()) === false) {
return false;
}
return $info;
}

Related

mysqli::$error contains no error message if I move its access too far away in return chain

As per the documentation at https://www.php.net/manual/en/mysqli.error.php I expect an empty string to be indicative of there being no error. All my classes are using a database object that gets passed around each and retrieved by this class method getConn:
public function getConn() {
return $this->_conn;
}
And the database class contains exactly:
public function __construct() {
$this->_conn = new mysqli(self::$host, self::$user, self::$password, self::$schema);
$this->_conn->set_charset(self::$charset);
...
}
...
The destructor indicates that it is the last thing to destruct and this is where the connection is forcibly closed.
My API is invoked in a way which results in this class method executing in file1.php which in turn calls create on $this->_p:
class PAPI {
public function __construct(APPDB $DB) {
$this->DB = $DB->getConn();
$this->_p = new APPP($DB);
...
}
...
private function _newC() {
...
for ($i = 0; $i < $numFiles; $i ++) {
if ($_FILES['p']['size'][$i] > 0) { /// files not guaranteed to be present
if (($res = $this->_p->create($_FILES['p']['tmp_name'][$i], $cid, $uid)) !== APPP_STATUS::APPP_SUCCESS) {
switch ($res) {
...
case APPP_STATUS::APPP_SQLERR:
APPCore::Log(json_encode($this->DB->error));
return new PAPIError(PAPI_ERROR::PAPISQLERR, PAPI_ERROR_MSG[PAPI_ERROR::PAPISQLERR], $this->DB->error);
...
}
} else {
...
}
}
}
...
}
...
}
create is seen here in the second class in file2.php, it calls setP on $this->_c:
class APPP {
public function __construct(APPDB $DB) {
$this->DB = $DB->getConn();
$this->_c = new APPC($DB);
...
}
...
public function create(string $tmp = null, int $cid = null, int $uid = null, string $table = null) {
...
if ($this->_c->setP($cid, $hash, $table) === APPC_STATUS::APPC_SUCCESS) {
...
} else {
APPCore::Log(json_encode($this->DB->error));
return APPP_STATUS::APPP_SQLERR;
}
}
}
And setP is seen here finally in file3.php:
class APPC {
public function __construct(APPDB $DB) {
$this->DB = $DB->getConn();
}
...
public function setP(int $id = null, string $hash = null, string $table = null) {
...
$target = self::_tableFromHash($table);
$params = [
json_encode([$hash]),
$id
];
$updateCQuery = $this->DB->prepare("UPDATE `${target}` SET `p` = ? WHERE id = ?");
/*!< since table is an argument, this could fail if the table doesn't exist */
if ($updateCQuery === false)
return APPC_STATUS::APPC_SQLERR; /*!< this is where it the error propagates out from */
...
}
}
It is expected that APPC_STATUS::APPC_SQLERR should be returned from the final line included in the snippet - this occurs and it is seen by file2.php. In file2.php the APPCore::Log(json_encode($this->DB->error)); succeeds if it is left uncommented, and the error makes it out to the log. Note that this has crossed execution over one return from another file. If I instead return APPP_STATUS::APPP_SQLERR; in file2.php and log from the switch in file1.php instead of the successful position that would otherwise be left in file2.php, then the value of $this->DB->error is an empty string.
Why is it empty when the only path the execution has taken, is a return into a switch statement? I have verified that only one instance of the database class exists and the a server disconnection is only made much after the empty mysqli::$error is observed. I.e., I cannot find anywhere where the the mysqli object performs any tasks between returning, and accessing its error property.
I have verified with mysql's general_log, that the last query is indeed the last query and that a quit is made very shortly after.
2020-09-14T06:36:06.025182Z 10 Prepare INSERT INTO `p` (`code`, `uid`, `cid`, `private`, `created`, `modified`) VALUES(?, ?, ?, ?, ?, ?, NOW(), NOW())
2020-09-14T06:36:06.025222Z 10 Execute INSERT INTO `p` (`code`, `uid`, `cid`, `private`, `created`, `modified`) VALUES('c288b073d41f26753a937d02056a14864ae1959d', NULL, 22353, NULL, NOW(), NOW())
2020-09-14T06:36:06.029293Z 10 Close stmt
2020-09-14T06:36:06.030761Z 10 Quit
I thought it worth pointing out that although referring to the mysqli::$error property doesn't consume it, that I am shifting the reference to it from file2.php to file1.php where it makes more sense in the surrounding software.
What is happening and how do I prevent it happening such that I can access the error appropriately?
Debugging shows the following sequence:
The differences between each of these images are two steps 1 to 2, call $this->DB->prepare(...); and return because of the error and each of the other steps are single steps (F11 in VSCode with XDebug server).
Regarding your actual problem: there is no magic. Either there is a new connection is created somewhere, or there is another successful query is called, thus nullifying the error message.
Either way, that's a minor issue compared to that outdated method of checking errors you are using. It's 2020 now and time to use exceptions. Seriously, 90% of this code will just disappear in a puff of smoke.
Just tell mysqli to throw exceptions. Now, you can start writing your code straight away, without a single if statement intended for the error checking.
Let me to recreate the full elaborate code for a single query execution:
$updateCQuery = $this->DB->prepare("UPDATE `${target}` SET `p` = ? WHERE id = ?");
if ($updateCQuery === false)
return APPC_STATUS::APPC_SQLERR;
}
$updateBind = $updateCQuery->bind_param("ss", $hash, $id);
if ($updateBind === false)
return APPC_STATUS::APPC_SQLERR;
}
$updateExecute = $updateCQuery->execute();
if ($updateExecute === false)
return APPC_STATUS::APPC_SQLERR;
}
and compare this monster of a code with the following code snippet:
$updateCQuery = $this->DB->prepare("UPDATE `${target}` SET `p` = ? WHERE id = ?");
$updateCQuery->bind_param("ss", $hash, $id);
$updateCQuery->execute();
Moreover, the same goes for all the levels above. There is no need for the elaborate error checking in the other files either
if ($this->_c->setP($cid, $hash, $table) === APPC_STATUS::APPC_SUCCESS) {
...
} else {
APPCore::Log(json_encode($this->DB->error));
return APPP_STATUS::APPP_SQLERR;
}
will become just
$this->_c->setP($cid, $hash, $table);
and
if (($res = $this->_p->create($_FILES['p']['tmp_name'][$i], $cid, $uid)) !== APPP_STATUS::APPP_SUCCESS) {
switch ($res) {
...
case APPP_STATUS::APPP_SQLERR:
APPCore::Log(json_encode($this->DB->error));
return new PAPIError(PAPI_ERROR::PAPISQLERR, PAPI_ERROR_MSG[PAPI_ERROR::PAPISQLERR], $this->DB->error);
...
}
will become just
$res = $this->_p->create($_FILES['p']['tmp_name'][$i], $cid, $uid);
but surely you would like to know where to put that logging and returning code. And I have an answer for you: in the error/exception handler. Just create a single place where all errors being logged and the appropriate message is returned to the client. A simple exception handler would catch all errors that would ever occur, and handle them in a single place in the uniform manner, without the need of writing the elaborate error checking code on the every level.

PHP PDO: Call to a member function fetch() on boolean

So I'm currently trying to do a PDO SELECT Request, but when executing and fetching the extracted data, this error shows up:
1 - Fatal error: Uncaught Error: Call to a member function fetch() on boolean in C:\wamp64\www\NewKali\includes\user.inc.php on line 53
2 - Error: Call to a member function fetch() on boolean in C:\wamp64\www\NewKali\includes\user.inc.php on line 53
This is where I call the function:
include 'includes/user.inc.php';
$userOBJ = new User;
if($userOBJ->isAdmin($_SESSION['session_u-name'])){
AdminControl();
}
Code:
public function isAdmin($user){
$userToGet = $user;
$stmt = $this->Connect()->prepare("SELECT * FROM user_secure WHERE username_db=?");
$query1 = $stmt->execute([$userToGet]);
if(!$query1)
{
die("Execute query error, because: ". print_r($this->Connect()->errorInfo(),true) );
}else{
foreach ($query1->fetch() as $row) {
if($row['admin_db'] == 1){
return true;
} else {
return false;
}
}
}
}
The first error says that I'm not handling the PDO errors, which I think that I'm already handling any PDO error in my code, but somehow still gets detected as I'm not doing so... (Correct me if wrong)
Second error states that calling PDO->fetch() is returning a boolean, but I'm requesting data, so it's not able to continue with the following code...
I don't get why this is showing... The "username_db" var in the query is the same as the one that I have in my DB.
In the same file as the function above, I have this next function and when called, it does fine
public function RegisterUser($user, $pwd, $mail){
$u_Insert = $user;
$p_Insert = $pwd;
$m_Insert = $mail;
$stmt = $this->Connect()->prepare("INSERT INTO user_secure(username_db, password_db) VALUES (?,?)");
$query1 = $stmt->execute([$u_Insert, $p_Insert]);
$stmt = $this->Connect()->prepare("INSERT INTO user_info(mail_db) VALUES (?)");
$query2 = $stmt->execute([$m_Insert]);
if($query2 && $query1){
return true;
} else {
return false;
}
}
Is there something that I'm missing?
I have already checked this thread but I'm still in the exact position...
Thank you for your time
(I'm still learning PDO, sorry if my code isn't clean)
This line here is one of the reasons. execute returns true or false indicating if the query succeeded or failed.
$query1 = $stmt->execute([$userToGet]);
In a sense, $query1 is a boolean.
Now in these lines, you are trying to access the fetch method from $query1 which is a boolean.
foreach ($query1->fetch() as $row) {
if($row['admin_db'] == 1){
return true;
} else {
return false;
}
}
To get the row, you need to write it like this:
$results = $stmt->fetch();
or in your case:
foreach ( $stmt->fetch() as $row) {
if($row['admin_db'] == 1){
return true;
} else {
return false;
}
}

PHP PDO CRUD class Call to a member function rowCount() on a non-object

I'm just getting started using PDO to move away from mysqli but hit a problem. I'm following a tutorial and I want to return an array from the database but I get the following error:
Fatal error: Call to a member function rowCount() on a non-object in C:\xampp\htdocs\phptuts\crud\core\managedb.class.php on line 27
Here is my managedb.php class:
<?php
class ManageDatabase
{
public $link;
function __construct()
{
include_once('database.class.php');
$conn = new database;
$this->link = $conn->connect();
return $this->link;
}
function getData($table_name, $id=null)
{
if(isset($id))
{
$query = $this->link->query("SELECT * FROM $table_name WHERE id = '$id' ORDER BY id ASC");
}
else
{
$query = $this->link->query("SELECT * FROM $table_name ORDER BY id ASC");
}
$rowCount = $query->rowCount();
if($rowCount >= 1)
{
$result = $query->fetchAll();
}
else
{
$result = 0;
}
return $result;
}
}
Then I'm simply using the following code to try and get a response:
<?php
include_once('../core/managedb.class.php');
$init = new ManageDatabase;
$table_name = 'users';
$data = $init->getData($table_name);
print_r($data);
This is when I get the error, Any ideas?
I'd var_dump($query) before the $rowCount = $query->rowCount(); line to see what it actually is, because apparently it's not an object. I'm guessing it's either NULL or empty because the whole $this-link->query(<sql statement>); didn't return what you expected
A couple of things to check out:
From the PHP manual:
PDO::query() returns a PDOStatement object, or FALSE on failure.
You'll want to test if the query succeed and if not, why. You can check the error using PDO's errorInfo function:
if ($query == false)
{
print_r($this->link->errorInfo());
exit();
}
Another thing to note is that rowCount() in PDO returns the affected rows from a INSERT / UPDATE / DELETE type statement. For a SELECT you may get a row count, or you may not. The manual suggests a separate query to find the number of rows, but in your instance it might be easier testing if you get anything back from fetchAll():
$result = $query->fetchAll();
if (!empty($result))
{
return $result;
}
else
{
return 0;
}

ADODB / SQL Server mssql_execute stored procedure fails but works if debug is set to true

I am connecting to a SQL Server database from PHP using ADODB. I have several stored procedures that I have been executing just fine. The last call that I have created for some reason keeps failing. I put debug as true and it works just fine. I have no idea why this would be happening.
here is the error I get
Error: mssql_execute() [<a href='function.mssql-execute'>function.mssql-execute</a>]: stored procedure execution failed in /path/to/adodb/lib/adodb5/drivers/adodb-mssql.inc.php on line 768
Here is the method I created to pass all my stored procedures to, like I said it works fine with other stored procedures but this one and I have double checked that all the spellings of the parameters and stored procedure are correct.
protected function _getSpArray($db, $sp, array $spParams) {
try {
$stmt = $db->PrepareSP($sp);
foreach ($spParams as $param) {
$db->InParameter($stmt, $param[0], $param[1]);
}
$db->SetFetchMode(ADODB_FETCH_ASSOC);
$rs = $db->Execute($stmt);
$rsArray = array();
if (!$rs) {
echo 'No records found \n';
return;
}
else {
foreach ($rs as $k => $row) {
$rsArray[$k] = $row;
}
}
return $rsArray;
} catch (ErrorException $e) {
echo $e->getMessage();
}
}
It is failing on this line #768 of adodb5/drivers/adodb-mssql.inc.php
$rez = mssql_execute($sql[1]);
and the sql array has these values
[0] stored_procedure_name
[1] resource id='657' type='mssql statement'
I have seen the following comments on PHP.net, I changed my freetds host name to be something different then the IP address and still nothing. I am not sure about the free result since I am using adodb.
http://www.php.net/manual/en/function.mssql-execute.php#93938
I am using adodb 5.11
When you set debug as true ADODB uses other function to execute the statement. In this case function _adodb_debug_execute(&$zthis, $sql, $inputarr)
If debug is set to false ADODB uses function &_Execute($sql,$inputarr=false). When you check the source for both methods you can clearly see the difference.
<?php
function _adodb_debug_execute(&$zthis, $sql, $inputarr)
{
//ADODB prepares debug information dump...
$qID = $zthis->_query($sql,$inputarr);
//Here ADODB makes the difference
if ($zthis->databaseType == 'mssql') {
// ErrorNo is a slow function call in mssql, and not reliable in PHP 4.0.6
if($emsg = $zthis->ErrorMsg()) {
if ($err = $zthis->ErrorNo()) ADOConnection::outp($err.': '.$emsg);
}
} else if (!$qID) {
ADOConnection::outp($zthis->ErrorNo() .': '. $zthis->ErrorMsg());
}
if ($zthis->debug === 99) _adodb_backtrace(true,9999,2);
return $qID;
}
?>
Here is the _Execute function
<?php
function &_Execute($sql,$inputarr=false){
//Here ADODB chooses which fn to use
if ($this->debug) {
global $ADODB_INCLUDED_LIB;
if (empty($ADODB_INCLUDED_LIB)) include(ADODB_DIR.'/adodb-lib.inc.php');
$this->_queryID = _adodb_debug_execute($this, $sql,$inputarr);
} else {
$this->_queryID = #$this->_query($sql,$inputarr);
}
//...
if ($this->_queryID === false) { // error handling if query fails
//If debug ADODB prints backtrace regardless the result
if ($this->debug == 99) adodb_backtrace(true,5);
$fn = $this->raiseErrorFn;
if ($fn) {
$fn($this->databaseType,'EXECUTE',$this->ErrorNo(),$this->ErrorMsg(),$sql,$inputarr,$this);
}
$false = false;
//Returns false no matter what...
return $false;
}
//...
}
?>
Try adding this to your script to test the behaviour of the script and keep in mind that if the execute fails it will return a false value. So take care with the returned value.
protected function _getSpArray($db, $sp, array $spParams) {
try {
$stmt = $db->PrepareSP($sp);
foreach ($spParams as $param) {
$db->InParameter($stmt, $param[0], $param[1]);
}
$db->SetFetchMode(ADODB_FETCH_ASSOC);
$rs = $db->Execute($stmt);
$rsArray = array();
if (!$rs) {
echo 'No records found \n';
return;
}
else {
foreach ($rs as $k => $row) {
$rsArray[$k] = $row;
}
}
//add this line to free the resources after use.
mssql_free_result($rs);
return $rsArray;
} catch (ErrorException $e) {
echo $e->getMessage();
}
}
Hope it helps!!
Ok, after finding the php.net comments, even though my freetds wasn't setup with the proper name, I was using the ip address. I also wasn't using the V 8.0 I was using 7.0 and everything was working fine up until this point with the store procedures. I tried the mssql_free_result solution provide in the comments and DarkThrones answer and that didn't work but when I used adodb's close() method it is working. It uses free result and passes a query id.
If someone can comment as to why this is necessary that would be great. Is it because I was using up to much memory or something?

PHP: How can I check for errors during a mysqli commit?

I am inserting a lot of records using mysqli commit statement, using this code from http://www.php.net/manual/en/mysqli.commit.php#88857. I then check affected rows using:
$mysqli->affected_rows
but even though all records have been inserted, I get zero affected rows.
How can I check when commit failed and retrieve the error?
Thank you
You could do something like this:
mysqli_autocommit($dbconn, FALSE);
$errors = array();
if (!$mysqli->query(/* some SQL query */)) {
$errors[] = $mysqli->error;
}
// ... more queries like the above
if(count($errors) === 0) {
$mysqli->commit()
} else {
$mysqli->rollback();
print_r($errors);
}
When a query goes wrong, it will add the error to the $errors array, so you will know what went wrong. You could also add keys with identifiers for the queries, so you know which query went wrong.
For better handling, you could write a UnitOfWork class for this:
class UnitOfWork
{
protected $_db;
protected $_errors;
protected $_queries;
public function __construct($db) {
$this->_db = $db;
$this->_errors = array();
$this->_queries = array();
}
public function addQuery($id, $sql) {
$this->_queries[$id] = $sql;
return $this;
}
public function getErrors() {
return $this->_errors;
}
public function try() {
$this->_db->autocommit($this->_db, FALSE);
foreach($this->_queries as $id => $query) {
if ($this->_db->query($query) === FALSE) {
$this->_errors[$id] = $this->_db->error;
}
}
$hasErrors = count($this->_errors);
($hasErrors) ? $this->_db->rollback() : $this->_db->commit();
$this->_db->autocommit($this->_db, TRUE);
return !$hasErrors; // return true on success
}
}
and you could use it like
$unit = new UnitOfWork($mysqli);
$unit->addQuery('foo', 'SELECT foo FROM somewhere')
->addQuery('bar', 'SELECT bar FROM somewhereElse')
->addQuery('baz', 'SELECT baz WITH brokenQuery');
if($unit->try() === FALSE) {
print_r($unit->getErrors());
}
mysqli::affected_rows will return the number of rows affected by the last MySQL operation.
If you are doing something like this (pseudo-code) :
$db->query("insert ...");
$db->query("insert ...");
$db->query("insert ...");
$db->commit();
$num = $db->affected_rows();
You will not get the number of inserted rows : the commit instruction is the last executed one, and it doesn't "affect" any row.
If you want to know whether mysqli::commit succedeed or not, you should check it's return value (quoting) :
Returns TRUE on success or FALSE on
failure.
If it returned true, then all your previous inserts, since the beginning of the current transaction, will have been commited.
And if an error occured, you can use mysqli::errno and/or mysqli::error to get informations about it.

Categories