PHP OOP Programming - php

That's something I was curious. In php mysqli I can do action function over the unknown class name, for example:
$res = $mysqli->query($query);
$res->fetch_array();
That is the name of the fetch_array known, but the res variable. I would like to know how it is implemented. Sorry for the sloppy google translation.

If I understand your question correctly, then the answer is that method mysqli::query returns object that is instance of class mysqli_result. This class has method fetch_array().
Also, you may be interested in function get_class.

Before this line:
$res = $mysqli->query($query);
There was for sure mysqli connection:
$mysqli = new mysqli("localhost", "user", "password", "database");
From this, mysqli is a class which is ready for you that provided by PHP, consider its like this:
class mysqli{
}
The, $res is copy of $mysqli->query($query); which is:
here query is a property of mysqli class (a function), we do this to copy the result of mysqli query which is resource for (success) and false for failure.
You can also combine like this:
$row = $mysqli->query($query)->fetch_array();
print_r($row);

My guess is
class MySQLi{
/* other code */
public function query($query){
// some code
return new MySQLiResult(); // Don't know if that's the real class name
}
}
And the result should look like this
class MySQLiResult{
public function fetch_array(){
return array();
}
}
Note, that I've only written imaginary code. I don't really know, what the class names are.
It's only a rough estimate of how it should look.

Related

Non-static method mysqli_result::fetch_array() cannot be called statically

