PHP mysql_query transactions not rolling back - php

I have written a database class(es) sometime ago, I use this for most projects and recently extended the class to work with transactions.
The Transaction class below does not seem to work correctly, the server is MySQL innoDB and I've checked that transactions work on my database(with PHPMyAdmin). So this is obviously an error in my code or my misunderstanding.
<?php
/**
* Description of Database
*
* #author http://stackoverflow.com/users/820750/gerve
*/
class Database {
private $connection;
public function __construct($dbServer, $dbName, $dbUser, $dbPassword) {
$this->connection = mysql_connect($dbServer, $dbUser, $dbPassword);
mysql_select_db($dbName, $this->connection);
}
public function query($query, $die = true) {
return new Query($query, $this->connection, $die);
}
public function escape($param) {
return mysql_real_escape_string($param, $this->connection);
}
public function transaction() {
return new Transaction($this->connection);
}
}
class Query {
private $query;
public $count;
public $countModified;
public $queryString;
public function __construct($query, $link_identifier, $die = true) {
if($die){
$this->query = mysql_query($query, $link_identifier) or die(mysql_error());
}
else{
$this->query = mysql_query($query, $link_identifier);
}
$this->count = is_resource($this->query) ? mysql_num_rows($this->query) : 0;
$this->countModified = mysql_affected_rows($link_identifier);
$this->queryString = $query;
}
public function nextRow() {
return mysql_fetch_object($this->query);
}
public function allRows() {
$array = array();
while($row = mysql_fetch_object($this->query)){
$array[] = $row;
}
return $row;
}
}
class Transaction {
private $connection;
private $isAlive;
public function __construct($link_identifier) {
$this->connection = $link_identifier;
$this->query("BEGIN");
$this->isAlive = true;
}
public function query($query) {
if($this->isAlive){
$q = new Query($query, $this->connection, false);
if(mysql_error()){
$this->rollBack();
return false;
}
else{
return $q;
}
}
}
public function rollBack() {
if($this->isAlive){
$this->query("ROLLBACK");
$this->isAlive = false;
}
}
public function commit() {
if($this->isAlive){
$this->query("COMMIT");
$this->isAlive = false;
return true;
}
else{
return false;
}
}
}
?>
EDIT - Example usage of classes
$DB = new Database(dbServer, dbName, dbUser, dbPassword);
$transaction = $DB->transaction();
$transaction->query("INSERT INTO myTable `c1`, `c2` VALUES('1', '2')"); //Works
$transaction->query("INSERT INTO jhsdjkag 5dafa 545"); //Fails
$transaction->query("INSERT INTO myTable2 `c1`, `c2` VALUES('3', '4')"); //Works
$transaction->commit();
The above code should not insert any rows into the database, the second query has failed so none should succeed.
My problem is that it doesn't rollback, always rows are inserted regardless of failing queries.

try using START TRANSACTION instead of BEGIN
by the way START TRANSACTION will allow you to use WITH CONSISTENT SNAPSHOT:
The WITH CONSISTENT SNAPSHOT option starts a consistent read for storage engines that are capable of it. This applies only to InnoDB.
source: MySQL Documentation

I Found that there was an error in the code, just in the __construct(). It was pretty simple, I just changed 1 line:
FROM:
public function __construct($link_identifier) {
$this->connection = $link_identifier;
$this->query("BEGIN");
$this->isAlive = true;
}
TO:
public function __construct($link_identifier) {
$this->connection = $link_identifier;
$this->isAlive = true;
$this->query("BEGIN");
}
->isAlive was being set after the first "BEGIN" query, which means BEGIN was never sent.

Related

How to modify my PHP database extraction class to work with MySQLi

