Check if DropTable in DAO was successful - php

I'm looking at executing a query like this:
Yii::$app->db->createCommand()->dropTable($r)
I have been using execute() http://www.yiiframework.com/doc-2.0/yii-db-command.html#dropTable()-detail in order to do so, but I'm unsure from the docs if I should be doing this. I in particular want to return the success or failure of dropping a table in this instance. What is the correct way? I see that execute only seems to return the number of rows affected?

Yes the execute return the number of the row affect. In this case one row is affected . For a better error mnaagement you can also manage the error situation
adding
use yii\base\Exception;
use yii\web\NotFoundHttpException;
and the in you function adding
try {
Yii::$app->db->createCommand()->dropTable($r)->execute();
}
catch (\yii\db\Exception $e) {
// yii db exception
$populateError = $e->getMessage();
}
catch (\Exception $e) {
// not a db exception
$populateError = $e->getMessage();
}
In this way if you have an error (eg: the table to be dropped not exist) you can manage it.

Related

How to structure complex SQL Transactions in PHP

I am totally confused about how we should write SQL transactions in PHP.
We have a invoice payment section, so we have to do
Make the DB changes in the invoice tables as per the payment details updateInvoice()
Do data insertions in the Journal as per the payment amount addJournals()
Update/Insert the payment details in the reports for reporting section setUpReport()
so we included all the three actions into a single transaction
try {
$this->conn->beginTransaction();
updateInvoice();
addJournals();
setUpReport();
$this->conn->commit();
} catch (Exception $ex) {
$this->conn->rollback();
}
There are around 8-10 tables involved in this transactions and it seems the transactions are locking all these tables.
Also we have noticed this process is taking too much time and there are occasional deadlocks happening during this process. On doing some research I understood we need to make the above transaction atomic and simple. And most of the suggestion points towards splitting the transaction into multiple transactions.
So I was planning to make separate transaction for each function like
try {
$this->conn->beginTransaction();
updateInvoice();
$this->conn->commit();
} catch (Exception $ex) {
$this->conn->rollback();
}
try {
$this->conn->beginTransaction();
addJournals();
$this->conn->commit();
} catch (Exception $ex) {
$this->conn->rollback();
}
try {
$this->conn->beginTransaction();
setUpReport();
$this->conn->commit();
} catch (Exception $ex) {
$this->conn->rollback();
}
If I restructure the code like this, if an error happens on setUpReport() it will be difficult to revert the actions in the above 2 transactions.
So I am really confused how we need to structure the transaction.
I had the same problem, I changed max_execution_time = 60
If you can't change value, make reconnect
EXAMPLE:
echo "Other queries in your system / framework.....";
$this->sendQuery("SQL: SELECT * from session_tab...");
$this->sendQuery("SQL: SELECT * from privileges... ");
$this->sendQuery("SQL: .....");
$this->sendQuery("SQL: ......");
$this->sendQuery("SQL: SELECT * from table..."); // Waiting long time..
echo "After that you want execute next queries.. " ;
$this->db->disconnect();
$this->db->connect();
try {
$this->conn->beginTransaction();
setUpReport();
$this->conn->commit();
} catch (Exception $ex) {
$this->conn->rollback();
}

No true result return in insert query

