I'm trying my hand at custom functions in PHP in order to streamline a lot of stuff I'm otherwise doing manually. I'm damn new to custom functions so I'm not sure the limitations. Right now I'm trying to get data with MySQLi using custom functions Here's the code, and then I'll explain the issue:
function connect_db($db = 'db_username') {
iconv_set_encoding("internal_encoding", "UTF-8");
mb_language('uni');
mb_internal_encoding('UTF-8');
# $mysqli = new mysqli('host',$db,'password',$db);
if(mysqli_connect_errno())
{
die('connection error');
}
}
This one seems to be working fine. It's the next function I'm having more trouble with.
edit: Updated thanks to Jeremy1026's response
function do_query($db = 'default_db', $query) {
connect_db();
$result = $mysqli->query($query);
if(!$result){
trigger_error("data selection error");
}
while($row = $result->fetch_assoc()){
$result_array[] = $row;
}
return $result_array;
}
My host forces database names and usernames as the same, so if the db name is 'bob' the username to access it will be 'bob' as well, so that's why $db shows up twice in the connection.
The problem I'm having is that these two functions are in functions.php and being called from another page. I want to be able to pull the results from the query in that other page based on the column name. But I also need to be able to do this with formatting, so then maybe the while() loop has to happen on that page and not in a function? I want this to be as universal as possible, regardless of the page or the data, so that I can use these two functions for all connections and all queries of the three databases I'm running for the site.
God I hope I'm being clear.
Big thanks in advance for any suggestions. I've googled this a bit but it's tough to find anything that's not using obsolescent mysql_ prefixes or anything that's actually returning the data in a way that I can use.
Update: I'm now getting the following error when accessing the page in question:
Fatal error: Call to a member function query() on a non-object in /functions.php`
with the line in question being $result = $mysqli->query($query);. I assume that's because it thinks $query is undefined, but shouldn't it be getting the definition from being called in the page? This is that page's code:
$query = "SELECT * FROM `table`";
$myArray = do_query($db, $query);
echo $myArray['column_name'];
In your 2nd function you aren't returning any data, so it is getting lost. You need to tell it what to return, see below:
function do_query($db = 'default_db', $query) {
connect_db();
$result = $mysqli->query($query);
if(!$result){
trigger_error("data selection error");
}
while($row = $result->fetch_assoc()){
$result_array[] = $row;
}
return $result_array;
}
Then, when using the function you'll do something like:
$myArray = do_query($db, 'select column from table');
$myArray would then be populated with the results of your query.
This is a half-answer. The following single function works in place of the two.
function query_db($database, $new_query) {
$sqli = new mysqli('host', $database, 'password', $database);
$sqli->set_charset("utf8");
global $result;
if($result = $sqli->query($new_query)){
return $result;
}
}
By adding global $result I was able to access the results from the query, run from another page as
query_db("username","SELECT * FROM `column`");
while($row = $result->fetch_assoc()){
print_r($row);
}
It's more streamlined than I have without functions, but it's still not idea. If I have the connection to the database in another function, it doesn't work. If I try to put the while loop in the combined function, it doesn't work. Better than nothing, I guess.
Related
I am trying to run the following.
<?php
$db = mysqli_connect("localhost","user","pw") or die("Database error");
mysqli_select_db($db, "database");
$agtid = $_POST['level'];
$sql = sprintf("call agent_hier(%d)", $agtid);
$result = mysqli_query($db, $sql) or exit(mysqli_error($db));
if ($result) {
echo "<table border='1'>
<tr><th>id</th>
<th>name</th>
<th>parent_id</th>
<th>parent_name</th>
<th>level</th>
<th>email</th></tr>";
while ($row = mysqli_fetch_assoc($result))
{
$aid = $row["id"];
$sql2 = "SELECT * FROM members WHERE MEMNO = '$aid'";
$result2 = mysqli_query($db,$sql2) or exit(mysqli_error($db));
while ($newArray = mysqli_fetch_array($result2)) {
$fname = $newArray['FNAME'];
$lname = $newArray['LNAME'];
$mi = $newArray['MI'];
$address = $newArray['ADDRESS'];
$city = $newArray['CITY'];
$state = $newArray['STATE'];
$zip = $newArray['ZIP'];
$kdate = $newArray['KDATE'];
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
echo sprintf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
$row["id"],$row["name"],
$row["parent_id"],$row["parent_name"],
$row["level"],$row["email"]);
}
echo "</table>";
}
mysqli_free_result($result);
mysqli_close($db);
?>
If I remove lines from:
$aid = $row["agent_id"];
to....
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
everything will work fine. If not, I get the following error:
Commands out of sync; you can't run this command now
In researching, I think it could be due to multiple MySQLi queries run at the same time, in which using mysqli_multi_query but for all the samples and general data in the guide does not seem to be applicable.
Any ideas?
The MySQL client does not allow you to execute a new query where there are still rows to be fetched from an in-progress query. See Commands out of sync in the MySQL doc on common errors.
You can use mysqli_store_result() to pre-fetch all the rows from the outer query. That will buffer them in the MySQL client, so from the server's point of view your app has fetched the full result set. Then you can execute more queries even in a loop of fetching rows from the now-buffered outer result set.
Or you mysqli_result::fetch_all() which returns the full result set as a PHP array, and then you can loop over that array.
Calling stored procedures is a special case, because a stored procedure has the potential for returning multiple result sets, each of which may have its own set of rows. That's why the answer from #a1ex07 mentions using mysqli_multi_query() and looping until mysqli_next_result() has no more result sets. This is necessary to satisfy the MySQL protocol, even if in your case your stored procedure has a single result set.
PS: By the way, I see you are doing the nested queries because you have data representing a hierarchy. You might want to consider storing the data differently, so you can query it more easily. I did a presentation about this titled Models for Hierarchical Data with SQL and PHP. I also cover this topic in a chapter of my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Here is how to implement mysqli_next_result() in CodeIgnitor 3.0.3:
On line 262 of system/database/drivers/mysqli/mysqli_driver.php change
protected function _execute($sql)
{
return $this->conn_id->query($this->_prep_query($sql));
}
to this
protected function _execute($sql)
{
$results = $this->conn_id->query($this->_prep_query($sql));
#mysqli_next_result($this->conn_id); // Fix 'command out of sync' error
return $results;
}
This has been an issue since 2.x. I just updated to 3.x and had to copy this hack over to the new version.
Simply,
You have to call mysqli_next_result($db) , after mysqli_free_result is called.
mysqli_free_result($result);
mysqli_next_result($db)
mysqli_close($db);
simply call this function :
$this->free_result();
function free_result() {
while (mysqli_more_results($this->conn) && mysqli_next_result($this->conn)) {
$dummyResult = mysqli_use_result($this->conn);
if ($dummyResult instanceof mysqli_result) {
mysqli_free_result($this->conn);
}
}
}
You have to close previous connection hold by Stored Procedure.
Instead of closing connection each time, you can simply use :
mysqli_next_result($conn);
For new mariaDb check in_predicate_conversion_threshold param. By default you can use up to 1000 attributes for "In" queries
If you are also making a few calls to stored procedures, and facing the aforementioned error, I have a solution for you, Mr. Wayne.
IMHO, Somewhere down the line, the CALL to Stored Procedure actually messes up the connection. So all you have to do is reset the database connection handle.
You could even have a function sitting in your config file doing just that for you, and all you do is call that function once before making any query or CALL to the Stored Procedure
My implementation is (just ignore the $app since I am working on silex framework it is there, YMMV)
function flushhandle() {
global $app;
global $db_host;
global $db_user;
global $db_pass;
global $db_name;
$app['mysqlio']->close();
$app['mysqlio'] = new mysqli($db_host, $db_user, $db_pass, $db_name);
return $app['mysqlio'];
}
I'm trying to fill 2 dropdowns from database, so I try to make one query and fill the first dropdown and then trying to make the second and fill the last dropdown.
It returns the second $rows as false but first is ok, what am I doing wrong?
<?php
$db = new Db();
$rows = $db -> select("call Store1(0,0);");
var_dump($rows);
$rows = $db -> select("call Store1(0,0);");
var_dump($rows);
?>
These are my 2 functions:
public function select($query) {
$rows = array();
$result = $this -> query($query);
if($result === false)
{
return false;
}
while ($row = $result -> fetch_assoc())
{
$rows[] = $row;
}
return $rows;
}
public function query($query) {
$connection = $this -> connect();
$result = $connection -> query($query);
return $result;
}
My guess (and this is just a guess...)
The first resultset (cursor) is still open. The cursor hasn't been closed. (Myabe there are more rows remaining in the current resultset which haven't been fetched yet. Or haven't been discarded by issuing a close.
And an attempt to prepare (execute) a second statement (on that same database connection) is causing an error.
(Agian, this is just a guess, by no means an actual diagnosis of the problem.)
My recommendation would be that we enable error reporting. If a SQL statement execution is returning FALSE, then we need to retrieve the error code and error message from the database. Or consider enabling PDO exceptions, so that errors are thrown so we can see them. (Something similar is probably available using mysqli.)
The return of FALSE is telling us that an error occurred, but it doesn't tell us what the error is. (And without that, it's going to be very difficult to know what to fix.)
In some cases, it may be that we don't care what the error is. In this case, it's pretty clear that we do care. And it's our job to find out what the error is... without diagnosing the root cause of the error, any attempt at writing a prescription is going to be a "try this" and "try that" shooting in the dark without aiming, hoping to hit something.
Recommended reading:
https://ericlippert.com/2014/03/05/how-to-debug-small-programs/
I am trying to run the following.
<?php
$db = mysqli_connect("localhost","user","pw") or die("Database error");
mysqli_select_db($db, "database");
$agtid = $_POST['level'];
$sql = sprintf("call agent_hier(%d)", $agtid);
$result = mysqli_query($db, $sql) or exit(mysqli_error($db));
if ($result) {
echo "<table border='1'>
<tr><th>id</th>
<th>name</th>
<th>parent_id</th>
<th>parent_name</th>
<th>level</th>
<th>email</th></tr>";
while ($row = mysqli_fetch_assoc($result))
{
$aid = $row["id"];
$sql2 = "SELECT * FROM members WHERE MEMNO = '$aid'";
$result2 = mysqli_query($db,$sql2) or exit(mysqli_error($db));
while ($newArray = mysqli_fetch_array($result2)) {
$fname = $newArray['FNAME'];
$lname = $newArray['LNAME'];
$mi = $newArray['MI'];
$address = $newArray['ADDRESS'];
$city = $newArray['CITY'];
$state = $newArray['STATE'];
$zip = $newArray['ZIP'];
$kdate = $newArray['KDATE'];
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
echo sprintf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
$row["id"],$row["name"],
$row["parent_id"],$row["parent_name"],
$row["level"],$row["email"]);
}
echo "</table>";
}
mysqli_free_result($result);
mysqli_close($db);
?>
If I remove lines from:
$aid = $row["agent_id"];
to....
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
everything will work fine. If not, I get the following error:
Commands out of sync; you can't run this command now
In researching, I think it could be due to multiple MySQLi queries run at the same time, in which using mysqli_multi_query but for all the samples and general data in the guide does not seem to be applicable.
Any ideas?
The MySQL client does not allow you to execute a new query where there are still rows to be fetched from an in-progress query. See Commands out of sync in the MySQL doc on common errors.
You can use mysqli_store_result() to pre-fetch all the rows from the outer query. That will buffer them in the MySQL client, so from the server's point of view your app has fetched the full result set. Then you can execute more queries even in a loop of fetching rows from the now-buffered outer result set.
Or you mysqli_result::fetch_all() which returns the full result set as a PHP array, and then you can loop over that array.
Calling stored procedures is a special case, because a stored procedure has the potential for returning multiple result sets, each of which may have its own set of rows. That's why the answer from #a1ex07 mentions using mysqli_multi_query() and looping until mysqli_next_result() has no more result sets. This is necessary to satisfy the MySQL protocol, even if in your case your stored procedure has a single result set.
PS: By the way, I see you are doing the nested queries because you have data representing a hierarchy. You might want to consider storing the data differently, so you can query it more easily. I did a presentation about this titled Models for Hierarchical Data with SQL and PHP. I also cover this topic in a chapter of my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Here is how to implement mysqli_next_result() in CodeIgnitor 3.0.3:
On line 262 of system/database/drivers/mysqli/mysqli_driver.php change
protected function _execute($sql)
{
return $this->conn_id->query($this->_prep_query($sql));
}
to this
protected function _execute($sql)
{
$results = $this->conn_id->query($this->_prep_query($sql));
#mysqli_next_result($this->conn_id); // Fix 'command out of sync' error
return $results;
}
This has been an issue since 2.x. I just updated to 3.x and had to copy this hack over to the new version.
Simply,
You have to call mysqli_next_result($db) , after mysqli_free_result is called.
mysqli_free_result($result);
mysqli_next_result($db)
mysqli_close($db);
simply call this function :
$this->free_result();
function free_result() {
while (mysqli_more_results($this->conn) && mysqli_next_result($this->conn)) {
$dummyResult = mysqli_use_result($this->conn);
if ($dummyResult instanceof mysqli_result) {
mysqli_free_result($this->conn);
}
}
}
You have to close previous connection hold by Stored Procedure.
Instead of closing connection each time, you can simply use :
mysqli_next_result($conn);
For new mariaDb check in_predicate_conversion_threshold param. By default you can use up to 1000 attributes for "In" queries
If you are also making a few calls to stored procedures, and facing the aforementioned error, I have a solution for you, Mr. Wayne.
IMHO, Somewhere down the line, the CALL to Stored Procedure actually messes up the connection. So all you have to do is reset the database connection handle.
You could even have a function sitting in your config file doing just that for you, and all you do is call that function once before making any query or CALL to the Stored Procedure
My implementation is (just ignore the $app since I am working on silex framework it is there, YMMV)
function flushhandle() {
global $app;
global $db_host;
global $db_user;
global $db_pass;
global $db_name;
$app['mysqlio']->close();
$app['mysqlio'] = new mysqli($db_host, $db_user, $db_pass, $db_name);
return $app['mysqlio'];
}
I'm having an issue with sqlsrv_query();
I dont think this is a duplicate as i have been searching through other questions, but if it is i do apologise.
I have two php files one with the databse functions and one using them, the database file is included in the top of the file using it.
In the databse file i have two functions (Along with other connection functions);
function database_GetData($sql) {
$connection = database_GetDatabaseConnection();
return sqlsrv_query($connection, $sql);
}
function database_ReadData($rs) {
if (!$rs)
return false;
$data = sqlsrv_fetch_array($rs);
return $data;
}
The issue is that the database_GetData($sql) returns the resource but it is "unknown". If i print the sqlsrv_query($connection, $sql) result in the function or do something along the lines of sqlsrv_fetch_array(sqlsrv_query($connection, $sql)) within the function i get the expected result but as soon as i return the resource it gets messed up. The connection is all fine as i get the expected result but cannot return it. I would like to keep the functions the same as they are used throughout the application.
Is there any reason why a result of sqlsrv_query($connection, $sql) would get set to 'unknown' once returned from database_GetData($sql) function?
by ref parameters
function database_GetData($sql,&$resutl) {
$connection = database_GetDatabaseConnection();
$result=sqlsrv_query($connection, $sql);
return $result;
}
I am trying to run the following.
<?php
$db = mysqli_connect("localhost","user","pw") or die("Database error");
mysqli_select_db($db, "database");
$agtid = $_POST['level'];
$sql = sprintf("call agent_hier(%d)", $agtid);
$result = mysqli_query($db, $sql) or exit(mysqli_error($db));
if ($result) {
echo "<table border='1'>
<tr><th>id</th>
<th>name</th>
<th>parent_id</th>
<th>parent_name</th>
<th>level</th>
<th>email</th></tr>";
while ($row = mysqli_fetch_assoc($result))
{
$aid = $row["id"];
$sql2 = "SELECT * FROM members WHERE MEMNO = '$aid'";
$result2 = mysqli_query($db,$sql2) or exit(mysqli_error($db));
while ($newArray = mysqli_fetch_array($result2)) {
$fname = $newArray['FNAME'];
$lname = $newArray['LNAME'];
$mi = $newArray['MI'];
$address = $newArray['ADDRESS'];
$city = $newArray['CITY'];
$state = $newArray['STATE'];
$zip = $newArray['ZIP'];
$kdate = $newArray['KDATE'];
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
echo sprintf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>",
$row["id"],$row["name"],
$row["parent_id"],$row["parent_name"],
$row["level"],$row["email"]);
}
echo "</table>";
}
mysqli_free_result($result);
mysqli_close($db);
?>
If I remove lines from:
$aid = $row["agent_id"];
to....
$date = abs(strtotime(date('m/d/Y')) - strtotime(date($kdate))) / (60 * 60 * 24);
}
everything will work fine. If not, I get the following error:
Commands out of sync; you can't run this command now
In researching, I think it could be due to multiple MySQLi queries run at the same time, in which using mysqli_multi_query but for all the samples and general data in the guide does not seem to be applicable.
Any ideas?
The MySQL client does not allow you to execute a new query where there are still rows to be fetched from an in-progress query. See Commands out of sync in the MySQL doc on common errors.
You can use mysqli_store_result() to pre-fetch all the rows from the outer query. That will buffer them in the MySQL client, so from the server's point of view your app has fetched the full result set. Then you can execute more queries even in a loop of fetching rows from the now-buffered outer result set.
Or you mysqli_result::fetch_all() which returns the full result set as a PHP array, and then you can loop over that array.
Calling stored procedures is a special case, because a stored procedure has the potential for returning multiple result sets, each of which may have its own set of rows. That's why the answer from #a1ex07 mentions using mysqli_multi_query() and looping until mysqli_next_result() has no more result sets. This is necessary to satisfy the MySQL protocol, even if in your case your stored procedure has a single result set.
PS: By the way, I see you are doing the nested queries because you have data representing a hierarchy. You might want to consider storing the data differently, so you can query it more easily. I did a presentation about this titled Models for Hierarchical Data with SQL and PHP. I also cover this topic in a chapter of my book SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.
Here is how to implement mysqli_next_result() in CodeIgnitor 3.0.3:
On line 262 of system/database/drivers/mysqli/mysqli_driver.php change
protected function _execute($sql)
{
return $this->conn_id->query($this->_prep_query($sql));
}
to this
protected function _execute($sql)
{
$results = $this->conn_id->query($this->_prep_query($sql));
#mysqli_next_result($this->conn_id); // Fix 'command out of sync' error
return $results;
}
This has been an issue since 2.x. I just updated to 3.x and had to copy this hack over to the new version.
Simply,
You have to call mysqli_next_result($db) , after mysqli_free_result is called.
mysqli_free_result($result);
mysqli_next_result($db)
mysqli_close($db);
simply call this function :
$this->free_result();
function free_result() {
while (mysqli_more_results($this->conn) && mysqli_next_result($this->conn)) {
$dummyResult = mysqli_use_result($this->conn);
if ($dummyResult instanceof mysqli_result) {
mysqli_free_result($this->conn);
}
}
}
You have to close previous connection hold by Stored Procedure.
Instead of closing connection each time, you can simply use :
mysqli_next_result($conn);
For new mariaDb check in_predicate_conversion_threshold param. By default you can use up to 1000 attributes for "In" queries
If you are also making a few calls to stored procedures, and facing the aforementioned error, I have a solution for you, Mr. Wayne.
IMHO, Somewhere down the line, the CALL to Stored Procedure actually messes up the connection. So all you have to do is reset the database connection handle.
You could even have a function sitting in your config file doing just that for you, and all you do is call that function once before making any query or CALL to the Stored Procedure
My implementation is (just ignore the $app since I am working on silex framework it is there, YMMV)
function flushhandle() {
global $app;
global $db_host;
global $db_user;
global $db_pass;
global $db_name;
$app['mysqlio']->close();
$app['mysqlio'] = new mysqli($db_host, $db_user, $db_pass, $db_name);
return $app['mysqlio'];
}