foreach loop loops once on empty array, bug? - php

I've found weird behaviour in PHPs foreach loop. When I do it like this:
<?php
$arr = array();
foreach ( $arr as $a )
{
echo 'Hello';
}
?>
Then as I expect, nothing is printed simply because the array is empty, but when I do it like this:
<? $arr = array(); ?>
<? foreach ( $arr as $a ): ?>
Hello
<? endforeach; ?>
Then the word 'Hello' is printed once... Even though the array is empty. I don't see why this would behave differently. To me this looks like a bug, but I could be missing something...
I'm using PHP 5.4 (Windows).
And in case you're wondering, I'm using the second method in my .phtml files (template files).

I did some tests, and it seems this is happening because short_open_tag is off.
You're seeing "Hello" because the PHP is not being ran.
You need to edit your php.ini and set short_open_tag to 1.
As of PHP 5.4, the <?php echo alias <?= is always available, but the short tag (<?) syntax still needs that option set.
Docs: http://www.php.net/manual/en/ini.core.php#ini.short-open-tag

Related

Reusing collection var as iteration var in a PHP foreach

I have a coworker that I noticed was using his foreachs in the following fashion:
foreach ($var as $var) {
// do stuff here
}
I have tested the code above and it works correctly to my surprise. Would the PHP gurus like to hop in and tell me why this is wrong? It feels very wrong.
Because it changes the value of $var. After the foreach() it's no longer an array but is set to the last value in the array.
$var = array('apple', 'orange');
foreach ($var as $var) {
echo $var."<br/>";
}
echo $var; //orange
If you don't want to change the variable's value, it'll need to be a different variable name:
$var = array('apple', 'orange');
foreach ($var as $fruit) {
echo $fruit."<br/>";
}
echo $var; //array
As #UselessIntern pointed out, it's fine if you're not going to use the variable after looping through it, but it's definitely not encouraged because it can lead to confusion.
As #PLB pointed out, it iterates over a copy of the $var not $var itself. So every iteration the value of $var is changing, but it doesn't break the loop because it's looping over the copy that was created.
Because it's a loop. Performing:
array > string
array > string
So
foreach ($var AS $var){
/*
Is basically being re-read at the end so your foreach can read the array again to get the next step of the array
array > string
recheck array > string
recheckarray > string
*/
}
Even it feels wrong, it still works because the moment the foreach starts, PHP internally has already access on the data.
So even $var gets overwritten, in memory this data is still present (the original array) and $var is set in each iteration to the current value of it.
The concrete problem you've spotted and about which you say is wrong is also known as variable-reuse which you should prevent because this is a code-smell.
It not only feels wrong, it is wrong to write such code. Tell your co-worker so you can write better code together.
Your coworker seems to don't need $var as an array after the loop anymore. When PHP initializes the foreach loop (what is done only once) it uses the original values from $var as it is an array at the moment. Then on every step in the loop the current element of the element is assigned to a new var called var. Note that the orginal array $var doesn't exist anymore. After the loop $var will have the value of the last element in the original array.
check this little example which demonstrates what I've said:
$a = array(1,2,3);
foreach($a as $a) {
echo var_dump($a);
}
// after loop
var_dump($a); // integer(3) not array
I could imaging that your coworker does this to save a little memory as the reference to the array will get overwritten and therefore the garbage collector will remove it's memory on next run, but I would not advice you to do the same, as it's less readable.
Just do the following, which is the same but is much more readable:
$array = array(1,2,3);
foreach($array as $value) {
echo var_dump($value);
}
delete($array);
delete($value);
Check this expression:
$x = array(1,2,3);
foreach ($x as $x) {
echo $x; //prints 123
}
What is happening here is that the foreach extracts the first element of array $x and overrides into $x itself. However the array variable $x which was on the left side of the as keyword remains in internal scope of the foreach argument which is why the looping works without problems.
Once the loop completes the $x which was internal to the foreach (the array) loses scope and no longer exists, and what remains is the $x variable which now contains the last element of the original $x array. In this case that would be 3.

