Call to undefined function mysqli_result::num_rows() - php

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);
}

Related

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!

pass database connection to phpunit test

i am trying to test a function that executes a select statement, but im a bit confused about the way i should pass the connection to this function and how to assert the data.
this is the class with the function i want to test:
class AdProviders {
public $providers = null;
protected $db = null;
function __construct() {
}
function getDbh() {
if ($this->db === null){
$this->db = Slim::getInstance()->db;
}
return $this->db->getConnection();
}
function setDbh($db) {
$this->db = $db;
}
function getProviders() {
if ($this->providers == null){
$DBH = $this->getDbh();
$query = "select * from providers";
$STH = $DBH->query($query);
$STH->setFetchMode(PDO::FETCH_ASSOC);
while($provider = $STH->fetch()) {
$this->providers[$provider['id']] = $provider;
}
}
return $this->providers;
}
}
and this is my test:
require dirname(__FILE__).'/../../src/vendor/autoload.php';
class AdProvidersTest extends PHPUnit_Extensions_Database_TestCase
{
/**
* #return PHPUnit_Extensions_Database_DB_IDatabaseConnection
*/
public function getConnection()
{
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'root', 'LWw6A$cXvvf');
return $this->createDefaultDBConnection($pdo, 'testdb');
}
/**
* #return PHPUnit_Extensions_Database_DataSet_IDataSet
*/
public function getDataSet()
{
return $this->createXMLDataSet(dirname(__FILE__).'/../dbTest/fixtures/providers.xml');
}
public function testgetProviders_compareResult()
{
$db = $this->getConnection();
$fixture = new AdProviders($db);
$res = $fixture->getProviders();
}
}
when i run the test function now i get the following error:
1) AdProvidersTest::testgetProviders_compareResult
Trying to get property of non-object
why am i getting it? and how can i get the data inside the providers.xml
using the getProviders? thx
You have to change the constructor of AdProviders class to that the PDO instance can be passed to it. Otherwise $fixture = new AdProviders($db); will not work.
Please change
function __construct() {
}
to
function __construct($db) {
$this->db = $db;
}
UPDATE:
I saw that you are not storing a PDO instance in your AdProviders::$db property. If your are passing a PDO instance to your __construct you should also change your method setDbh from
function getDbh() {
if ($this->db === null){
$this->db = Slim::getInstance()->db;
}
return $this->db->getConnection();
}
to
function getDbh() {
if ($this->db === null){
$this->db = Slim::getInstance()->db->getConnection();
}
return $this->db;
}

Class methods over different files

