This question already has answers here:
What to do with mysqli problems? Errors like mysqli_fetch_array(): Argument #1 must be of type mysqli_result and such
(1 answer)
MySQLi prepared statements error reporting [duplicate]
(3 answers)
Closed 4 years ago.
I am looking for a way to build a handler or edit a php object, the following is an example for mysqli
I have a system with many files using the mysqli object in a variable
$my_var=new mysqli($host, $user, $pass, $base);
Files call this to do queries and get the results like this:
$q=$my_var->query("SELECT * FROM table");
$q->fetch_assoc();
if there is an error on the query, $my_var->error will be populated on the first line and will throw an error 500 on the second line.
I am looking for a way to build a handler for $my_var->error and throw the error before any fetch_* method is called, so,
is there any way to build a handler/listener for $my_var->error?
if this is not possible, is there any way to override mysqli fetch_assoc(), fetch_row() and fetch_array() methods to check if $my_var->error is true and show the error before continue?
I know about try{}catch(), throw new Exception and or die() methods, but these mean to edit every fetch_* in the system, I would like to do it editing the connection file only.
Thank you!
---editing---
I think prepared statements are not what I am looking for.
---editing 2---
And no, I am not looking how to get mysqli errors.
Thank you for your help fyrye!
---Answer Final Code---
class mysqli2{
private $c;
function __construct(...$args){ // open connection
$this->c=new mysqli(...$args);
if(!empty($this->c->error)){ // check for errors
echo("<script>console.error(\"MySQL: ".$this->c->error."\");</script>");
}
}
function query($query, $resultmode = MYSQLI_STORE_RESULT){
$r=$this->c->query($query, $resultmode); // make query
if(!empty($this->c->error)){ // check for errors
echo("<script>console.error(\"MySQL: ".$this->c->error."\");</script>");
}
return $r; // returns query results
}
function __call($method, $args){ // calls others mysqli methods
return $this->c->$method(...$args);
}
function __get($name){ // get all the properties
return $this->c->$name;
}
function __set($name, $value){ // set all the properties
if (property_exists($this->c, $name))$this->c->$name = $value;
}
}
To suggest a best practice, when using either PDO or MySQLi extensions, it is suggested to always check the return results from any of the usable methods, before moving on to a method that relies on its result.
As mysqli::query returns false on an error, it should be checked before using fetch_assoc, instead of assuming it is not false. The same applies to using fetch_assoc, as it can return NULL;
if (!$q = $my_var->query($sql)) {
//something went wrong - handle the error here.
}
if ($data = $q->fetch_assoc()) {
echo $data['column'];
}
However as I suggested, you would need to create an abstraction layer.
Since $q would be false on error, the exception from the code in your question would be:
Fatal error: Call to a member function fetch_assoc() on boolean
Meaning you would not be able to override the fetch_* methods, without overriding mysqli::query to always return an object.
Please do not use in production code.
This is only an example of how to override the mysqli::query method with your own. You would also need to override all other mysqli::*** methods, like prepare, commit, etc.
Example https://3v4l.org/PlZEs
class Conn
{
private $conn;
public function __construct(...$args)
{
$this->conn = new mysqli(...$args);
}
public function query($query, $resultmode = \MYSQLI_STORE_RESULT)
{
$d = $this->conn->query($query, $resultmode);
if (!empty($this->conn->error)) {
throw new \RuntimeException($this->conn->error);
}
return $d;
}
}
//Your connection code
$con = 'my_var';
$$con = new Conn('host', 'user', 'pass', 'base');
//example usage of valid query
$q = $my_var->query('Valid Query');
$q->fetch_assoc();
//example use of invalid query throwing an exception before `fetch_assoc`
$q = $my_var->query('This is not valid');
$q->fetch_assoc();
Results
I was successful
-----
Fatal error: Uncaught exception 'RuntimeException' with message 'Expected "Valid Query"' in /in/PlZEs:51
Stack trace:
#0 /in/PlZEs(70): Conn->query('This is not val...')
#1 {main}
thrown in /in/PlZEs on line 51
This approach uses PHP 5.6 argument packing and unpacking, to match the function call arguments, to prevent having to manually define them.
It is possible to expand on this to write a message to log file, send an email, trigger an event, or display a friendly error message instead of throwing an exception. As well as overriding the mysqli_stmt responses with your own Statement object(s).
Related
I have cerb2 https://github.com/wgm/cerb2, it is an old ticketing system, it might looks weird but I have a a lot of knowledgeable information burried that I want to exploit.
It is basically a PHP5/Mysql software with the mysql_connect() constructor
The main issues are queries that stay null at all times. The code base lays on Class definition and pseudo-variable $this to call a query method from another Class.
function CER_HASH_QUEUES(&$parent)
{
global $session;
global $queue_access; e();
$this->db = (new cer_Database())->getInstance();
$this->_parent = &$parent;
if(empty($queue_access))
$this->queue_access = new CER_QUEUE_ACCESS();
else
$this->queue_access = $queue_access;
$sql = "SELECT q.queue_id, q.queue_name FROM queue q ORDER BY q.queue_name";
$res = $this->_db->query($sql);
That last line ($res =… ) is stopping code execution with:
Fatal error: Call to a member function query() on null in
The Query method called from cer_Database.class looks like this ( https://raw.githubusercontent.com/wgm/cerb2/stable/cerberus-api/database/cer_Database.class.php )
function query($sqlString,$return_assoc=true)
{
$config_db = (new cerConfiguration())->getInstance();
if($return_assoc === true) $this->db->SetFetchMode(ADODB_FETCH_ASSOC);
else $this->db->SetFetchMode(ADODB_FETCH_NUM);
$res = $this->db->Execute($sqlString);
if($cfg->settings["debug_mode"]) {
$time_end = microtime();
$query_time = microtime_diff($time_end,$time_start) * 1000; // convert secs to millisecs
echo "<b>[CERBERUS QUERY]:</b> " . $sqlString . " (Ran: <b>" . sprintf("%0.3f",$query_time) . "ms</b>)<hr>";
}
return $res;
}
That last method depends on a second Class cerConfiguration() which relies on import_config_db() to construct the mysql connector.
I am unable to figure out how to pass successfully my sql request following the design pattern carried by the relevant software here.
The databases and the tables are OK on their sides, and the following php script is returning the sql query OK.
<?php
include('includes/third_party/adodb/adodb.inc.php');
$db = ADONewConnection('mysql');
$db->Connect("localhost", "user", "pass", "databasename");
$rs = $db->Execute('SELECT q.queue_id, q.queue_name FROM queue q ORDER BY q.queue_name');
print "<pre>";
print_r($rs->GetRows());
print "</pre>";
I guess without the object oriented design pattern understanding used by PHP I am going to struggle to resuscitate that old piece of software.
Any help, ideas very welcome.
Yours.
Fatal error: Call to a member function query() on null in
You are likely to find further issues in your investigation, but the problem you're asking for help with is relatively simple.
The database property in the class is defined early on in your code as $this->db, but you are then trying to access it as $this->_db. The underscore makes it a different variable name, and thus it doesn't exist. So it is null, hence why you are getting Call to a member function query() on null.
To fix it, make sure you're using the same name for it throughout the class.
I know there are many questions similar to my question. But I really can not figure the problem here. I have class named 'UsersClass' which is responsible on every task related to users. And I use mysqli prepare statement to insert or select data from the database, the problem is that in many cases prepare statement return false. I solved the issue by making new connection every while, but this caused another issue in other functions "2006: Mysql server has gone away" See code below please:
Here and in other functions the prepare return false.
function isPostingAllowed() {
$this->setPsSelectUsers($this->_connection->prepare("Select * from userpermissions where userpermissions.UserId = ? and userpermissions.PermissionId = 1"));
$this->_psSelectUsers->bind_param('i',$this->UserId);
$this->_psSelectUsers->execute();
if ( false===$this->_psSelectUsers ) {
die('prepare() failed: ' . htmlspecialchars($this->_connection->error));}
if ($this->_psSelectUsers->fetch()){
return true;
}else{
return false;
}}
function setPsSelectUsers($stmt) {
$this->unsetPsSelectUsers();
// mysqli_close($this->_Connection);
// $this-> __construct();
$this->_psSelectUsers= $stmt;}
When I uncomment these two lines The first function will work and prepare staement will not return false, but in this case the following function will throw error 2006:
function checkUserAuthentication() {
$this->setPsSelectUsers($this->_connection->prepare("SELECT UserLogInName FROM Users WHERE UserLogInName=? AND UserPassword=?"));
$this->_psSelectUsers->bind_param('ss',$this->UserLogInName, $this->UserPassword);
$this->_psSelectUsers->execute();
if ($this->_psSelectUsers->fetch()){
return true;
}else{
return false;
}}
So How to solve the first problem without making new problem?
Problem:
... I use mysqli prepare statement to insert or select data from the database, the problem is that in many cases prepare statement return false.
That's because you're running the queries in out of order fashion i.e. you're executing ->prepare() before closing the previous statement object. Given your current code, add the following error reporting code in your prepared statements,
if(!$this->_connection->prepare(...)){
printf('errno: %d, error: %s', $this->_connection->errno, $this->_connection->error);
die();
}
If you look at $this->_connection->error, you'll see the following error,
Commands out of sync; you can't run this command now
This issue has been documented in many forums, such as:
From the MySQL documentation,
If you get Commands out of sync; you can't run this command now in your client code, you are calling client functions in the wrong order.
From this SO thread,
You can't have two simultaneous queries because mysqli uses unbuffered queries by default (for prepared statements;...
Solution:
Execute the commands in correct order, (See this example code)
Open up the connection (Once: at the very beginning, not during the execution of every query)
Create a prepared statement
Bind parameters
Execute the query
Bind result variables
Fetch value into those bound variables
Close the statement object
Close the connection (Once: at the very end, not during the execution of every query)
(Follow steps 2 to 7 for executing all of your queries, though one or more steps might be optional based on of your query)
So the solution is, close the previous statement object before calling ->prepare() again. Take this method call $this->unsetPsSelectUsers(); out of the setPsSelectUsers() method and place it before the if ($this->_psSelectUsers->fetch()){...}else{...} block of isPostingAllowed() and checkUserAuthentication() methods. Furthermore, save the status of $this->_psSelectUsers->fetch() method call in a variable and use it in the subsequent if block. So your code should be like this:
public function isPostingAllowed() {
$this->setPsSelectUsers($this->_connection->prepare("Select * from userpermissions where userpermissions.UserId = ? and userpermissions.PermissionId = 1"));
if(!$this->_psSelectUsers){
printf('errno: %d, error: %s', $this->_connection->errno, $this->_connection->error);
die();
}
$this->_psSelectUsers->bind_param('i',$this->UserId);
$this->_psSelectUsers->execute();
$status = $this->_psSelectUsers->fetch();
$this->unsetPsSelectUsers();
if ($status){
return true;
}else{
return false;
}
}
private function setPsSelectUsers($stmt){
$this->_psSelectUsers= $stmt;
}
private function unsetPsSelectUsers() {
if (isset($this->_psSelectUsers)) {
$this->_psSelectUsers->close();
unset($this->_psSelectUsers);
}
}
public function checkUserAuthentication() {
$this->setPsSelectUsers($this->_connection->prepare("SELECT UserLogInName FROM Users WHERE UserLogInName=? AND UserPassword=?"));
if(!$this->_psSelectUsers){
printf('errno: %d, error: %s', $this->_connection->errno, $this->_connection->error);
die();
}
$this->_psSelectUsers->bind_param('ss',$this->UserLogInName, $this->UserPassword);
$this->_psSelectUsers->execute();
$status = $this->_psSelectUsers->fetch();
$this->unsetPsSelectUsers();
if ($status){
return true;
}else{
return false;
}
}
Moreover, you don't have to use this setPsSelectUsers() method anymore, you can directly use the property $_psSelectUsers in your methods like this:
$this->_psSelectUsers = $this->_connection->prepare(...);
In my class 'uzytkownikclass.php' i have a variable which is called '$handler'. That variable store connection with my MySQL database. Before, i created few methods that query database for data. Those functions are working fine. But i created a new method called 'hasRole' which check user role (Administator, moderator etc). But i have a error, PDO is new for me and i can not fix this alone.
Here is my code:
public function hasRole()
{
$email=$_SESSION['email_session'];
$stmt = $this->handler->prepare("SELECT COUNT(*) FROM users INNER JOIN rolausera ON users.id = idusera INNER JOIN rola ON idroli = rola.id WHERE email = :email AND rola.id = :rola");
$stmt->bindValue(':email',$email, PDO::PARAM_STR);
$stmt->bindValue(':rola',$rola, PDO::PARAM_STR);
$stmt->execute();
$result = $stmt->fetchAll();
if($result[0]>0)
{
return TRUE;
}
else
{
return FALSE;
}
}
Here is an error:
Fatal error: Call to a member function prepare() on null in D:\Programy\XAMPP\htdocs\logic\uzytkownikclass.php on line 120
As i said before, i have working methods:
$stmt = $this->handler->prepare("SELECT id, login, email, haslo FROM users WHERE email=:email1 ");
$stmt->execute(array(':email1'=>$email1));
$userRow=$stmt->fetch(PDO::FETCH_ASSOC);
and here is my connection method:
public $handler;
public function connection()
{
$this->handler = new PDO('mysql:host=localhost;dbname=blog', 'root', '',array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
if($this->handler==false)
{
echo 'Blad';
}
}
I tried to delete relation in query and use simple 'SELECT * FROM user' and i had same error.
Greetings.
The question is: did you call the connection() method before calling the hasRole(), or perhaps you closed the connection somewhere in your code? Anyway, I would check in all methods, that try to query the database, if the connection is established and call the connection() method if not. Php's is_null() function helps determining if a variable has been set. I would also check if $handle is false.
i think it's a problem with PHP 5.6 [Beta].
Check your active PHP version, if you have PHP 5.6 [Beta], change to 5.4.
I haved the same problem, a class for connection with PDO working fine, and without changes into the code, this error: Fatal error: Call to a member function prepare() on null in...
For experience, when i have these errors, i see into my PHP version. On my case the hosting changes the PHP version without advise :(
See: PHP bugs
This question already has an answer here:
What to do with mysqli problems? Errors like mysqli_fetch_array(): Argument #1 must be of type mysqli_result and such
(1 answer)
Closed 5 years ago.
I'm creating quite big import script. I want to see any problematic (unexecuted) SQL queries. I have problem with catching wrong SQL queries with try-catch PHP block.
I have a query:
SELECT id FROM tag WHERE name IN ()
Of course there is an error in it so I want to print queries like that with this code:
$sql = "SELECT id FROM tag WHERE name ".$tagsSql."";
try
{
$query = mysqli_query($this->mysqli, $sql);
$result = $query->fetch_assoc();
}
catch(Exception $e)
{
echo 'Problem with: '.$sql;
print_r($e); die;
}
When running the script PHP just throws me this:
Fatal error: Call to a member function fetch_assoc() on a non-object in C:\www\blackboard-import\index.php on line 227
Why this error isn't catched? Because it's fatal? How can I handle that kind of situations?
I use mysqli to contact with MySQL.
This is no regular exception but a fatal error (which causes PHP to shut down). mysqli_query() will return false on error, this is why your current script is failing, so a solution could be something like the following
try {
$query = $this->msqli->query($sql);
if ($query === FALSE) {
throw new Exception($this->mysqli->error);
}
$result = $query->fetch_assoc();
} catch(Exception $e) {
//...
}
This question already has answers here:
Why does this PDO statement silently fail?
(2 answers)
Closed 6 years ago.
I am just trying out PDO and I get this error, Fatal error: Call to a member function fetch() on a non-object, but isn't it already on the $this->db object?
class shoutbox {
private $db;
function __construct($dbname, $username, $password, $host = "localhost" )
{ # db conections
try {
$this->db = new PDO("mysql:host=".$hostname.";dbname=".$dbname, $username, $password);
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
function getShouts()
{
$sql_shouts = $this->db->query('SELECT shoutid, message, pmuserid, ipadress, time FROM shouts WHERE pmuserid == 0');
return $sql_shouts->fetch(PDO::FETCH_OBJ);
}
}
Look carefully at the documentation for PDO::query, particularly the "Return Values" section:
PDO::query() returns a PDOStatement
object, or FALSE on failure.
The important bit is "FALSE on failure". FALSE is not an object, so calling ->fetch() is bad news.
The error is probably due to your use of "==" comparison operator. In SQL, it's just "=".
You should test that the $sql_shouts is not false, and then do something smart with the error, if there was one:
<?PHP
$sql_shouts = $this->db->query('...');
if ($sql_shouts === false){
$errorInfo = $this->db->errorInfo();
//log the error or take some other smart action
}
return $sql_shouts->fetch(PDO::FETCH_OBJ);
I would say that your query is not executing due to incorrect syntax. You should really check PDO's errorinfo static function to see if the query errored out or not.
Here is a potential correct SQL statement:
$sql_shouts = $this->db->query('SELECT shoutid, message, pmuserid, ipadress, `time` FROM shouts WHERE pmuserid = 0');
I believe Time is a reserved word in MySQL I would recommend using a different name but encasing it in backticks will alleviate that issue. 1 equal sign is used for mysql, not two. But yea, look into the errorinfo function to determine if your query failed due to a syntax error and handle it gracefully.
It happens when the table doesn't exist also. make sure it actually exists, and isn't just a holder in the database due to hard drive errors.
When that happens I suggest you recreate the database/table.
I got this error message due to a silly mistake with brackets. It was nested inside an if statement and just didn't see it.
db_query("SELECT thing FROM table WHERE var=:var", array(":var" => $var)->fetchField());
It took me a while to work out that I didn't close the db_query bracket in the right place. Maybe it helps someone else staring at this wondering wth. Correct:
db_query("SELECT thing FROM table WHERE var=:var", array(":var" => $var))->fetchField();