Since moving to PHP 8 the code below now gives me the error 'Fatal error: Uncaught Error: Non-static method mysqli_result::fetch_array() cannot be called statically...'
if (!isset($isone)) {
$seestuno=isset($_POST['seestuno']);
}
if (isset($seestuno)) {
include ('/home/username/private/dblon.php'); //database connection ($dbcnx below)
$sql="SELECT stuno,fname,lname FROM Studetails WHERE stuno=$seestuno";
$result=#mysqli_query($dbcnx, $sql);
$row=#mysqli_result::fetch_array($result); //I changed from mysql_fetch_array. I believe this is more correct?
The bottom line gives the error. I thought this was basic stuff (since I did it) so can anyone help me out? Many thanks.
mysqli_result::fetch_array ( int $mode = MYSQLI_BOTH ) is the method signature for the method "fetch_array" of the mysqli_result class. Therefore, you'd have to call it as an object method:
$row = $result->fetch_array();
Or, of course, you use the equivalent to mysql_fetch_array instead:
$row = mysqli_fetch_array($result);
There are examples at the bottom of the official docs for that. The rest of your code uses mysqli's procedural interface, so I'd stick to the later case unless all mysqli calls are converted to object oriented style.
:: is a scope resolution operator and is used to access static and constant members of a class.
fetch_array() is not a static method. You can't access it statically. It is an instance method that can be called on every object.
To call a method on an object you have to use object operator ->.
When you fix your code, it should look something like this:
if (!isset($isone)) {
$seestuno = isset($_POST['seestuno']);
}
if (isset($seestuno)) {
include '/home/username/private/dblon.php'; //database connection ($dbcnx below)
$stmt = $dbcnx->prepare("SELECT stuno,fname,lname FROM Studetails WHERE stuno=?");
$stmt->bind_param('s', $seestuno);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_array($result);
}

Zend Raw SQL Query

I'm looking to execute a raw SQL query in a Zend Controller.
I looked here http://framework.zend.com/manual/1.11/en/zend.db.statement.html and it says to do
$sql = 'SELECT * FROM bugs WHERE reported_by = ? AND bug_status = ?';
$stmt = new Zend_Db_Statement_Mysqli($db, $sql);
$stmt->execute(array('goofy', 'FIXED'));
but whenever I do that it gives me an error cause of the $db.... What's $db supposed to hold cause it doesn't say anywhere in the documentation...
Also whenever I'm done I would like to return an array with whatever the result was, what would I be able to do something like $resultArray = $stmt->execute(array('goofy', 'FIXED')); ???
Thanks,
It should be an object of type Zend_Db_Adapter.
Elsewhere in the documentation examples, the variable $db is commonly used as an instance of a Zend_Db_Adapter. I guess one is expected to read the full documentation, and notice conventions like that.
Re your comment:
You can load your application.ini file into a Zend_Config_Ini object, and then use that Zend_Config object as the argument to Zend_Db_Factory. See example here: http://framework.zend.com/manual/1.11/en/zend.db.adapter.html#zend.db.adapter.connecting.factory-config
Try this code:
Put code in bootstrap.php
protected function _initRegistry() {
$this->bootstrap('db');
$dbc = $this->getResource('db');
$dbc->setFetchMode(Zend_Db::FETCH_OBJ);
Zend_Registry::set('db', $dbc);
global $db;
$db = Zend_Registry::get('db');
}

PHP PDO Wrapper class implementation

I have been developing a wrapper class inside a small PHP framework and I am experiencing something weird with one of the method in my class.
First here is the class code :
<?php
namespace Framework;
header('Content-Type: text/html; charset=UTF-8');
class cConnexion
{
public $m_log;//This will be instantiate as a cLog object (another class in the namespace)
private $m_DB;//PDO instance
private $m_Host;//Host of the conneciton string
private $m_DBName;//Name of the DB to connecte to
private $m_Driver;//Driver name, mysql for MySQL, sqlsrv for MSSQL
private $m_Login;//Username for authentification
private $m_Password;//Password for authentification
public function __construct($p_Driver = "mysql", $p_host, $p_DBName, $p_login, $p_password)
{
$this->m_Host= $p_host;
$this->m_Driver = $p_Driver;
$this->m_DBName = $p_DBName;
$this->m_Login = $p_login;
$this->m_Password = $p_password;
$this->m_log = new cLog('', '', true, false, false);
}
public function SecureExecute($p_query, $p_param)
{
try
{
$stmt = $this->m_DB->prepare($p_query);
$status = $stmt->execute($p_param);
$this->m_log->setMessageFR("Aucune exception ne s'est levé. ");
$this->m_log->setMessageEN("No exceptions were raised. ");
$this->m_log->setSuccess($status);
return $status;
}
catch(\PDOException $e)
{
$this->m_log->setMessageFR("Une exception de type PDO est levé. ".$e->getMessage());
$this->m_log->setMessageEN("A PDO exception was raised. ".$e->getMessage());
$this->m_log->setSuccess(false);
return false;
}
}
}
?>
Note that I have remove every other method that are not relevant to the question but kept the constructor and all the properties. Also note that there is a method to connect the $m_DB property to the DB, you might want to assume it has already been called.
Here is the problem I need help solving:
Step by Step description of what is going on:
I create an instance of cConnexion and use the connecteToDB method that isn't described in here but it is working properly because I can use another method to do MySQL 'SELECT' statement.
I tried to UPDATE a row which doesn't exist in a MySQL table but the object still tell me that the UPDATE was successful.
Is that normal? I read the PDO::PDOStatement::execute Doc and is says that the return value of PDO::PDOStatement::execute is either true (on success) or false(if fails).
Here is an exemple of code using the method:
$sql = "UPDATE Employes SET CieNo = 3, Nom = :Name, Dept = :Department WHERE EmplCode = :Code";
$params = array
(
'Code'=>$EmplCode,// = 123
'Name'=>($lname." ".$fname),// = Lalonde Sebastien
'Department'=>$dep,// = INFO
);
$cn = new cConnexion(/*Connection string and params here*/);
$cn->connectToDB();
if($cn->SecureExecute($sql, $params))
{
echo "SQL1: true";
}
The following code always output "SQL1: true" even when the UPDATE shouldn't had work...(because the Employes table doesn't contains an EmplCode = 123).
Can someone suggest a way to change my class so that the method SecureExecute() return false when this happen?
The execute method returns true on success, which in your case is true: The mysql query was executed successfully, however no rows were effected.
If you need to check whether the update actually altered anything, you need to use "rowCount":
$stmt = $this->m_DB->prepare('Update ...');
$stmt->execute();
$effected = $stmt->rowCount();
You can then decide if you want to return a true/false value from your method.
http://www.php.net/manual/en/pdostatement.rowcount.php
You'll only receive an error if the SQL command itself is in error. So if Employes is a valid table, and CieNo, Nom, Dept, and EmplCode are all valid columns, then your SQL statement is valid, even though it doesn't always find a row to UPDATE. Technically, it was "successful", just not in the way you're wanting.
Take a look at rowCount()
for info on how to determine whether 0 or more rows were affected by your query.
In fact, this whole class appears to be utterly useless. Exactly the same outcome can be achieved with raw PDO, with very small addition.
$cn = new PDO(/*Connection string and params here*/);
$cn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$stmt = $cn->prepare($sql);
$stmt->execute($params);

Efficiently use mysqli in object oriented setting

