Where is $row defined? - php

In the code below, can someone tell me where the variable $row is coming from in the foreach statement?
public function getProcResultSet($cmd)
{
try
{
$meta = $cmd->result_metadata();
while ($field = $meta->fetch_field())
{
$params[] = &$row[$field->name];
}
call_user_func_array(array(
$cmd,
'bind_result'), $params);
while ($cmd->fetch())
{
foreach ($row as $key => $val)
{
$c[$key] = $val;
}
$results[] = $c;
}
return $results;
}
catch (Exception $e)
{
logToFile("Exception: " . $e);
return resultFailedUnknown();
}
}
Edit, here is the caller of this function:
public static function getPlayerUnits($playerId, $unitTypeId)
{
$mysqli = new mysqli(GDB_HOST, GDB_USERNAME, GDB_PASSWORD, GDB_NAME);
if ($mysqli->connect_errno)
{
throw new Exception('DB Connection Failed. Error Code: ' . $mysqli->connect_errno);
}
$cmd = $mysqli->prepare('CALL sp_get_player_units(?, ?)');
if (!$cmd)
{
throw new Exception($mysqli->error);
}
$cmd->bind_param('ii', $playerId, $unitTypeId);
$cmd->execute();
$results = parent::getProcResultSet($cmd);
$cmd->close();
$mysqli->close();
return $results;
}
Here is the result array which all I did was receive on a client and JSON.stringify(..):
[{"Id":1,"Name":"Machine Gunner","Quantity":0},{"Id":2,"Name":"Rocket Soldier","Quantity":0},{"Id":3,"Name":"Paratrooper","Quantity":0},{"Id":4,"Name":"Demolition Soldier","Quantity":0}]
As you can see, the result is showing the columns per row as expected.

In the first while loop, because of the reference operator &. It doesn't exist before though.
The line $params[] = &$row[$field->name] creates the $row variable. What happens is that PHP wants to take a reference to $row that doesn't exist, so it creates it without any message (quite a bug source if you'd ask me). The $row is created as array with a key $field->name set to nothing.
$row does exist in the foreach loop, but not in the while loop. There it is created as an array with empty values.
The whole thing is quite obfuscated code and not very readable. Sometimes being verbose in code is a Good Thing.

Normally people declare $row as the result of mysql_fetch_row($result), where $result is what you get after querying your SQL database. However, I don't see that mentioned anywhere here so I don't think your code should actually work.
edit: obviously the exception to this would be if this isn't your entire file and it's defined somewhere above this. but yeah, shouldn't work otherwise.

Related

PHP Generators: How to always clean resources, even when break is called?

The output of this code:
function gen() {
$rows = ['a', 'b', 'c'];
foreach ($rows as $row) {
echo "yield $row\n";
yield $row;
}
echo "finished\n";
}
foreach (gen() as $v) {
echo "val $v\n";
// break;
}
is
yield a
val a
yield b
val b
yield c
val c
finished
With the break uncommeted is:
yield a
val a
So if I break the loop, the code after the loop in gen() function is not executed. I need clean some resources, but I don't know how to do it.
For example, here:
public function getRows(string $query, array $pameters = []): \Generator
{
$stmt = $this->pdo->prepare($query);
// bind pameters...
$stmt->execute();
while ($row = $stmt->fetch()) {
$item = $this->something($row);
yield $item;
}
$stmt->closeCursor(); // this code is not executed if a break is called
}
Or a function who reads a file and the cursor should be closed at the end:
$cursor = openFileCursor('myfile.txt')
while ($line = $cursor->getLine()) {
$item = someFunction($line);
yield $item
}
closeFileCursor($cursor);
Any idea?
I've just had a play with using try/finally inside of your generator block...
Try/Finally always: always execute the contents of the finally block, regardless of break
function gen(){
try{
$rows = ['a', 'b', 'c'];
foreach( $rows as $row ){
yield $row;
}
}
finally
{
// close your connections here, always
echo "finally";
}
}
foreach ( gen() as $v ){
echo $v;
break;
}
This will print either: a b c finally or a finally based on the break
Try/Finally with Condition: try/finally block with a check on some kind of condition.
In this example I've used key( $rows ) as this will return null once the foreach has been sucesfully exhausted (ie full iteration) or a non-null on a "break"
function gen(){
try {
$rows = ['a','b','c'];
foreach ( $rows as $row) {
yield $row;
}
}
finally {
// condition to detect incomplete return (like a row/total counter)
if( key( $rows ) !== null ) {
// close your connections here, but only on incomplete generation
echo "finally";
}
}
}
foreach ( gen() as $v ) {
echo $v;
break;
}
This will print either: a b c or a finally based on the break
I do realize this is rather a generic question, and the other answer is a direct answer to it.
However, as this question is tagged with PDO and your actual example is about this particular API, there is a much simpler way to achieve your goal: given PDOStatement is already traversable, you could write this function like this
public function getRows(string $query, array $parameters = []): PDOStatement
{
$stmt = $this->pdo->prepare($query);
$res = $stmt->execute($parameters);
return $stmt;
}
then you could use it the same way as your generator function:
$sql = "SELECT * FROM users WHERE salary > ?";
foreach ($db->getRows($sql, [0]) as $row) {
// whatever
}
when the loop will be finished, this way or another, $stmt will be nullified and thus cursor will be closed automatically.
As a bonus, you could give this function a more generic name and use it for any query, INSERT or DELETE for example.
Also, an important note: if you are expecting a result set that big, consider using an unbuffered query, as otherwise your RAM will be consumed despite fetching rows one by one.