I am attempting to place a commonly used method (opendb) in the same class and file as my connect configuration file (connect.php) See fig A
class qcon{
public static $conn;
function dbcon()
{
if (empty($conn))
{
$host = 'x';
$username = 'x';
$password = 'x';
$dbname = 'x';
$conn = mysqli_connect($host , $username , $password ,$dbname) or die("Oops! Please check SQL connection settings");
}
return $conn;
}
function openDB($conn)
{
if (!$conn)
{
$this->error_msg = "connection error could not connect to the database:! ";
return false;
}
$this->conn = $conn;
return true;
}
Now I want to be able to pass the connection output of fig A so I can properly use the methods in another class file. Call it class.php. Here's one example function on class.php for viewing records. See fig. B
require_once("assets/configs/connect.php");
class dbcats {
var $conn;
function getResult(){
$result = mysqli_query($this->conn , "SELECT * from felines" );
if ($result) {
return $result;
}
else {
die("SQL Retrieve Error: " . mysqli_error($this->conn));
}
}
function closeDB() {
mysqli_close($this->conn);
}
Now, to get the call to work, Fig C below is where I'm at. I'm a tiny bit stuck.
$db1 = new qcon();
$helper = new dbcats();
$db1->openDB();
$helper = $db1;
$result = $helper->getResult();
So here we are. The logic is simple enough (I'll update the question if I'm not quite clear) So would someone advise on what amendments I need to get the call operational?
Orangepill's solution is fine and will more than likely get you going quickly. However, I would recommend making some minor alterations to your classes to allow you to more easily reuse their functionality across your programs.
Here, qcon holds your database connection information and functionality. It has a getter method, getConn() that allows you to get, and pass around that connection as you need.
class qcon
{
protected $conn;
public function __construct() { ... }
public function dbcon() { ... }
public function openDB() { ... }
public function closeDB() { ... }
public function getConn() { return $this->conn; }
}
Here's an example of an alternative dbcats class. It takes in your qcon class in part of its construction and holds it in as a protected member variable that it can use whenever it needs it. It also has getters and setters so that you can change or retrieve your database connection for this class at any time via getQConn() and setQConn().
class dbcats
{
protected $qcon;
public function __construct(qcon $q) { $this->qcon = $q; }
public function getResult() { ... }
public function getQConn() { return $this->qcon; }
public function setQCon(qcon $q) { $this->qcon = $q; }
}
It may not be the quickest fix, but I believe practices such as this will serve you better in the long-run.
From what I can see you are missing the end curly bracket } on both your classes. Everything would be much easier to see if you make the indentation correctly. That is, each time you have a left curly bracket { the following lines will be indented with one tab. Like this:
class qcon {
public static $conn;
function dbcon()
{
if (empty($conn))
{
$host = 'x';
$username = 'x';
$password = 'x';
$dbname = 'x';
$conn = mysqli_connect($host , $username , $password ,$dbname) or die("Oops! Please check SQL connection settings");
}
return $conn;
}
function openDB($conn)
{
if (!$conn)
{
$this->error_msg = "connection error could not connect to the database:! ";
return false;
}
$this->conn = $conn;
return true;
}
} <<< Missing this one
class dbcats {
var $conn;
function getResult(){
$result = mysqli_query($this->conn , "SELECT * from felines" );
if ($result) {
return $result;
}
else {
die("SQL Retrieve Error: " . mysqli_error($this->conn));
}
}
function closeDB() {
mysqli_close($this->conn);
}
} <<< Missing this one
You need to inject an instance of the qcon class into the dbcats class.
require_once("assets/configs/connect.php");
class dbcats {
var $conn;
public function __construct(qcon $dbconn){
$this->conn = $dbconn;
}
function getResult(){
$result = mysqli_query($this->conn , "SELECT * from felines" );
if ($result) {
return $result;
}
else {
die("SQL Retrieve Error: " . mysqli_error($this->conn));
}
}
function closeDB() {
mysqli_close($this->conn);
}
}
Then when you create an instance of dbcats pass in an instance of conn like ...
$db1 = new qcon();
$db1->openDB();
$helper = new dbcats($db1);
$result = $helper->getResult();

PHP mysql_query transactions not rolling back

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.

Extending mysqli and using multiple classes

I'm new to PHP oop stuff.
I'm trying to create class database and call other classes from it. Am I doing it the right way?
class database:
class database extends mysqli {
private $classes = array();
public function __construct() {
parent::__construct('localhost', 'root', 'password', 'database');
if (mysqli_connect_error()) {
$this->error(mysqli_connect_errno(), mysqli_connect_error());
}
}
public function __call($class, $args) {
if (!isset($this->classes[$class])) {
$class = 'db_'.$class;
$this->classes[$class] = new $class();
}
return $this->classes[$class];
}
private function error($eNo, $eMsg) {
die ('MySQL error: ('.$eNo.': '.$eMsg);
}
}
class db_users:
class db_users extends database {
public function test() {
echo 'foo';
}
}
and how I'm using it
$db = new database();
$db->users()->test();
Is it the right way or should it be done another way?
Thank you.
You can do it that way, there's nothing wrong with that (I do something similar quite often). The only thing I would suggest is using exceptions instead of die (that way you can safely handle the error)...
protected function error($eNo, $eMsg, $extra = '') {
throw new Exception('MySQL error: ['.$eNo.'] '.$eMsg.': '.$extra);
}
Plus, I'd suggest overloading the query method as well
public function query($sql, $result_mode = MYSQLI_STORE_RESULT) {
$result = parent::query($sql, $result_mode);
if ($result === false) {
$this->error($this->errno, $this->errstr, $sql);
}
return $result;
}
I'd also suggest storing a copy of the $db object inside of the child class. So:
class db_users extends database {
protected $db = null;
public function __construct(Database $db) {
$this->db = $db;
}
public function test() {
echo 'foo';
}
}
Then, in __call:
if (!isset($this->classes[$class])) {
$class = 'db_'.$class;
$this->classes[$class] = new $class($this);
}
There is nothing wrong with this factory style for creating classes. I'd place a bit of exception handling in it.
My only other concern is extending database in your sub classes.
So I'd modify as follows:
public function __call($className, $args) {
if (!isset($this->classes[$class])) {
if(include_once('db_'.$class)) {
$class = 'db_'.$class;
$this->classes[$class] = new $class($this);
} else {
throw new Exception("Db class not found");
}
}
return $this->classes[$class];
}
And the users class as:
public class db_users {
private $db;
public __constructor($db) {
$this->db = $db;
}
public function test() {
return 'Foo';
}
}

Categories