Assigning array() before using a variable like array

I tried to find a proper and explanatory title but I couldn't and I will try to explain what I am asking here:
Normally if you don't assign an empty array to a variable, you can start assign values to indexes like this:
$hello["world"] = "Hello World";
...
echo $hello["world"];
but I always encounter such definition:
$hello = array() //assigning an empty array first
$hello["hello"] = "World";
...
echo $hello["hello"];
Why is it used a lot. Is there a performance gain or something with the second one?
Thanks.
Two reasons:
Better readability (you know the array is initialized at this point)
Security - when running on a system with register_globals enabled a user could add e.g. hello[moo]=something to the query string and the array would already be initialized with this. $hello = array(); overwrites this value though since a new array is created.
Initializing your variables is good practice.
Take for example this:
$foo = 'bar';
// 10 lines and 1 year later
$foo['baz'] = 'test';
Congratulations, you now have the string "tar".
This may happen accidentally and introduce needless bugs. It gets even worse with conditional variable creation. It's avoided easily by getting into the good habit of explicitly initializing your variables.
$hello = array();
if(someConditionIsTrue){
$hello["world"] = "Hello World";
}
foreach($hello as $val){ // this will not give you any error or warning.
echo $val;
}
But
if(someConditionIsTrue){
$hello["world"] = "Hello World";
}
foreach($hello as $val){ // this will give you error .
echo $val;
}
If I remember correctly, the first one will produce a warning by PHP if you have error_reporting as E_ALL. You should always use the second method because it explicitly initialises a new array. If you are looking through code and out of nowhere see $hello["hello"] but cannot recall seeing any reference to $hello before, it would be confusing.
The same will happen if you do $hello[] = "World", a warning will be displayed

Declaring PHP array items before array loop?