PHP json_encode & SQL server callback function returns only one result

I'm trying to set up a PHP callback function for use in our application. It needs to pull data from a SQL server, and while I can get it to work initially, it's not quite doing what I want.
Code:
//Callback function for passing queries
function queryCallback($conn, $query) {
$response = sqlsrv_query($conn, $query);
while ($row = sqlsrv_fetch_array($response)){
if ($row === false) {
die(print_r(sqlsrv_errors(), true));
}
$responseData[] = $row;
}
foreach($responseData as $v) {
$output[key($v)] = current($v);
}
$responseDataJSON = JSON_encode($output, 128);
return $responseDataJSON;
}
In the above, $conn represents our server creds, as passed to sqlsrv_connect(), and $query is the string containing the query passed to SQL. Both have been verified as working.
Issue:
This code contacts the server correctly, and runs the query, but it only returns one result. This is obviously a problem with how the loops are set up, but I just can't spot it
My feeling is that the following $row = sqlsrv_fetch_array($response) is fetching the whole row as an array, but your usage of $output[key($v)] = current($v) is only returning the first column with the same key, and overwriting the $output index with every iteration.
foreach($responseData as $v) {
$output[key($v)] = current($v);
}
lets say you instead perform
foreach($responseData as $k => $v) {
$output[$k] = $v;
}
At which point this is redundant as $responseData[] already is this structure.
You may actually want this if you plan to extract just the first column out of your row.
foreach($responseData as $v) {
$output[] = current($v);
}

Cannot redeclare function previously declared php

