I have a somewhat general question regarding what's best when programming in PHP using also database connections. I am building a project which includes several modules and each module needs to connect to the MySQL sometimes. The module files are included in the main index.php depending on the action selected from the menu by the user. I guess, most projects work this way anyway.
So far what I do is always open the connection at the start of each module file and close it after the queries have run.
My question is this: is it better to open the connection to the database in the beginning of the index.php and close it in the end so to have 1 connection open, or do multiple connections which stay open for less time? What's best for speed and overhead?
As N.B. pointed out, you should probably setup a class to handle all your database related tasks. I am posting a snippet from my code archives to illustrate how you can setup such a class. This is solely for purposes of illustrating and not guaranteed to work for you if you just copy and paste. It may need some refinement. It also makes use of the smarty class to process data views.
I recommend stripping out whatever you don't need. Set your MySQL connection params. In your index file, you can instantiate a SQL object and simply call the appropriate methods. The methods return and keep your result as both an associative and indexed array. Iterating through table rows is as easy as:
$SQL->GetRows('RowTemplate.tpl', 'StoredProcedure', 'Parameters');
FYI This is part of a bigger $Portal framework object in case you're wondering what the $Portal reference is to. $SQL merely extends $Portal.
I hope this helps. Good luck
class SQL {
/********** MEMBER VARIABLES **********************************************************************/
var $Connection; // DB connection handler
var $Result; // ResultSet returned from last call during life of object instance
var $RowCount; // RowCount for ResultSet returned from last call during life of object instance
var $ResultArray; // ResultSet Array returned from last call during life of object instance
var $Query; // Query last submitted during life of object instance
var $ErrorNumber; // Error number for error returned from last call during life of object instance
var $Error; // Error returned from last call during life of object instance
var $Message; // Messages returned during life of object instance
// Switches, flags, markers, etc
var $DebugMode;
var $LogActive;
var $ShowErrorMsg;
// Modules array
var $Modules;
// SQL Connection Info - PROTECTED!
protected $Host = "localhost";
protected $User = "mydatabase";
protected $Password = "mypassword";
protected $Schema = "myschema";
/********** MEMBER FUNCTIONS **********************************************************************/
// Object Constructor
function __construct() {
// Automatically open DB Connection
//$this->OpenDBConnection();
//echo "User Object Constructor<br>";
}
// Open new DB Connection
function OpenDBConnection() {
return ($this->Connection = mysqli_connect($this->Host, $this->User, $this->Password, $this->Schema))? true : false;
}
// Close DB Connection
function CloseDBConnection() {
mysqli_close($this->Connection);
//return true;
}
// Return error messages
function GetError() {
return $this->Error;
}
// Return last query string
function GetQuery() {
return $this->Query;
}
// Call, execute stored procedure and return result set
/* NOTES: The result set is always returned as an int array of associative arrays
That is, if $Result was returned, the first row would be referenced as $Result[0]
and any column of the first row would be referenced as $Result[0]['ColumnName']
COMMENTS: */
function CallProcedure($StoredProcedure) {
// Clear any System Errors
$this->ErrorNumber = '';
$this->Error = '';
// Open DB Connection
if(!$this->OpenDBConnection()) return false;
// Kill error if there are no extra Params passed
$Params = #func_get_arg(1);
// Build Query
$this->Query = "CALL $StoredProcedure ($Params)";
//if($this->Result = $this->Connection->query($this->Query)) {
if($this->Result = mysqli_query($this->Connection, $this->Query)) {
// Reset global Result Set
$this->ResultArray = Array();
// Set record count for current record set
$this->RowCount = 0;
while($Row = #mysqli_fetch_array($this->Result, MYSQLI_BOTH)) {
$this->ResultArray[$this->RowCount] = $Row;
$this->RowCount++;
}
// Close DB Connection
$this->CloseDBConnection();
return $this->ResultArray;
}
// Grab Error
$this->ErrorNumber = mysqli_errno($this->Connection);
$this->Error = mysqli_error($this->Connection);
// Close DB Connection
$this->CloseDBConnection();
return false;
}
/* Using Smarty class, return row template filled with record set from given stored procedure
EXAMPLE 1: Primary Function - Using data set from stored procedure
$Portal->SQL->GetRows('RowTemplate.tpl', 'StoredProcedure', 'Parameters');
EXAMPLE 2: Secondary Function - Using data set in second dimensional associative array
$Portal->SQL->GetRows('RowTemplate.tpl', 'ARRAY', $MyDataSetArray); */
function GetRows($RowTemplate, $Procedure) {
// Kill error if there are no extra Params passed
$Parameters = #func_get_arg(2);
// Set return string
$ReturnString = '';
// If Procedure is ARRAY then params are data set else Call procedure and return results array
$Result = ($Procedure=='ARRAY')? $Parameters : $this->CallProcedure($Procedure, $Parameters);
// Loop through result set initializing smarty obj for each row
$Count = 0;
while(IsSet($Result[$Count])) {
$RowTemplateObj = new Smarty;
$RowTemplateObj->assign('SCRIPT_NAME', SCRIPT_NAME);
$RowTemplateObj->assign('HOST_NAME', HOST_NAME);
// Loop though each result row as an associative array of column - values
foreach ($Result[$Count] as $Key => $Value) {
if(IsSet($Result[$Count][$Key])) $RowTemplateObj->assign($Key, (is_array($Value))?$Value:stripslashes($Value));
//if(IsSet($Result[$Count][$Key])) $RowTemplateObj->assign($Key, $Value);
}
$RowTemplateObj->assign('bgcolor', '{$bgcolor'. ($Count%2 + 1) .'}');
// Concatenate populated row into return string
$ReturnString .= $RowTemplateObj->fetch($RowTemplate);
$Count++;
}
return $ReturnString;
}
function GetSelectList($Procedure, $Parameters, $OptionValueField, $OptionNameField) {
// Kill error if there are no extra Params passed
$Selected = #func_get_arg(4);
// Set return string
$ReturnString = '';
// Get List Resultset
$Result = $this->CallProcedure($Procedure, $Parameters);
// Loop through result set and set <option> ta row
$Count = 0;
while(IsSet($Result[$Count])) {
$ReturnString .= '<option value="'.$Result[$Count][$OptionValueField].'"';
$ReturnString .= ($Selected==$Result[$Count][$OptionValueField])? ' selected ' : '';
$ReturnString .= '>'.$Result[$Count][$OptionNameField].'</option>';
$Count++;
}
return $ReturnString;
}
function Execute($SQL) {
// Clear any System Errors
$this->ErrorNumber = '';
$this->Error = '';
// Open DB Connection
if(!$this->OpenDBConnection()) return false;
// Assign Query
$this->Query = $SQL;
if($this->Result = mysqli_query($this->Connection, $this->Query)) {
// Reset global Result Set
$this->ResultArray = Array();
// Set record count for current record set
$this->RowCount = 0;
while($Row = #mysqli_fetch_array($this->Result, MYSQLI_BOTH)) {
$this->ResultArray[$this->RowCount] = $Row;
$this->RowCount++;
}
// Close DB Connection
$this->CloseDBConnection();
return $this->ResultArray;
}
// Grab Error
$this->ErrorNumber = mysqli_errno($this->Connection);
$this->Error = mysqli_error($this->Connection);
// Close DB Connection
$this->CloseDBConnection();
return false;
}
MAKE Saperate connections in same file and close them according.
/*****Connection for portal1 ******/
$portal1_link = mysql_connect('localhost','root','') or die('Cannot connect to the DB');
mysql_select_db('mylekha_auction',$portal1_link) or die('Cannot select the DB');
/*****Connection for portal2 ******/
$portal2_link = mysql_connect('localhost','root','') or die('Cannot connect to the DB');
mysql_select_db('mylekha_auction',$portal2_link) or die('Cannot select the DB');
Related
I am working to create a content class which will pull all content from the database via a PDO, then run the results through a method which will convert the results into objects, and the loop through the objects, and echo the object to a specific module of the webpage.
What is expected
When the class is activated, there will be an instance created via a PDO that will retrieve the needed data from the mysql database. Then this information will echo out and display on the webpage.
What is happening
I end up with no errors and the boolean number "1" where content should be. At first I thought this was an sql error. I get the same result "1" if I use print_r($query_result). See my tests below.
My class code is below
<?php
class Content {
// --- START OF ACTIVE RECORD CODE ---
public $n_id;
public $n_name;
public $n_text;
public $n_photo;
// Instantiating a STATIC Database Connection for the class to use
static protected $dbconnect;
static public function database($dbconnect) {
self::$dbconnect = $dbconnect;
}
// constructing arguments
public function __construct($args=[]) {
$this->n_id = $args['n_id'] ?? '';
$this->n_name = $args['n_name'] ?? '';
$this->n_text = $args['n_text'] ?? '';
$this->n_photo = $args['n_photo'] ?? '';
}
// Multi use method to pass in sql that will execute the PDO only two parameters are bound ID and contentText.
static public function find_by_sql($sql) {
// -------------BEGIN PDO-----------
// preparing PDO by loading sql, calling db connection, and storing in variable $stmt
$stmt = self::$dbconnect->prepare($sql);
// Binding Parameters for sql
$stmt->bindParam(':nid', $n_id, PDO::PARAM_INT);
$stmt->bindParam(':nall', $n_name, PDO::PARAM_STR);
$stmt->bindParam(':ntext', $n_text, PDO::PARAM_STR);
$stmt->bindParam(':nphoto', $n_photo, PDO::PARAM_INT);
// executing $stmt PDO and storing the result in $stmt
$query_result = $stmt->execute();
return $query_result;
// clearing the PDO after information is stored in $record
$stmt->closeCursor();
// ------------END PDO ----------
// Checking to see if a result exist. If nop result is stored in $stmt, then it will echo "Database query failed."
if(!$query_result) {
exit("Query doesn't exist.");
}
// -------- BEGIN TURNING RESULTS INTO OBJECTS --------
$object_array = [];
// The result $stmt will be stored in the variable $record
while($record = $query_result->fetchAll()) {
// Taking $record and passing it to the static method instantiate() - see below. This method will return the $object_array.
$object_array[] = self::instantiate($record);
}
return $object_array;
// ------------ END TURNING RESULTS INTO OBJECTS --------
}
// method to test passing $sql to method find_all_sql();
static public function find_all(){
$sql = "SELECT * FROM nmain WHERE nid = :id AND nall = :nall AND ntext = :ntext AND nphoto = :nphoto";
return self::find_by_sql($sql);
}
// --- BEGIN INSTANTIATE METHOD TO CREATE OBJECTS ---
static protected function instantiate($record) {
$object = new self;
// Auto assign values
foreach($record as $property => $value) {
if(property_exists($object, $property)){
$object->$property = $value;
}
}
return $object;
}
// ----- END INSTANTIATE OF RECORD TO CREATE OBJECTS ---
// ---END OF ACTIVE RECORD CODE---
}
?>
**On my html webpage:**
$contents = Content::find_all();
foreach ((array) $contents as $content) {
echo $content;
}
What I have tested
This is the output I get when I run var_dump($stmt);
object(PDOStatement)#3 (1) { ["queryString"]=> string(119) "SELECT * FROM ndb WHERE id = :id AND nall = :nall AND ntext = :ntext AND nphoto = :nphoto" }
If I copy the query and paste it in myphpadmin the query will run binding the params.
This is the output if I run var_dump($query_result):
bool(true) if I use print_r($query_result) I get "1"
This passes my if(!$query_result) test
If I run var_dump($record) or var_dump($query_result) I get nothing. It seems as if $query_result, because it is a bool, has no array to pass to $record.Therefore, there is nothing to convert to an object.I am at a loss here. Is it my PDO binding?
Your fetch should be on the statement and not the result of the execute (which is just to say the execute has succeeded or failed), also fetchAll will attempt to return all records, what you most likely want is fetch to process 1 record at a time. So you should have something like...
while($record = $stmt->fetch()) {
You can now remove the earlier return which is stopping further processing.
Right now, I'd wrote a function in my model as:
public function getRowsByZipCode($zip)
{
// SQL to get all the rows with the given zip code
$stmt = $this -> getAdapter()
-> query( "SELECT *
FROM
table_name
WHERE
table_name.status = 1 AND
table_name.zip={$zip}");
$resultRows = $stmt->fetchAll();
// -------------------------------------------------------- //
// Convert result set to an array of objects
$resultObjects = array();
// If there is atleast one row found in DB
if(count($resultRows) > 0)
{
// Loop throguh all the rows in the resultset
foreach($resultRows as $resultRow) {
// Create table row and fill it with the details got from DB
$h = $this->createRow();
$h->setFromArray($resultRow);
// Add to the array
$resultObjects[] = $h;
}
}
return $resultObjects;
// -------------------------------------------------------- //
}
Which is working perfectly as I needed. And it is returning me an array that contains the tables row objects(App_Model_TableName Objects), which will be used later for further operations like save and delete etc.
What I really want is to remove the code that loop through the rows I got from the result set and converting each row to an object of App_Model_TableName that I'd wrote inside the comments // --- //.
Thanks in advance.
Firstly, I am assuming you are using PDO.
Try the following
class App_Model_TableName
{
public $status;
public $zip;
// public $other_column;
}
class YourClass
{
protected function getAdapter()
{
// Do adapter stuffs
}
public function query($query, array $param)
{
// When Using PDO always use prepare and execute when you pass in a variable
// This will help prevent SQL injection
$stmt = $this->getAdapter()->prepare($query);
return $query->execute($param);
}
/**
* #return App_Model_TableName[]
*/
public function getRowsByZipCode($zip)
{
// SQL to get all the rows with the given zip code
// This way will help prevent SQL injection
$query = "SELECT * FROM table_name WHERE table_name.status = 1 AND table_name.zip = :zip";
$qData = array(':zip' => $zip);
$results = $this->query($query, $qData);
return $results->fetchAll(PDO::FETCH_CLASS, 'App_Model_TableName');
}
}
Calling YourClass::getRowsByZipCode() will then return you an array of App_Model_TableName objects. You can then access them like:
$data = $instance_of_yourclass->getRowsByZipCode(12345);
foreach ($data as $row)
{
echo $row->zip;
echo $row->do_stuff();
}
All these awesome functions I found on:
http://php.net/manual/en/pdostatement.fetchall.php
http://php.net/manual/en/pdostatement.execute.php
Disclaimer: this code was not tested :(
Be cool but stay warm
Finally, I had found the solution:
public function getRowsByZipCode($zip)
{
// SQL to get all the rows with the given zip code
$stmt = $this -> getAdapter()
-> query( "SELECT *
FROM
table_name
WHERE
table_name.status = 1 AND
table_name.zip={$zip}");
$resultObjects= array();
while($data = $stmt->fetch())
{
$h = $this->createRow();
$h->setFromArray($data);
// Add to array
$resultObjects[] = $h;;
}
return $resultObjects;
}
I had removed the code that do the fetchAll() and loop through each row in the resultset. Now, I am taking each row from the resultset and creating an row of App_Model_TableName object using the data we got from the resultset.
Working perfectly for me.
I write a class to load data and export as SMARTY format through MySQLi
public function myRow($sql)
{
$this->connect();
$rec = $this->conn->query($sql);
$this->recordCount = $rec->num_rows;
if ($this->makeRecordCount) {
$this->totalRecordCount = $this->recordCount;
}
if ($this->recordCount > 0) {
$names = array();
$result = array();
$temp = array();
$count = $rec->field_count;
// Get fields name
while ($fields = mysqli_fetch_field($rec)) {
$names[] = $fields->name;
}
while ($row = $rec->fetch_assoc()) {
foreach ($names as $name) {
$temp[$name] = $row[$name];
}
array_push($result, $temp);
}
} else {
$result = null;
}
$this->conn->close();
return $result;
}
Then I can to something like
$sql = "SELECT * FROM `table`";
$datas = $class->myRow($sql);
$smarty->assign('datas', $datas);
There are maybe many data need to be loaded in one page, and I only want to connect to database once, but I want to do it all in class, I don't want to do something like
$class->connect();
$sql = "SELECT * FROM `table`";
$datas = $class->myRow($sql);
$smarty->assign('datas', $datas);
$sql = "SELECT * FROM `table2`";
$datas = $class->myRow($sql);
$smarty->assign('data2s', $data2s);
$class->close();
I feel it's ugly, but if I do this in class, that means I open and close connection when each data is loading, how to do it more beautiful?
maybe i'm wrong but you don't need to force a mysql connection to close because of the fact that if the connection is not persistent the php garbage collector close all connections after the script execution.
so i suggest you not to force the mysql close, let the garbage collector handle this task and only close the connection by yourself if you're sure that no more mysql transactions are required.
You simply don't need to (and shouldn't) open/close the connection inside your myRow() function.
Option 1 (naive approach): handle the connection at class level
class MyDAOClass {
private static $connection = null;
public function __construct() {
if (self::$connection === null) {
// establish connection here
}
}
public function myRow(...) {
// use self::$connection here
}
}
Option 2:
Handle the connection from outside the class altogether (possibly in a singleton class), since all objects from your application probably can share the same object.
Your second suggestion is what I would do.
$class->connect();
$sql = "SELECT * FROM `table`";
$datas = $class->myRow($sql);
$smarty->assign('datas', $datas);
$sql = "SELECT * FROM `table2`";
$datas = $class->myRow($sql);
$smarty->assign('data2s', $data2s);
$class->close();
You connect to the database once. As PHP is single threaded, you will load the first result, then go right away and load the second result. Once everything is done, you close the connection. No connection is kept alive longer than it has to, which is good.
What I usually do it make a method associated with Smarty that closes my database-connection too. That way I don't have to worry about closing it.
Something like this:
<?php
// Store reference to Smarty in Class
$class->setSmarty($smarty);
[...]
// Done with all database fetching, now display the template
$class->display('my_template.tpl');
[...]
// Example inplementation of the class
Class YourClass {
private $smarty;
public function setSmarty($smarty) {
$this->smarty = &$smarty;
}
public function display($tpl) {
$this->close();
$this->smarty->display($tpl);
}
}
?>
I am trying to retrieve a list of items from a mySQL db and insert them as a list in a select object on a webpage. The following is the bit of code that isnt working.
In the first line, I am trying to retrieve a JSON object from a public function called getBrands() in a singleton object I have created called DatabaseInterface.
The second line is then attempting to turn that JSON object into a php array.
Finally, I am running a loop which can option each item in between tags for the webpage.
Where am I going wrong?
<?php
var $brandArrayJSON = DatabaseInterface::getBrands();
$brandArray = JSON_decode($brandArrayJSON);
for ($loop=0; $loop < sizeof($brandArray); $loop++) {
echo "<option>$brandArray[$loop]</option>";
}
?>
EDIT: In case it helps, here is my DatabaseInterface singleton. I have included this file at the top of my php file
class databaseInterface {
private static $_instance;
// Private constructor prevents instantiation
private function __construct() {
}
public static function getInstance() {
if (!self::$_instance) {
self::$_instance = mysqli_connect(self::databaseHost, self::databaseUsername, self::databasePassword, self::databaseName);
if (mysqli_connect_errno(self::$_instance)) {
throw new Exception("Failed to connect to MySQL:" . mysqli_connect_error());
}
}
return self::$_instance;
}
public function getBrands() {
try {
$con = DatabaseInterface::getInstance();
} catch (Exception $e) {
// Handle exception
echo $e->getMessage();
}
$query = "SELECT psBrandName from brands";
$result = mysqli_query($con, $query) or die ("Couldn't execute query. ".mysqli_error($con));
$resultArray[] = array();
while ($row = mysqli_fetch_assoc($result)) {
extract($row);
$resultArray[] = $psBrandName;
}
return json_Encode($resultArray);
}
There is nothing "wrong" with the code, in that it should work (provided nothing is broken on the query-side). However, there are several things that should be improved.
First, basically what the getBrands() method is doing is equivalent to this:
$brandArray = json_encode(array('test','test2','test3'));
echo $brandArray; // returns ["test","test2","test3"]
Now, when you decode that you get the same thing you originally put in (an array):
$brandArray = json_decode('["test","test2","test3"]');
var_dump($brandArray); // Will dump an array
Since this is an array (not a PHP object), you can just use a foreach.
foreach($brandArray as $option) {
echo '<option>', $option, '</option>';
}
If you're worried about it being an object in some instances (maybe you had a non-array JS object which would be mostly the equivalent to a PHP associative array), you could cast the json_decode result into an array.
$brandArray = (array)$brandArray;
Now, in your getBrands() method, I would highly recommend just using $row['psBrandName'] instead of cluttering things up with extract, unless you have a really good reason to do this.
PHP/MySQLisolating database access in class - how to handle multiple row Selects
Here’s a coding question.
I isolated all DB access functions in a class
<?php
class DB {
var $conn;
function DBClass () {
#$this-> conn = mysqli_connect (DB_SERVER, DB_USER, DB_PASS, DB_NAME);
}
function validateUser ($aUserid, $aPassword) {
… validation code – sql injection code etc..
$sql = "Select userid, name, level From users where userid = '$aUserid' and password = '$aPassword'";
$result = mysqli_query ( $this->conn, $sql );
if (!$result || (mysqli_num_rows ($result) < 1)) {
return false;
}
$dbarray = mysqli_fetch_assoc ($result); // get a row
return $dbarray;
}
function getProduct ($aProductid) {
return $dbarray;
}
function getProductList () {
// <----------- this would be the problem function
}
}
$DB = new DBClass();
?>
My calling routine:
<?php
$dbarray = $DB->validateUser ($_POST['userid'], $_POST['password']);
?>
No problem it works fine. I run into a problem with a result set of more than one row. Now I have to get back to the class object for each row. It’s no problem if I include the MySQL code in the calling routine, but I’d like to keep it isolated in my class and I’m not sure how to code it.
Any thoughts? Any examples?
If you use PHP 5.3.0 and mysqlnd, you can use the new function mysqli_fetch_all(). This returns an array of associative arrays.
If you use an earlier version of PHP, you could switch to using PDO, and use the function PDOStatement::fetchAll().
You ask in a comment what about a very large result set. It's true that an unbounded result set could cause the array to exceed your PHP memory limit and that would cause a fatal error and halt the script. But is this really a problem? How many products do you have? You could use LIMIT to make sure the query isn't unbounded.
Re the other part of your questions regarding going back to a class, I'd suggest making an Iterator class:
class DB implements IteratorAggregate
{
protected $_data = array();
public function getProductList() {
// fetch all results from SQL query, stuff them into $this->_data
return $this->getIterator();
}
public function getIterator() {
return new ArrayIterator($this->_data);
}
}
Now you can use the class in a foreach loop:
$db = new DB();
foreach ($db->getProductList() as $product) {
// do something with each product
}
The IteratorAggregate interface means you can even do this:
$db = new DB();
$db->getProductList();
// ...other steps...
foreach ($db as $product) {
// do something with each product
}
Of course you could only store one result set at a time with this method. If you used your DB class for any other queries in the meantime, it would complicate things. For this reason, most people don't try to write a single class to encapsulate all database operations. They write individual classes for each type of Domain Model they need to work with, decoupled from the database connection.
you could save the result in an array and return it:
function getProductList () {
$sql = "SELECT ...";
$result = mysqli_query ( $this->conn, $sql );
$myProducts = array();
while ($row = mysqli_fetch_assoc($result))
$myProducts[] = $row; // or array_push($myProducts, $row)
}
return $myProducts
}
As a result you'll have an array of arrays and each element of it will contain one row of the result.
You have a SQL injection right in your login page.
What happens if someone inputs that as password:
xxx' OR 'yyy' <> 'x