I was wondering if there was a way that I can declare a variable at the top of my php file that contains some 'array stuff' and have it used in a foreach loop below somewhere?
For example:
$variable = 'Here is some text, and here is the value:'.$items->value.'.';
foreach ( $somearray as $items ) {
echo $variable;
}
*note: I am using ezsql for database actions, hence the $items->value variable....
Make sense? I'm just trying to make my file easy to change for other situations... or is there a better way?
Thanks for looking and for your help.
The nicest way to do this would, I think, be printf. It's not exactly what you asked for, but I think it will make your code cleaner.
$variable = 'Here is some text, and here is the value:%s.';
foreach ($somearray as $items) {
printf($variable, $items->value);
}
The value passed as the second argument to printf will be substituted into the code.
You can use eval(), but you should be aware it's risky, and you better read about the consequences of using it.
Other than that, you can use it like you said, but escape the dollar sign so it won't get treated as a variable until the foreach, or use single quotes:
$variable = 'Here is some text, and here is the value: {$items->value}';
foreach ( $somearray as $items ) {
echo eval('return "'.$variable.'";');
}
or is there a better way?
Use a function/method:
function formatItems($items)
{
return 'Here is some text, and here is the value:'.$items->value.'.';
}
foreach ( $somearray as $items ) {
echo formatItems($items);
}
Using a function allows more complex formatting. If it's going to be as simple as your example, you could use printf, as suggested by lonesomeday.
If appropriate, you could have a display/format method in your Items class definition.
Your $items variable will be overwritten by your for loop - $variable won't be though.
$variable is set only once and takes on the value of $items->value at the time (which will probably result in an error since $items doesn't exist at that time and you are referencing a field.
what you want is this:
$variable = 'Here is some text, and here is the value: {$items->value}';
foreach ( $somearray as $items ) {
echo $variable;
}

How to make the output of server headers more readable?

Given the following php5 code that output a gigantuous amount of difficult to read code:
<?=var_dump($_SERVER);?>
<?=print_r($GLOBALS); ?>
Question: how to make the output more human-readable? e.g. houw to but every "item" on a new line?
You can just wrap a pre-element around it:
<pre><?php var_dump($_SERVER); ?></pre>
<pre><?php print_r($GLOBALS); ?></pre>
Also note that <?= requires short_open_tags to be set to true (which is false in newer versions of php)
On your development environment, you should install the Xdebug extension.
Amongst other useful features (such as a debugger !), it'll get you nicer var_dump() :
colors
formating
For example, here's a screenshot of the beggining of the output I get for var_dump($_SERVER); :
(source: pascal-martin.fr)
You can use <pre> tag to format the output
<pre><?=print_r($GLOBALS); ?></pre>
Like everyone else mentioned, you can wrap that in <pre> tags to make it readable. I usually have the following 2 functions in my code at all times. Used as utility functions, inspired by cake.
function pr() {
$vars = func_get_args();
echo '<pre>';
foreach ($vars as $var) {
print_r($var);
}
echo '</pre>';
}
function prd() { //dies after print
$vars = func_get_args();
echo '<pre>';
foreach ($vars as $var) {
print_r($var);
}
echo '</pre>';
die();
}
Apart from the <pre> trick, you can try using dbug
Makes things much nicer and clearer: dBug
the previous answers suggest good solution, but if you want more control on the output you can run a loop over the arrays.
$_SERVER and $_GLOBALS are arrays, so you can do
foreach($_SERVER as $key=>$value){
echo $key . ' is ' . $value . '<br />' . PHP_EOL;
}
you can also add if statements to ignore some items in $_SERVER/$_GLOBALS
It's not whatever "server headers" but regular arrays.
To output array contents, a programmer usually makes use of a loop, and then format output in the manner they wish:
.
foreach($_SERVER as $key => $value){
echo "<b>$key:</b> $value<br>\n";
}
Note that your output being gigantic only because you're printing out the contents of $GLOBALS variable, which being completely useless for you.

How to avoid typing <pre> when viewing arrays... any ideas?

I often have the need to view data in arrays and use the html tag <pre> to do this. However, it becomes tedious and frustrating having to type the same thing out all the time.
My question, what is your technique for avoiding this annoyance of programming with PHP?
There is no way to view it nicely formatted format except for using <pre> tag. Alternatively you can create this function and use that instead:
function pretty_print(array $array){
echo '<pre>';
print_r($array);
echo '</pre>';
}
Now instead of print_r, you can use the pretty_print. No need to type <pre> every now and then :)
Install XDebug. Besides making print_r and var_dump a lot prettier (and more useful), it also has other very handy features.
function pre_($array)
{
echo '<pre>' . print_r( $array, true ) . '</pre>';
}
You could try something like this. Note it is untested.
function html_var_dump($obj)
{
ob_start();
var_dump($obj);
$output = htmlentities(ob_get_contents());
ob_end_clean();
echo "<pre>$output</pre>";
}
You can use print_r instead of var_dump if you prefer.
I use this
function d($obj)
{
ob_start();
print_r($obj);
$output = htmlspecialchars(ob_get_clean());
echo "<pre>$output</pre>";
}
I change the default_mimetype (default text/html) php.ini setting to text/plain.
One of my favorite tricks, if printing the array is all I'm doing:
header('Content-type: text/plain');
print_r($arr);
Funny you should ask that, I just wrote a short function to save me the pain of having to do this so much.
function pre($option = "open"){
if (is_object($option) || is_array($option)):
print "<pre>";
print_r($option);
print "</pre>";
else:
$option=="open"?print "<pre>": print "</pre>";
endif;
}
If you pass an array or an object to it, it will print it inside pre tags. If you just want an opening tag, then do not pass an argument. If you want a closing tag, pass it any other argument (e.g. 1)
e.g.:
pre($result); //prints in pre tags
pre(); //just prints <pre>
print "hello";
pre(1); //just prints </pre>

Categories