This is the class I have created which I am using for the queries:
<?php
mysqli_report(MYSQLI_REPORT_INDEX | MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
class DBConnect {
private $dbcon;
private $paramquery;
private $result;
public function __construct() {
try {
$this->dbcon = mysqli_init();
mysqli_real_connect($this->dbcon, '127.0.0.1', '', '', '', 3306, '', MYSQLI_CLIENT_COMPRESS);
$this->paramquery = $this->dbcon->stmt_init();
} catch (mysqli_sql_exception $e) {
exit('Database Connection Failed');
}
}
public function dbquery($querysql, $querydata) {
try {
mysqli_ping($this->dbcon);
$this->paramquery->prepare($querysql);
array_walk($querydata, function(&$escval){$escval = mysqli_real_escape_string($this->dbcon, $escval);}); //Problem
call_user_func_array(array($this->paramquery, 'bind_param'), $querydata); //Problem
$this->paramquery->execute();
} catch (mysqli_sql_exception $e) {
exit('Database Query Failed');
}
$this->result = $this->paramquery->get_result(); // problem
if ($this->result) {
$drs = $this->result->fetch_array();
$this->result->free_result();
return $drs;
}
}
public function __destruct() {
if (($this->dbcon !== null) && ($this->paramquery !== null) && ($this->result !== null)) {
$this->paramquery->close();
$this->dbcon->close();
}
unset($this->result);
unset($this->paramquery);
unset($this->dbcon);
}
}
?>
The index.php file code is this:
<?php
require_once('connection.php');
$DBX = new DBConnect();
$DBX->dbquery('INSERT INTO `xathx_key` (`license`, `client`, `server`, `uniquex`) VALUES (?, ?, ?, ?)', array('ssss', '1', '3', '5', '7'));
var_dump($DBX);
unset($DBX)
?>
I am trying to do an INSERT query in this instance. And I want to get a success result or flag when the query is executed successfully. But in the var_dump of the object I get some irrelevant data and if I use echo I get an error that the object cannot be converted to a string. I just want to get a 0 for query execution failure, corruption or problem and a 1 for completion, success, ok status. When am I going wrong in the code?
EDIT: Can you guys just tell me what are the things that are wrong with this simple script? The main goal of this script is to connect to mysql server and execute all possible queries as fast as possible, as securely as possible.
Full Project Source: https://github.com/FSMySQL/PHP-FSMySQL
The main goal of this script is to connect to mysql server and execute all possible queries as fast as possible, as securely as possible.
The goal is a good one but the implementation could benefit from many improvements.
Disclaimer: there will be a lot of links to my own site because I am helping people with PHP for 20+ years and got an obsession with writing articles about most common issues.
The concept of error reporting
First of all, you need to change the concept of error reporting. Your exit() approach would be a nightmare for a programmer, as error messages are a vital source of information when something goes wrong. A programmer should go at any lengths trying to get the error message in the full detail. In my article, PHP error reporting, I do explain how to make error reporting both programmer- and user-friendly. In short, you shouldn't catch errors on the spot, but have a single dedicated place to report errors and exceptions, and then it could be easily configured depends on the current server's role.
Although, as suggested in the other answer, you could use a global try-catch block in your index.php file to act as such a global error handler, I would prefer a dedicated error handler script, as explained in the article above. It will make your code better organized and make index.php less bloated.
Besides, your idea of having "a true result return in insert query" contradicts with your intention to use exceptions. When one is using exceptions, there is no point to verify the immediate function's result. In case of error it will just bubble up to the error handler or a catch block, so, it will never reach the condition. A quick example:
function test() {
throw new Exception("Test");
return false;
}
$result = test();
if ($result === false) {
echo "false";
}
The code execution in this example will never reach the condition, therefore making your functions return false on error useless. Which, in turn, makes returning true on success superfluous. Just return a meaningful result but don't use it as flag: simply write your code without any conditions, as though everything is fine. Remember that you have your error handling code elsewhere that will be magically invoked in case of error.
Connection
As explained in my other article, How to connect properly using mysqli, there is a slight chance to reveal connection credentials in case of a connection error. To avoid even a possibility but keep the programmer informed we have to throw a brand new exception, however keeping the error information - so the stack trace will begin from the throw line, and thus contain no sensitive information.
Also, the connection code lacks an essential part - setting the correct charset. Although in MySQL 8 the correct charset is set by default, it's better to make it explicit.
Also, making a mysqli statement a class variable is a grave mistake that will lead to race condition errors. The only state that your class should keep is that related to the connection but not a single class variable should be used for a statement.
So let's rewrite your constructor based on the code from the article above:
public function __construct()
{
mysqli_report(MYSQLI_REPORT_INDEX | MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$this->dbcon = mysqli_init();
$this->dbcon->real_connect('127.0.0.1', '', '', '', 3306, '', MYSQLI_CLIENT_COMPRESS);
$this->dbcon->set_charset('utf8mb4');
} catch (\mysqli_sql_exception $e) {
throw new \mysqli_sql_exception($e->getMessage(), $e->getCode());
}
}
The dbquery function
The function is, frankly, weird. It's a strange mix between prepared statements and escaping. Let's rewrite it based on my mysqli helper function that actually utilizes mysqli prepared statements
public function dbquery($sql, $data = [], $types = "")
{
$this->dbcon->ping(); // not sure if it's necessary
$stmt = $this->dbcon->prepare($sql);
if ($data) {
$types = $types ?: str_repeat("s", count($data));
$stmt->bind_param($types, ...$data);
}
$stmt->execute();
return $stmt->get_result();
}
Now this function fulfills your desire for secure SQL queries
So finally we can rewrite your index.php
<?php
require_once('connection.php');
$DBX = new DBConnect();
$sql = 'INSERT INTO `xathx_key` (`license`, `client`, `server`, `uniquex`) VALUES (?, ?, ?, ?)';
$DBX->dbquery($sql, ['1', '3', '5', '7']);
Just as you learned above, there is no need for a "flag when the query is executed successfully". Just act as though there is always a success. In case of error it will appear without any conditions (an on a live site will be handled properly if you include an error handler script in your index).
In your DBConnect Class, you have try catch blocks. But your catch blocks are simply terminating the request using exit statement. Your Class should not be doing that.
Imagine you deploy this on production and for some reason the DB Connection Fails. In that case User will simply see a white screen with Message "Database Connection Failed" which would not look professional at all.
Instead your class should pass this information back to the index.php which called the method of this Class and let index.php handle the Error Message or Exception.
So I would make following changes to your code:
DBConnect Class should throw an Exception rather than terminating the execution of the program completely. Below is how the __contruct() should look.
public function __construct() {
try {
$this->dbcon = mysqli_init();
mysqli_real_connect($this->dbcon, '127.0.0.1', '', '', '', 3306, '', MYSQLI_CLIENT_COMPRESS);
$this->paramquery = $this->dbcon->stmt_init();
} catch (mysqli_sql_exception $e) {
//exit('Database Connection Failed'); Commented this out.
//Throw the Exception Here. This will then be passed to the calling code.
throw $e;
}
}
You will need to change the other methods accordingly.
In your index.php File, you should be looking to catch the above exception. So you should move your code in a Try Catch Block to catch that exception.
require_once('connection.php');
try {
$DBX = new DBConnect();
$DBX->dbquery('INSERT INTO `xathx_key` (`license`, `client`, `server`, `uniquex`) VALUES (?, ?, ?, ?)', array('ssss', '1', '3', '5', '7'));
} catch (Exception $e) {
$message = 'Caught exception: ', $e->getMessage() . "\n";
//Display this Message to User in an appropriate way.
//Write to Error Log
}
//var_dump($DBX);
//unset($DBX)
So this will catch the Exception in case the DB Connection Fails as well as when the Insert Query Fails. You can write the exception to the logs so that you can check them later and you can display any appropriate error message to user based on the exception caused.
You could read more on Exceptions in PHP Manual
You have a problem with
$this->result = $this->paramquery->get_result();
because mysqli_stmt::get_result returns a resultset for successful SELECT queries, or FALSE for other DML queries or on failure.
Other DML-queries are INSERT, UPDATE, DELETE. And that's exactly what you have in the example.
To resolve your problem you can modify the class by adding some extra-checks to $mysqli->errno:
$this->result = $this->paramquery->get_result();
if ($this->result) {
...
}
if ($this->paramquery->errno !== 0) { // we have some real error
exit('Database Query Failed');
}
// we have DML-query (INSERT, UPDATE, DELETE)
// and we can return number of affected rows (if it's necessary)
return $this->paramquery->affected_rows;
P.S. I agree with this comment and I think that your class should be used for educational purposes only because it has multiple serious flaws.

How do I loop through the mysql resultset in fatfree framework?

I am new to the php fat-free framework, and I am trying figure out how to loop through my mysql query results, or better yet, get it as an associative array (for learning purposes only).
What I did so far is
while(!$users->dry()){
array_push($user_assoc,$users->cast());
$users->next();
}
This works, but I was wondering if there is a better way of doing this? Also how do I setup a error handler? I mean how do I check if the query had any errors (i.e. fat-free equivalent of mysql_error())?
DB querying
There are 3 variants to loop through db results:
Without mapper:
Execute a SQL query and fetch the result set as an array of associative arrays:
$users = $db->exec('SELECT * FROM users');
foreach($users as $user)
echo $user['name'];//associative array
With mapper->load:
Fetch mapper rows one by one (your method):
$user=new \DB\SQL\Mapper($db,'users');
$user->load('');
while(!$user->dry()) {
echo $user->name;//db mapper
$user->next();
}
With mapper->find:
Fetch the result set as an array of mappers:
$mapper=new \DB\SQL\Mapper($db,'users');
$users=$mapper->find('');
foreach($users as $user)
echo $user->name;//db mapper
DB error handling
\DB\SQL is a subclass of PDO so it can throw catchable PDO exceptions. Since these are disabled by default, you need to enable them first. This can be done in 2 different ways:
at instantiation time, for all transactions:
$db = new \DB\SQL($dsn, $user, $pwd, array( \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION ));
later on in the code, on a per-transaction basis:
$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
Once PDO exceptions are enabled, just catch them as other exceptions:
try {
$db->exec('INSERT INTO mytable(id) VALUES(?)','duplicate_id');
} catch(\PDOException $e) {
$err=$e->errorInfo;
//$err[0] contains the error code (23000)
//$err[2] contains the driver specific error message (PRIMARY KEY must be unique)
}
This also works with DB mappers, since they rely on the same DB\SQL class:
$db=new \DB\SQL($dsn,$user,$pwd,array(\PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION));
$mytable=new \DB\SQL\Mapper($db,'mytable');
try {
$mytable->id='duplicate_id';
$mytable->save();//this will throw an exception
} catch(\PDOException $e) {
$err=$e->errorInfo;
echo $err[2];//PRIMARY KEY must be unique
}
You're already using the correct way. At least if you want to use the mapper. By using the SQL class directly, an associative array is returned. Mostly everything related to that is described here http://fatfreeframework.com/databases#querying-the-database
$result = $db->exec('SELECT * FROM users');
print_r($result);
If you're looking for errors or you want to know what has been executed, use $db->log();. http://fatfreeframework.com/databases#profiling

zend get() single record returns exception instead of zero results

I have a very simple function in a zend skeleton that gets a row based on an ID you input. If you use an ID that it finds in the database it works fine:
$this->getTestimonialTable()->get($request->getPost('id'));
however if you put an id that doesn't exist, it returns an exception saying the row doesn't exist, which i know already.
I want it so, if it finds the row, continue as normal, if not show that there was no results, not raise an exception.
I've tried wrapping it in a count:
if (count($this->getTestimonialTable()->get($request->getPost('id'))) > 0) {
//more than 1 result
} else {
//zero results
}
however the exception is still raised on the initial call, i also tried wrapping it in a try statement:
try {
$this->getTestimonialTable()->get($request->getPost('id'));
//if im here i should have a result
} catch (Exception $e) {
//zero results
}
Can anyone suggest what the problem is?
solved
So the principle was right, however i was running the code on the factory method, not running it on the database call.
so in my factory i now have
public function get($id)
{
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if (!$row) {
return null;
}
return $row;
}
which then makes the above count work as intended
if (count($this->getTestimonialTable()->get($request->getPost('id'))) > 0) {
//more than 1 result
} else {
//zero results
}
Zend is behaving as expected. You have asked it to return a row. It should do nothing more. So, it's throwing an exception when it can't return a row.
I want it so, if it finds the row, continue as normal, if not show
that there was no results, not raise an exception.
Your best option is to put the database call in a try catch block, catch the exception and then parse the exception to determine if no rows were found.

how to find record insert to mysql using commit()

Sorry for this beginners question and i'm not a PHP developer, but now i'm trying to learn it.
i want to add record in MySQL data base and i'm using transactions lock.
my code is as below.
$SqlQuery="INSERT INTO tab_photo VALUES('$PhotoID','$ProjectId','$Day','$barCode','$photoName','$PhotoXml')";
$waiting = true;
while($waiting) {
try {
// save border data
$stmt = $conn->prepare($SqlQuery);
$conn->beginTransaction();
$stmt->execute();
sleep(1);
$x=$conn->commit();
echo "x value-".$x;
echo "Success";
$waiting = false;
}
catch (PDOException $e){
echo "Failled :".$PhotoID."-".$PhotoID;
if(stripos($e->getMessage(), 'DATABASE IS LOCKED') !== false) {
// This should be specific to SQLite, sleep for 0.25 seconds
// and try again. We do have to commit the open transaction first though
$conn->commit();
usleep(250000);
} else {
$conn->rollBack();
throw $e;
}
}
}
in here as output it gives,
x value-1 Success
but actually this record doesn't add to the database.
My Questions:
Even the commit is successful(output 1) how does it not added to the database?
how can i check whether record is added to database? ( Is there any way to find it without write select statement?
As I understand, you expect that PDOException will be thrown when statement is failed to execute. But as I can see, exception is not thrown by default in such cases.
See how you can change that here
Suppose in your case you should have a code like this:
$conn = new PDO($connection_string);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // this will force PDO to throw exception when SQL statement fails instead of simply setting an error.
Suppose this will work fine for you.
Please note that you should not use
$SqlQuery="INSERT INTO tab_photo VALUES('$PhotoID','$ProjectId','$Day','$barCode','$photoName','$PhotoXml')";
Instead of that, you should use parameters binding:
$SqlQuery="INSERT INTO tab_photo VALUES(:PhotoID,:ProjectId,:Day,:barCode,:photoName,:PhotoXml)";
$stmt = $conn->prepare($SqlQuery);
$conn->beginTransaction();
$stmt->execute(array(':PhotoID' => $PhotoID, ':ProjectId' => $ProjectId, ....));
sleep(1);
See this for more details.

Categories