I'm running into an "Cannot redeclare" error and I can't figure out how to fix it. So I have a few functions in a php file located below. Now these functions iterate over an array of data.
I think I've surmised that the problem is that I'm looping the function over and over again in the foreach loop, and its the foreach loop thats been the problem. It seems like its already writing one the function to memory the first time and then for some reason it doesn't like being evoked again.
Your help appreciated.
P.S I've seen a number of similar posts on the issue such as Fatal error: Cannot redeclare but that doesn't seem to work.
<?php
// *****Code Omitted from Stack****
function postHelper($data, $field1, $field2)
{ //TODO Abstract and make sure post Helper and modify Post can be the same thing.
$result = array();
for ($j = 0; $j < count($data); ++$j) { //iterator over array
if ($field2 == "") {
$result[$j] = $data[$j][$field1];
} else {
return $result[$j] = $data[$j][$field1][$field2];
}
}
return $result;
}
//returns an array with only # and # values
function modifyPost($data)
{
//puts symbol # before read data
function addSymbol($data, $field1, $field2)
{
$info = postHelper($data, $field1, $field2);
foreach ($info as &$n) {
$n = '#' . $n;
}
print_r($info);
}
/*
Parse texts and returns an array with only # or # signs used
*/
function parseText($data)
{
$newarr = array();
$text = postHelper($data, "text", "");
foreach ($text as &$s) { //separates into words
$ex = explode(" ", $s);
foreach ($ex as &$n) { //if text doesnt' begin with '#' or '#' then throw it out.
if (substr($n, 0, 1) === '#' || strpos($n, '#') !== false) {
array_push($newarr, $n . ',');
}
}
}
return $newarr;
}
}
foreach ($posts as $entry) {
if (!function_exists('modifyPost')) {
$nval = "hello";
modifyPost($entry);
$entry['mod_post'] = $nval;
}
}
?>
EDIT: I've solved the error. Turns out that the original posts did actually work. I messed in naming. I will give points to anyone who can explain to me why this is necessary for a call. Moreover, I will update post if there is an additional questions that I have.
Php doesn't support nested functions. Although you technically can declare a function within a function:
function modifyPost($data)
{
function addSymbol($data, $field1, $field2)
the inner function becomes global, and the second attempt to declare it (by calling the outer function once again) will fail.
This behaviour seems counter-intuitive, but this is how it works at the moment. There's RFC about real nested functions, which also lists several workarounds for the problem.
The error says it all. You have duplicate modifyData() & parseText functions.
Remove the top half of the php file so only one of each occurs.

MySQLi bind_param returning only one row

Despite coming from a programming background, I'm still relatively new to PHP and trying to grasp it properly. I have decided to use MySQLi instead of PDO (which turned out to be a grave mistake on my part) for my database needs, but I've run into an issue where bind_result only seems to return one result, despite the results, to my knowledge, being looped through. I've been googling for hours, but I just can't figure out what's going on. I'm sure the solution is very simple, I'm overlooking something, and/or am doing something terribly wrong. If so, I hope you can find it in your heart to forgive me and help me solve my pesky issue, Stack Overflow.
Here is the ever-so-malicious code.
public function query($query, $types, $params)
{
if ($stmt = $this->prepareQuery($query, $types, $params))
{
$stmt->execute();
// we prepare the array in which the results will be stored for dynamic results
$meta = $stmt->result_metadata();
$results = array();
while ($field = $meta->fetch_field())
{
$name = $field->name;
$$name = null;
// pass reference because bind_result() requires it
$results[$name] = &$$name;
}
while ($stmt->fetch())
{
// call_user_func_array is used for dynamic results
call_user_func_array(array($stmt, "bind_result"), $results);
var_dump($results) . "<br />";
}
$data = array();
while ($stmt->fetch()) {
array_push($data, $results);
}
return $data;
}
else
{
echo $this->db->error;
}
}

Output a result of an SQL query to a PHP array

I'm new to OOP in PHP, is that to seems correct ?
class whatever {
Function Maths() {
$this->sql->query($requete);
$i = 0;
while($val = mysql_fetch_array($this)) {
$tab[i][average] = $val['average'];
$tab[i][randomData] = $val['sum'];
$i=$i+1;
}
return $tab;
}
I want to access the data contained in the array
$foo = new whatever();
$foo->Maths();
for ($i, $i <= endOfTheArray; i++) {
echo Maths->tab[i][average];
echo Maths->tab[i][randomData];
}
Thank you ;)
EDIT: i want to output the result of the SQL query as an array, so i can access it from outside the class
In the interest of helping you out, here are some modifications. Please hear this, though: a lot of this might not make sense without a good background in PHP or OOP in general. You should look at #webbiedave's link.
class whatever {
static function maths() {
$tabs = array();
$results = $this->sql->query($requete);
while($val = mysql_fetch_array($this)) {
$tabs = $val;
}
return $tabs;
}
This fixes syntax errors and logic errors (for instance, the creation of the $results variable to hold the SQL query run).
I made the maths method static, since there's really no need to instantiate a whatever() object with this current example.
Here are some modifications to how this would be used:
$results = whatever::maths();
foreacho ($results as $result) {
echo $result['average'];
echo $result['randomData'];
}
Since maths() returns something, you need to store that in a variable; simply calling it, as you did previously, doesn't do anything.
That convoluted for loop can be replaced with a foreach loop.
Please check out PHP OOP basics:
http://www.php.net/manual/en/language.oop5.basic.php
Edit: Thanks for cleaning up the code. Try something along the lines of:
$tabs = array();
while($val = mysql_fetch_assoc($result)) {
$tabs[] = $val;
}
And:
$foo = new whatever();
$tabs = $foo->Maths();
for ($tabs as $tab) {
echo $tab['average'];
echo $tab['randomData'];
}
http://www.php.net/manual/en/language.oop5.basic.php

Categories