I am getting a warning stating:
mysqli_num_rows() expects parameter 1 to be mysqli_result
It has been a while since I worked with PHP which leads me to struggle figuring out why the size function in the MySQLReturn class is giving me grief. It is an old class I used years ago and modified it as best I could to work with php7 and MySQLi.
<?php
class MySQL {
private $host;
private $dbUser;
private $dbPass;
private $dbName;
private $dbConn;
private $connectError;
public function __construct($host,$dbUser,$dbPass,$dbName) {
$this->host=$host;
$this->dbUser=$dbUser;
$this->dbPass=$dbPass;
$this->dbName=$dbName;
$this->connectToDb();
}
public function __destruct() {
}
public function connectToDb () {
// Make connection to MySQL server
if (!$this->dbConn = mysqli_connect($this->host,$this->dbUser,$this->dbPass)) {
trigger_error('Could not connect to server');
$this->connectError=true;
// Select database
} else if (!mysqli_select_db($this->dbConn,$this->dbName)) {
trigger_error('Could not select database');
$this->connectError=true;
}
}
public function isError () {
if ($this->connectError)
return true;
$error=mysqli_error($this->dbConn);
if (empty($error))
return false;
else
return true;
}
public function query($sql) {
if (!$queryResource=mysqli_query($this->dbConn,$sql)) trigger_error ('Query failed: '.mysqli_error($this->dbConn).' SQL: '.$sql);
$result = new MySQLReturn($this,$queryResource);
return $result;
}
}
class MySQLReturn {
private $mysql;
private $query;
public function MySQLResult($mysql,$query) {
$this->mysql=$mysql;
$this->query=$query;
}
public function fetch() {
if ($row=mysqli_fetch_array($this->query,MYSQL_ASSOC) ) {
return $row;
} else if ( $this->size() > 0 ) {
mysqli_data_seek($this->query,0);
return false;
} else {
return false;
}
}
public function size() {
return mysqli_num_rows($this->query);
}
public function insertID() {
return mysqli_insert_id($this->mysql->dbConn);
}
public function isError() {
return $this->mysql->isError();
}
}
?>
This is how I am accessing the class functions;
<?php
$db = new MySQL($dbhost,$dbuser,$dbpass,$dbmain);
$authsql = $db->query("SELECT * FROM users WHERE email='".$uid."' AND password=MD5('".$pwd."')");
if ($authsql->size() >= '1') {
$logindtsql = $db->query("UPDATE users SET last_login=CURRENT_TIMESTAMP WHERE email='".$uid."' AND password=MD5('".$pwd."')");
$authsqlrow = $authsql->fetch();
extract($authsqlrow);
}
if ($authsql->size() == 0) {
unset($_SESSION['uid']);
unset($_SESSION['pwd']);
session_destroy();
}
?>
I am not getting the size of the query
Changed
public function MySQLResult($mysql,$query) {
$this->mysql=$mysql;
$this->query=$query;
}
to
public function __construct($mysql,$query) {
$this->mysql=$mysql;
$this->query=$query;
}
Works like a charm

mysqli_insert_id() returns 0 on same connection, after insert query

Before you ask, i have used the search function of stack overflow and google!
At my code, mysqli_insert_id does always return 0.
In my 'Model'-PHP-Class my save function looks like this:
$result = DB::q($sql);
if(!$result)
{
throw new Exception("Error:".mysqli_error(DB::getDB()));
}
else
{
if(is_null($this->id))
{
$this->id = DB::insert_id();
}
}
$this->update();
My Class DB looks like this:
class DB
{
public static $db = null;
public static function q($sql)
{
self::checkDB();
if(!isset($sql) OR $sql == null OR $sql == "")
{
throw new Exception("\$sql may not be empty");
}
else
{
$result = mysqli_query(self::$db, $sql);
if(!$result)
{
error_log($sql);
throw new Exception(mysqli_error(self::$db));
die();
}
return new DBResult($result);
}
}
public static function checkDB()
{
if(is_null(self::$db))
{
self::$db = mysqli_connect(DB_PORT, DB_USERNAME, DB_PASSWORD, DB_DATABASE);
if(!self::$db)
{
throw new Exception(mysqli_error(self::db));
}
}
}
public static function insert_id()
{
return mysqli_insert_id(self::$db);
}
}
class DBResult
{
private $result = null;
public function __construct($result)
{
$this->result = $result;
}
public function fetch_assoc()
{
return mysqli_fetch_assoc($this->result);
}
public function fetch_object()
{
return mysqli_fetch_object($this->result);
}
public function __get($willget)
{
if($willget == "num_rows")
{
return mysqli_num_rows($this->result);
}
else
{
return $this->result;
}
}
}
When i perform an multi-query directly on the server in the same database, it will work correctly:
INSERT INTO users (name, age) VALUES ('personA', 42); SELECT last_insert_id() as k;
I also have set the AUTO_INCREMENT flag on my id column.
Also there seems to be no place in my code between any query, where i will overwrite $db in the DB class.
Thanks alot for your help!

