I have a following function in php:
function addEntryHere($day, $numberId, $numberOfTexts, $entries) {
foreach($entries as $entry) {
if($entry->$day == $day) {
$entry->addToEntry($numberId, $numberOfTexts);
}
}
$newEntry = new Entry($day);
$newEntry->addToEntry($standId, $numberOfTexts);
array_push($entries, $newEntry);
//print_r($entries);
}
and when I invoke this function in a loop:
while($row = mysqli_fetch_array($result)) {
echo "count table before: " . count($entries);
for($i=23; $i<26; $i++) {
addEntryHere($i, $row[1], $row[2], $entries);
//print_r($entries);
}
echo "count table after: " . count($entries);
}
I see only:
count table before: 0
count table after: 0
My addToEntry method is quite simple:
function addToEntry($numberId, $numberOfTexts) {
switch($numberId) {
case 1: $this->number1+= $numberOfTexts; break;
case 2: $this->number2+= $numberOfTexts; break;
}
}
So why do I get constantly the output 0, even though there is some data in the $result? I've decided to pass the array $entries to the addEntryHere method because I couldn't refer to it in the method, even though I thought it has a global scope...
======= EDIT
after following the watcher's suggestion I modified my code, but then this line:
if($entry->$day == $day) {
throws me the error:
Notice: Undefined property: Entry::$23
and the browser prints such errors many, many times (since it's in the while loop). What might be the problem here?
You are only modifying the local variable inside the function. After every invocation of the function, that local variable disappears and is lost. What you want to do is return the value of your computation back to the calling context so that it can be used later:
array_push($entries, $newEntry);
return $entries;
}
And the call:
while($row = mysqli_fetch_array($result)) {
echo "count table before: " . count($entries);
for($i=23; $i<26; $i++) {
$entries = addEntryHere($i, $row[1], $row[2], $entries);
//print_r($entries);
}
echo "count table after: " . count($entries);
}
If you're trying to work with the global scope, note that in PHP you have to explicitly import the global scope inside of the function:
function addEntryHere($day, $numberId, $numberOfTexts) {
global $entries;
But realize that, in general, working with global variables is an anti-pattern and is advised against.
Related
PHP seems very inconsistent and (compiler) fails to generate the logic. Let's start our investigation, first with a simple set of JSON data.
[
{
"customer": "cust01",
"assembly": "assem01",
"date_received": "02-08-2015",
"time_received": "09:15",
"date_completed": "02-23-2015",
"time_completed": "10:27"
},
{
"customer": "lov_01",
"assembly": "lov_02",
"date_received": "lov_03",
"time_received": "lov_04",
"date_completed": "lov05",
"time_completed": "lov_06"
}
]
Then in the PHP, we retrieve an array of that data
$t_json_string = file_get_contents($t_json_file_path);
$t_json_arr = json_decode($t_json_string, true);
Assume we retrieve post values in an array like this
$t_new_entry = [
"customer" => "lov_01",
"assembly" => "lov_02",
"date_received" => "lov_03",
"time_received" => "lov_04",
"time_completed"=> "lov_05",
"time_completed"=> "lov_06"
];
and the goal is to verify as if new entry exists in the json array yet, by a condition whether both arrays have more than 2 similar values, for that I'm using $t_count to count the number of similar occurrences.
I wrote up 2 methods for checking that while passing the same data into the data pool.
// $t_boo = $db_entry_check($t_new_entry, $t_json_arr); echo $t_boo;
// true, $t_count shows 3.
$t_bool = $db_entry_exist($t_new_entry, $t_json_arr); echo $t_bool;
// False. It has to be true with the $t_count printed out at 3.
The first one employs call_user_function_array, which I tested and it works so I commented it out. Code for it here:
$db_entry_check = function($needle, $haystack){
$t_exist = 'false';
$t_count = 0;
function h_loop (&$t_count, $value, $array){
foreach ($array as $key => $val){
if (is_array($val)){
h_loop($t_count, $value, $val);
} else {
echo "<br/> --- value: ". $value. "<br/> --- val: ". $val . "<br/><br/>";
if ($val === $value){
$t_count += 1;
echo "<br/>" . $t_count . "<br/>";
continue;
}
}
}
}
function n_loop (&$t_count, $arr, $array){
foreach ($arr as $key => $value){
if (is_array($value)){
n_loop($t_count, $value, $array);
} else {
if ($t_count > 2) continue;
call_user_func_array('h_loop', [&$t_count, $value, $array]);
}
}
}
n_loop($t_count, $needle, $haystack);
echo "<br/>" . $t_count . "<br/>";
if ($t_count > 2) $t_exist = 'true';
return $t_exist;
};
The second one is my attempt to use lambdas on every component functions. I tried playing around putting $value, $array, and $t_count into use() part as those variables exist within the scope of $db_entry_exist for data binding & dependencies injection. When it comes to considering parameters (for the function) vs dependencies (for the use) Of the h_loop, I find it confusing, what an entire mess in PHP efforts to implement concepts of Javascript.
No matter what parameters I am passing onto the function part and no matter what variables got injected in the use() part. Many variations have been tested but none of them work. I usually get an error of 'Function name must be a string'. Invoking a closure within another closure in PHP seems not working as the logic in Javascript. It fails me whenever I tries to pass $h_loop($t_count, $value, $array); or echo $factorial(5); in the else part of the n_loop function. What I don't understand is that $db_entry_exist itself is a lambda (Closure as what PHP calls it) and n_loop function can be called inside without any error but calling/invoking a grandchild (h_loop) function by the same approach does not work, often resulting in the same error above.
$db_entry_exist = function($needle, $haystack){
$t_exist = 'false';
$t_count = 0;
// n_loop($t_count, $needle, $haystack);
$h_loop = function (&$t_count, $value, $array) use (&$h_loop) {
foreach ($array as $key => $val){
if (is_array($val)){
h_loop($t_count, $value, $val);
} else {
echo "<br/> --- value: ". $value. "<br/> --- val: ". $val . "<br/><br/>";
if ($val === $value){
$t_count += 1;
echo "<br/>" . $t_count . "<br/>";
continue;
}
}
}
};
$factoral = function($n) use (&$factoral) {
if ($n <= 1)
return 1;
else
return $n * $factoral($n - 1);
}; // source: https://gist.github.com/superic/8290704
$n_loop = function (&$t_count, $arr, $array) use (&$n_loop) {
foreach ($arr as $key => $value){
if (is_array($value)){
$n_loop($t_count, $value, $array);
} else {
if ($t_count > 2) continue;
$h_loop($t_count, $value, $array);
}
}
};
/*$n_loop = function ($arr, $array) use (&$n_loop, &$t_count){
// echo "<br/> --- nloop.t_count: " . $t_count . "<br/>";
foreach ($arr as $key => $value){
if (is_array($value)){
$n_loop($value);
} else {
if ($t_count > 2) continue;
// $h_loop($value, $array);
}
}
};*/
$n_loop($t_count, $needle, $haystack);
echo "<br/>" . $t_count . "<br/>";
if ($t_count > 2) $t_exist = 'true';
return $t_exist;
};
and here is the link to view my entire code:
<script src="http://ideone.com/e.js/YjLkZF" type="text/javascript" ></script>
To sum up, there are primarily 2 issues I don't understand and can't figure:
$n_loop is invoked fine within $db_entry_exist method but $h_loop isn't.
In the context of $db_entry_exist, how to pass and pass what variables to the function() and pass what as dependencies to the use() part.
$n_loop = function (&$t_count, $arr, $array) use (&$n_loop){}
// ------ OR ------- many other variations are there too.
$n_loop = function ($arr, $array) use (&$n_loop, &$t_count){}
Please investigate the code and let me know your thoughts. Thank you.
You have two misconceptions in your code that are affecting your understanding.
First: PHP does not actually have nested functions. When you say:
function outer()
{
function foo() {}
function bar() {}
}
what you are really saying is, when outer() is called, define foo() and bar() in the global scope. This means that once you call outer() once, anyone (not just outer()) can call foo() and bar(). This also means that calling outer() a second time results in a Cannot redeclare foo() error.
Second: Closures in PHP do not automatically close over any variables in their parent scope. Any variables intended to be part of the closure must be explicitly included in the use() list. This means that when you write:
$n_loop = function (&$t_count, $arr, $array) use (&$n_loop) {
//...
$h_loop($t_count, $value, $array);
//...
};
the call to $h_loop will always fail, because in the scope of that function, there is no variable named $h_loop. If you add $h_loop to your use() list, then you will be able to call it as expected.
I have this code:
<?php
function generator() {
yield 'First value';
for ($i = 1; $i <= 3; $i++) {
yield $i;
}
}
$gen = generator();
$first = $gen->current();
echo $first . '<br/>';
//$gen->next();
foreach ($gen as $value) {
echo $value . '<br/>';
}
This outputs:
First value
First value
1
2
3
I need the 'First value' to yielding only once. If i uncomment $gen->next() line, fatal error occured:
Fatal error: Uncaught exception 'Exception' with message 'Cannot rewind a generator that was already run'
How can I solve this?
The problem is that the foreach try to reset (rewind) the Generator. But rewind() throws an exception if the generator is currently after the first yield.
So you should avoid the foreach and use a while instead
$gen = generator();
$first = $gen->current();
echo $first . '<br/>';
$gen->next();
while ($gen->valid()) {
echo $gen->current() . '<br/>';
$gen->next();
}
chumkiu's answer is correct. Some additional ideas.
Proposal 0: remaining() decorator.
(This is the latest version I am adding here, but possibly the best)
PHP 7+:
function remaining(\Generator $generator) {
yield from $generator;
}
PHP 5.5+ < 7:
function remaining(\Generator $generator) {
for (; $generator->valid(); $generator->next()) {
yield $generator->current();
}
}
Usage (all PHP versions):
function foo() {
for ($i = 0; $i < 5; ++$i) {
yield $i;
}
}
$gen = foo();
if (!$gen->valid()) {
// Not even the first item exists.
return;
}
$first = $gen->current();
$gen->next();
$values = [];
foreach (remaining($gen) as $value) {
$values[] = $value;
}
There might be some indirection overhead. But semantically this is quite elegant I think.
Proposal 1: for() instead of while().
As a nice syntactic alternative, I propose using for() instead of while() to reduce clutter from the ->next() call and the initialization.
Simple version, without your initial value:
for ($gen = generator(); $gen->valid(); $gen->next()) {
echo $gen->current();
}
With the initial value:
$gen = generator();
if (!$gen->valid()) {
echo "Not even the first value exists.<br/>";
return;
}
$first = $gen->current();
echo $first . '<br/>';
$gen->next();
for (; $gen->valid(); $gen->next()) {
echo $gen->current() . '<br/>';
}
You could put the first $gen->next() into the for() statement, but I don't think this would add much readability.
A little benchmark I did locally (with PHP 5.6) showed that this version with for() or while() with explicit calls to ->next(), current() etc are slower than the implicit version with foreach(generator() as $value).
Proposal 2: Offset parameter in the generator() function
This only works if you have control over the generator function.
function generator($offset = 0) {
if ($offset <= 0) {
yield 'First value';
$offset = 1;
}
for ($i = $offset; $i <= 3; $i++) {
yield $i;
}
}
foreach (generator() as $firstValue) {
print "First: " . $firstValue . "\n";
break;
}
foreach (generator(1) as value) {
print $value . "\n";
}
This would mean that any initialization would run twice. Maybe not desirable.
Also it allows calls like generator(9999) with really high skip numbers. E.g. someone could use this to process the generator sequence in chunks. But starting from 0 each time and then skipping a huge number of items seems really a bad idea performance-wise. E.g. if the data is coming from a file, and skipping means to read + ignore the first 9999 lines of the file.
solutions provided here does not work if you need to iterate more than once.
so I used iterator_to_array function to convert it to array;
$items = iterator_to_array($items);
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.
I am trying to import a tab delimited file after upload. The meat of this is done with the following function. I'm trying to build an array of class instances. The code follows:
Import Function
$AddedProducts;
function importList($filename)
{
global $AddedProducts;
$AddedProducts=array();
$fileHandle = fopen($filename, "r");
$currentProduct = new productImport();
$line=fgets($fileHandle); $line=fgets($fileHandle); //throw away top 2 lines
echo '<hr>';
while(true)
{
$line = fgets($fileHandle);
if($line == null) break;
$cells=explode(' ', $line);
$i=0;
foreach($currentProduct as $ProductProperty)
{
if(isset($cells[$i]))
{
$ProductProperty = $cells[$i];
echo $i . '. ' . $cells[$i] . "<br>";
}
else return false;
$i++;
}
echo "<hr>";
$AddedProducts[]=$currentProduct;
}
fclose($fileHandle);
return true;
}
Array Output
<?
$i=0;
foreach($AddedProducts as $AddedProduct)
{
$i++;
echo "<hr>" . $i . "<br>";
foreach($AddedProduct as $key=>$value)
{
echo $key . ' = ' . $value . '<br>';
}
}
?>
Breakdown of Known Info
The final array length/size is correct. (Should be lines in file - 2)
It doesn't particularly matter how many properties are in the productImport class so long as it equates to the same number of tabs per line in the file being read.
importList function echos proper values for $cells[$i] which are the same values I'm missing in the array output.
The problem seems to be either the values aren't being assigned to the properties or the properties are not being read. I'm not sure of why either would be the case but I assume it is because PHP is not my primary language and likely something obvious about the foreach loops ;)
I'm using PHP v5.2.6
What's wrong with this code?
Answer:
foreach($currentProduct as $ProductProperty) becomes
foreach($currentProduct as &$ProductProperty)
I think the problem is in this section:
foreach($currentProduct as $ProductProperty)
{
if(isset($cells[$i]))
{
$ProductProperty = $cells[$i]; /* this seems to be the problem */
echo $i . '. ' . $cells[$i] . "<br>";
}
else return false;
$i++;
}
According to the php manual, Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. so the value you assign is discarded after the loop.
Edit: Apart from that, you are looping through object properties and although the manual does not explicitly state it, it seems you need foreach($class as $key => $value) instead of just foreach($class as $value)
In your foreach loops, the assigned variables such as $ProductProperty are not references, therefore they will not actually affect anything outside the loop.
i.e. $ProductProperty = $cells[$i] only affects the current iteration.
In addition to what the others are saying, it seems that you are attempting to insert property data to the same object every time, since you're not creating any new productImport instances in the loop.
Im working on a new minimal Project, but i've got an error, i dont know why.
Normally, i use arrays after i first created them with $array = array();
but in this case i create it without this code, heres an example full code, which outputs the error:
<?php $i = array('demo', 'demo'); $array['demo/demodemo'] = $i; ?>
<?php $i = array('demo', 'demo'); $array['demo/demodemo2'] = $i; ?>
<?php
foreach($array as $a)
{
echo $a[0] . '<br>';
}
function echo_array_demo() {
foreach($array as $a)
{
echo $a[0] . '<br>';
}
}
echo_array_demo();
?>
I create items for an array $array and if i call it (foreach) without an function, it works. But if i call in in a function, then the error comes up...
I ve got no idea why
Thank you...
Functions have their own variable scope. Variables defined outside the function are not automatically known to it.
You can "import" variables into a function using the global keyword.
function echo_array_demo() {
global $array;
foreach($array as $a)
{
echo $a[0] . '<br>';
}
}
Another way of making the variable known to the function is passing it as a reference:
function echo_array_demo(&$array) {
foreach($array as $a)
{
echo $a[0] . '<br>';
}
}
echo_array_demo($array);
Check out the PHP manual on variable scope.