I have come to the conclusion that using mysqli in an OO approach is better than a procedural approach. (Source: Why is object oriented PHP with mysqli better than the procedural approach?). But I'm not quite sure if what I am doing is really all that more efficient than what I was doing before.
I have a function that runs sql queries. This is what my block of code looked like:
Database connection:
function connectDB(){
$con = mysqli_connect(server, username, password, database);
return $con;
}
Query function:
function executeQuery($payload){
$con = connectDB;
$result = mysqli_query($con, $payload);
return $result;
}
As you can see, that's not very efficient because I'm creating a new database connection every time executeQuery is called. So I figured I'd try it using OOP.
Database connection (OOP):
function connectDB(){
$con = new mysqli(server, username, password, database);
return $con;
}
Database query (OOP):
function executeQuery($payload){
$con = connectDB();
$result = $con->query($payload);
return $result;
}
Now to me, it seems that I am obviously doing something wrong. Each time a query is called I am re-instantiating the mysqli class and I assume that mean's that I am making another database connection.
So how do I do this properly and efficiently?
So how do I do this properly and efficiently?
This really has nothing to do with using MySQLi in a procedural versus OOP way.
What this has to do with is the following line:
$con = connectDB();
This will recreate the database connection on every query. Which, as you noted, is not efficient.
There are many ways to solve this. For example:
Use the mysqli class directly.
Pass $con to executeQuery() (Dependency Injection)
Create a DB class with both connectDB() and executeQuery().
I usually use mysqli directly as I see no reason to wrap the native class. I create the connection object globally (in a config file) and use Dependency Injection when other objects/functions need it.
Although your procedural approach can be solved pretty easily
function connectDB(){
return mysqli_connect(server, username, password, database);
}
function executeQuery($payload){
static $con;
id (!$con)
{
$con = connectDB();
}
return $con->query($payload);
}
an OOP approach would be indeed better. I am not an OOP pro, but you can take a look at my approach which at least using encapsulation to hide all the dirty job inside and provide concise methods to get the data already in desired format like this:
// to get a username
$name = $db->getOne('SELECT name FROM table WHERE id = ?i',$_GET['id']);
// to get an array of data
$data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit);
// to get an array indexed by id field
$data = $db->getInd('id','SELECT * FROM ?n WHERE id IN ?a','table', array(1,2));
// to get a single dimensional array
$ids = $db->getCol("SELECT id FROM tags WHERE tagname = ?s",$tag);
// a simple code for the complex INSERT
$data = array('offers_in' => $in, 'offers_out' => $out);
$sql = "INSERT INTO stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE ?u";
$db->query($sql,$pid,$data,$data);
As a solution for your exact problem : "You do not want to instantiate a new MySQL connection for each time a query is executed" ,
Well, we can think about the following :
You need to make your connection variable ($con) in GLOBAL scope, such that when accessed through any function you can grab THAT variable you set before, not instantiate a new one.
we can do this using keyword "global" , as following :
The connection function :
function &connectDB(){
global $con;
if(empty($con)) {
$con = new mysqli(server, username, password, database);
}
return $con;
}
And for more performance , we avoid cloning/copying the connection variable/resource by using reference function ( &connectDB ),
Query Execution function
Now we've set the connection function in a flexible way , to set the queryExecution function , we can use more than one solution :
First solution :
function executeQuery($payload){
$con = &connectDB(); // do not forget the () , it's good practice
return $con->query($payload);
}
In this solution we made use of "reference" , so the expression :
$con = &connectDB();
will set the variable $con as a reference/shortcut for global $con (i.e : just pointing to the global variable $con)
or
Second solution :
function executeQuery($payload){
global $con;
return $con->query($payload);
}
but for the second solution : Function "connectDB()" MUST be called at least once before any calling to "executeQuery()", In order to make sure that there a connection has been established with the database,
Keep in mind that, according to this solution , calling "connectDB()" more than once will not create more than one connection , once it is called , connection is created, if called again it will return the PREVIOUSLY created connection.
Hope it helps :)
by the way : stay with the OOP approach for database connection , it has much more benefits over the procedural ways,
& I recommend using PDO, it is much more portable.

Set a PHP object global?

I just started switching my project form the mysql to PDO. In my project a new PDO Object is created more or less right a the beginning of the programm.
$dbh_pdo = new PDO("mysql:host=$db_url;dbname=$db_database_name", $db_user, $db_password);
Now I would like to use this handler (is that the correct name?) in some functions and classes. Is there a way to make objects global just like variables or am I trying something unspeakably stupid, because I couldn't find anything when searching the web ...
Yes, you can make objects global just like any other variable:
$pdo = new PDO('something');
function foo() {
global $pdo;
$pdo->prepare('...');
}
You may also want to check out the Singleton pattern, which basically is a global, OO-style.
That being said, I'd recommend you not to use globals. They can be a pain when debugging and testing, because it's hard to tell who modified/used/accessed it because everything can. Their usage is generally considered a bad practice. Consider reviewing your design a little bit.
I don't know how your application looks like, but say you were doing this:
class TableCreator {
public function createFromId($id) {
global $pdo;
$stmt = $pdo->prepare('SELECT * FROM mytable WHERE id = ?');
$stmt->execute(array($id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
// do stuff
}
}
}
You should do that instead:
class TableCreator {
protected $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function createFromId($id) {
$stmt = $this->pdo->prepare('SELECT * FROM mytable WHERE id = ?');
$stmt->execute(array($id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($rows as $row) {
// do stuff
}
}
}
Since the TableCreator class here requires a PDO object to work properly, it makes perfect sense to pass one to it when creating an instance.
You'll use $GLOBALS['dbh_pdo'] instead of $dbh_pdo inside any functions. Or you can use the global keyword, and use $dbh_pdo (i.e. global $dbh_pdo).
You could also try using a Singleton to pass back a PDO object to you. That way you only ever have one PDO object (and one database connection) in any request which saves on memory/server resources.

Categories