Call to a member function query() on null in PHP

I am getting below error when I run my PHP code.
Call to a member function query() on null
Below is the code,
function fetch_url($url_code) {
$query = "SELECT * FROM `urls` WHERE `url_code` = ? ";
$result = $this->db->query($query, array($url_code));
if ($result) {
return $result;
} else {
return false;
}
}
Please guide me how to remove this error.
this->db->query function need only SQL Code. not like PDO.
Make sure Library is loaded
In config/autoload.php add this $autoload['libraries'] = array('database');
Try this
function fetch_url($url_code) {
$query = $this->db->query("SELECT * FROM urls WHERE url_code = '$url_code' ");
$result = $query->result_array(); # setting array to objective
$count = count($result); # get count
if (!empty($count)) {
return $result;
}
else {
return false;
}
}
Your $this->db is null. Please make sure, it is initialized properly before calling query. Here is a very basic example for MySQL (you will have to adapt it to your DB system):
function init() {
$this->db = new mysqli('host', 'user', 'password', 'database');
if (!$this->db) {
die('MySQL Connection Error (' . mysqli_connect_errno() . ') '
. mysqli_connect_error());
}
}
function fetch_url($url_code) {
$sql = "SELECT * FROM urls WHERE url_code = '$url_code' ";
if ($this->db === null) {
$this->init();
}
$query = $this->db->query($sql);
$result = $query->result_array(); # setting array to objective
$count = count($result); # get count
if (!empty($count)) {
return $result;
}
else {
return false;
}
}
class Database {
public $host=DB_HOST; // Specify localhost
public $username=DB_USER; //Specify username
public $password=DB_PASSWORD; // specify password
public $dbname=DB_NAME; // specify database name
public $conn ;
public $error;
/*
* Class Constructor
*/
Public function __constructor() {
// Call Connect Function
$this->connect();
}
/*
* Connector
*/
Public function connect() {
//* Creating mySqli Class Object * //
$this->conn = new mysqli($this->host,$this->username,$this->password,$this->dbname);
if (!$this->conn) {
$this->error = "Connection failed :" .$conn->connect_error ;
return false;
}
}
/*
* Select function
*/
public function select($query) {
/* to avoid call to member function query() on Null Error
Use this 2 lines of code */
if ($this->conn === null) {
$this->connect();
}
$results = $this->conn->query($query) or die($this->conn->error.__LINE__);
/* If there is atleast one row in the table Condition is true / Return table Rows as per your sql query */
if($results->num_rows > 0) {
return $results;
}
else {
return false;
}
} //Select Function Ends;
} //Class ends here ..
Yes use this lines of code to get rid of the error Call to a member function query() on null in PHP I used it in my database class and it's working fine !
class Database {
public $host='localhost'; // Your Local Computer
public $username='root'; // Your PhpMyAdmin Username
public $password=''; // Your PhpMyAdmin Password
public $dbname='blog'; // Your database Name
// Declaring Variables
public $conn ;
public $error;
/*
* Class Constructor
*/
Public function __constructor() {
// Call Connect Function
$this->connect();
}
/*
* Connector
*/
Public function connect() {
//* Creating mySqli Class Object * //
$this->conn = new mysqli($this->host,$this->username,$this->password,$this->dbname);
if (!$this->conn) {
$this->error = "Connection failed :" .$conn->connect_error ;
return false;
}
}
/*
* Select function
*/
public function select($query) {
// these 2 lines of code help to resolve error "Call to a member function query() on null " //
if ($this->conn === null) {
$this->connect();
}
$results = $this->conn->query($query) or die($mysqli->error.__LINE__);
/* If there is atleast one records in the table based on Sql Query ! Condition is true / Return table Records*/
if($results->num_rows > 0) {
return $results;
}
else {
return false;
}

Database connection through class

i want to crete a class that will contain various general variables and functions to be used throughout the website. One of these things will be a database connection. I am trying the following code:
class Sys {
public $dbc = new mysqli('localhost', 'user', 'pass', 'db');
$dbc->query("SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'");
}
$system = new Sys;
It is giving me a syntax error in the first line of the class... What am i doing wrong?
Thanks
you should do things like this in the constructor. You can only set primitives in the initial declaration. Also you need braces when creating the object.
class Sys {
private $dbc;
private $someInteger = 4; // you can do this
private $someArray = array(); // and this.
public function __construct()
{
$this->dbc = new mysqli('localhost', 'user', 'pass', 'db');
$this->dbc->query("SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'");
}
public function getDbc()
{
return $this->dbc;
}
}
$system = new Sys();
//$system->getDbc()->soSomethingWithMyDb();
i would advise you to read up on using objects: http://php.net/manual/en/language.types.object.php
You should just declare a public $dbc.
And then have a constructor function function __construct() { ...... } where you initialize it/ set it up.
class Sys {
public $dbc;
function __construct() {
$this->dbc = new mysqli('localhost', 'user', 'pass', 'db');
$this->dbc->query("SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'");
}
}
$system = new Sys;
class Sys {
var $mysqli;
function DB($database,$server,$user,$pass) {
$this->mysqli = mysqli_connect($server, $user, $pass, $base) or die('Server connection not possible. '. mysqli_connect_error());
}
}
include("db.class.mysqli.php"); // db handler class
// Open the base (construct the object):
$db = new Sys(DB_NAME, SERVER_NAME, USER_NAME, PASSWORD);
This is how I do it
check out the use of magic methods http://php.net/manual/en/language.oop5.magic.php in php - as mentioned above the __construct method is needed for your class to work. Below is an example from Peter Lavin's book Object Oriented PHP http://objectorientedphp.com/ in chapter 9.
class MySQLConnect {
// Data Members
private $connection;
private static $instances = 0;
// Constructor
public function __construct($hostname, $username, $password) {
if(MySQLConnect::$instances == 0) {
$this->connection = new mysqli($hostname, $username, $password);
// Check for Errors then report them
if ($this->connection->connect_error) {
die("Connect Error ($this->connection->connect_errno) $this->connection->connect_error");
}
// Set the Class variable $instances to 1
MySQLConnect::$instances = 1;
} else {
$msg = "There's another instance the MySQLConnect Class with a connect open already. Close it";
die($msg);
}
}
}
Try this:
$db = new DB;
$link = $db->connect()->getLink();
class DB {
public $connection = array();
public function __construct() {
return $this;
}
public function connect($host=null, $user=null, $pass=null, $database=null) {
$this->connection = new Connection($host, $user, $pass, $database);
$this->connection->connect();
$this->link = $this->connection->getLink();
if ($this->connection->ping()) {
if (!is_null($database)) {
if (!$this->connection->databaseConnect($database)) {
throw new\Exception('Unable to connect to the server.');
}
}
}else{
throw new \Exception('Unable to connect to the server.');
}
return $this;
}
public function getLink() {
return $this->link;
}
}
class Connection {
protected $chost='localhost';
protected $cuser='guest';
protected $cpass='password';
protected $cdatabase='PROFORDABLE';
function __construct($host=null, $user=null, $pass=null, $database=null) {
$host = !is_null($host) ? $host : $this->chost;
$user = !is_null($user) ? $user : $this->cuser;
$password = !is_null($pass) ? $pass : $this->cpass;
$database = !is_null($database) ? $database : $this->cdatabase;
$this->set('host', $host)->set('user', $user)->set('password', $password)->set('database', $database);
return $this;
}
public function connect() {
$link = mysqli_connect($this->host->getHost(), $this->user->getUser(), $this->password->getPassword());
if (!is_object($link)) {
throw new \Exception("An error has occurred while connecting to the server.");
}
$this->link = $link;
}
public function databaseConnect($database) {
if (!mysqli_select_db($this->getLink(), $database)) {
throw new \Exception("Unable to select the database.");
}
}
public function getLink() {
return $this->link;
}
public function ping() {
if (mysqli_ping($this->link)) {
return TRUE;
}
return FALSE;
}
public function set($name, $param) {
if (!isset($name) || !isset($param)) return $this;
$class = __NAMESPACE__.'\\'.ucwords($name);
$this->$name = new $class($param);
return $this;
}
public function get($name) {
$getfunc = 'get'.ucwords($name);
return $this->$name->$getFunc();
}
}
class Host {
public function __construct($host) {
$this->setHost($host);
}
public function setHost($host) {
$this->host = $host;
}
public function getHost() {
return $this->host;
}
}
class User {
public function __construct($user) {
$this->setUser($user);
}
public function setUser($user) {
$this->user = $user;
}
public function getUser() {
return $this->user;
}
}
class Password {
public function __construct($password) {
$this->setPassword($password);
}
public function setPassword($password) {
$this->password = $password;
}
public function getPassword() {
return $this->password;
}
public function sha($value) {
return sha1($value);
}
}
class guestPassword extends Password {
const PASSWORD='password';
public function __construct() {
return PASSWORD;
}
}
class Database {
public function __construct($database) {
$this->setDatabase($database);
}
public function setDatabase($database) {
$this->database = $database;
}
public function getDatabase() {
return $this->database;
}
}
class Query {
}
//Create mysql.php and paste following code
<?php
class MySQL {
private $set_host;
private $set_username;
private $set_password;
private $set_database;
public function __Construct($set_host, $set_username, $set_password) {
$this->host = $set_host;
$this->username = $set_username;
$this->password = $set_password;
$con = mysql_connect($this->host, $this->username, $this->password);
if (!$con) {
die("Couldn't connect to the server");
}
}
//end of __construct
//connect to database
public function Database($set_database) {
$this->database = $set_database;
mysql_query("set character_set_server='utf8'");
mysql_query("set names 'utf8'");
mysql_select_db($this->database) or die("Unable to select database");
}
//fetch data from any table
public function fetch_data($sql) {
$this->sql = $sql;
$query = mysql_query($this->sql);
$result = array();
while ($record = mysql_fetch_array($query)) {
$result[] = $record;
}
return $result;
}
//fetch all columns from any table to be used for INSERT INTO.
public function get_all_columns($table_name) {
$this->table_name = $table_name;
$sql = "SHOW COLUMNS FROM $this->table_name";
$result = mysql_query($sql);
while ($record = mysql_fetch_array($result)) {
$fields[] = $record['0'];
}
$val = '';
foreach ($fields as $value) {
$val .= $value . ',';
}
$vals = rtrim($val, ',');
return $vals;
}
//insert data to any table by $_POST or set of variables separated by ,
public function insert_data($tbl_name, $tbl_value) {
$this->tbl_name = $tbl_name;
$this->tbl_value = $tbl_value;
//use mysql_real_escape_string($tbl_value) to clean & insert data.
$this->tbl_col = $this->get_all_columns($this->tbl_name);
$sql = "INSERT INTO $this->tbl_name ($this->tbl_col) VALUES ($this->tbl_value)";
$query_result = mysql_query($sql);
}
//end of insert data
public function delete_data($del_id, $table_name) {
$this->del_id = $del_id;
$this->table_name = $table_name;
if (isset($this->del_id) && is_numeric($this->del_id) && !empty($this->del_id)) {
$sql = "DELETE FROM $this->table_name WHERE id=$this->del_id LIMIT 1";
$del_result = mysql_query($sql);
$aff_row = mysql_affected_rows();
return $aff_row;
}
}
}
// class ends here
//call class to connect to server and db as well.
$connect = new MySQL('localhost', 'root', '');
$connect->Database('db_name');
?>
//include file mysql.php and call your class object as :
//fetching data from any table use :
$variable = $connect->fetch_data("SELECT * FROM table_name");
for($i=0;$i<count($variable);$i++){
$result = $variable[$i]['col_name'];
}
//insert INTO values in any table
$result = $connect->insert_data($tbl_name, $tbl_value);
if($result)
echo 'inserted';
else{
echo 'failed';
}
//delete record from any table
$result = $connect->delete_data($del_id, $table_name)
You can use a wonderful class ezSQL

Call to undefined function mysqli_result::num_rows()

I'm trying to count the number of rows in a result, and I keep getting the above returned error. I've checked the manual, and I'm using mysqli_result::num_rows() as I should be (I'm using object oriented style.) I've got three classes working here.
Class (Connection):
class utils_MysqlImprovedConnection {
protected $_connection;
public function __construct($host, $user, $pwd, $db)
{
$this->_connection = #new mysqli($host, $user, $pwd, $db);
if(mysqli_connect_errno ()) {
throw new RuntimeException('Cannot access database:' . mysqli_connect_error());
}
}
public function getResultSet($sql)
{
$results = new utils_MysqlImprovedResult($sql, $this->_connection);
return $results;
}
public function __destruct() {
$this->_connection;
}
}
Class (Handles Result):
class utils_MysqlImprovedResult implements Iterator, Countable {
protected $_key;
protected $_current;
protected $_valid;
protected $_result;
public function __construct($sql, $connection) {
if (!$this->_result = $connection->query($sql)){
throw new RuntimeException($connection->error . '. The actual query submitted was: '. $sql);
}
}
public function rewind()
{
if (!is_null($this->_key)){
$this->_result->data_seek(0);
}
$this->_current = $this->_result->fetch_assoc();
$this->_valid = is_null($this->_current) ? false : true;
}
public function valid()
{
return $this->_valid;
}
public function current()
{
return $this->_current;
}
public function key()
{
return $this->_key;
}
public function next()
{
$this->_current = $this->_result->fetch_assoc();
$this->_valid = is_null($this->_current) ? false : true;
$this->_key++;
}
public function count()
{
$this->_result->store_result();
$this->_result->num_rows();
}
}
Class function:
public function resetPassword($email, $pass){
//check if email exists, update authkey and password, send email
$sql = "SELECT * FROM table WHERE column = '$email'";
$results = $this->_db->getResultSet($sql);
if($results->count() == 1){
// Process
$this->_message = "Success!";
return $this->_message;
} else {
// Not unique
$this->_error = "Try again";
return $this->_error;
}
}
The test page I'm using to call all this is (include statement is just __autoload() function that is working fine):
$columnvar = 'emailaddress#test.com';
$pass = 'blah';
require_once 'inc.init.php';
$user = new utils_User();
try{
$string = $user->resetPassword($email, $pass);
echo $string;
}
catch(Exception $e) {
echo $e;
}
From the manual, it seems that mysqli_result::num_rows isn't a function, but rather a variable containing the number of rows.
It can be used like this:
$num_rows = $mysqli_result->num_rows;
The function equivalent is mysqli_num_rows($result), where you pass in the mysqli_result object, but that's if you're using the procedural style rather than object oriented style.
In your code, you should change your count() function in the utils_MysqlImprovedResult class to be like this (I'm assuming that's the function where you're getting the error message),
public function count()
{
// Any other processing you want
// ...
return $this->_result->num_rows;
}
or alternatively if you want to mix OO and procedural styles (probably a bad idea),
public function count()
{
// Any other processing you want
// ...
return mysqli_num_rows($this->_result);
}

Categories