Saturday 27 February 2016

mysqli - Handle PHP objects on declaration file for every method call




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("");
}
}
function query($query, $resultmode = MYSQLI_STORE_RESULT){
$r=$this->c->query($query, $resultmode); // make query
if(!empty($this->c->error)){ // check for errors
echo("");
}
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;
}
}

Answer



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.





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).


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...