As taken from https://stackoverflow.com/questions/4891301/top-bad-practices-in-php
Is this similar code killing kittens, too?
foreach (file("longFile.txt") as $line) {
// ouch! Kitten killed
}
??
For those who have no idea what am I talking about:
Is PHP getting longFile.txt everytime it goes to next line of file or no? Talking about this code:
foreach (file("longFile.txt") as $line) {
// something
}
In the linked question the for loop incurs a performance hit by calling count on every iteration. foreach uses internal pointers to iterate through the array passed to it.
In your example file() will be called once and the resulting array will be passed to foreach which will iterate through it, thus the kittens are saved. Happy Caturday!
It shouldn't be killing any kittens, since, in order for PHP to get the following line of the file, it has to know the position of the file pointer since the previous line that was pulled off. You are only advancing the iterator, which maintains a reference to the file object.
Also, it's bad practice to be opening a file like that; you should have a variable to store it in and close it when you're done.
You want to use a Duff Device to unroll the loop: http://de.wikipedia.org/wiki/Duff%E2%80%99s_Device. This would be faster then foreach and faster then for loop without using count() on each iteration and it would be faster then concatenating and rtrim the string but the same like using implode().
Is that file really large? Consider:
foreach(new SplFileObject($path) as $line) {
// neither kill puppies nor kittens
}
foreach works always on the concrete iterator. If you're passing an array:
Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. (ref)
So nor array or function call will executed each time the foreach steps ahead.
Related: How to store and reset a PHP array pointer?
No. There are many reasons why kittens die, but foreach loops are not one of them.
Related
I'm fairly certain the answer is no, but is it possible to insert something into an array during a foreach loop? Ideally at the very spot you are at in the array during the loop.
For example:
foreach($stock->StockData as &$stock) {
if($dateTime < $stock['DateTime']) {
// INSERT NEW RECORD AT THIS SPOT IN THE ARRAY
}
}
As I say, I'm fairly certain the answer is no, but rather than build a new array, I just thought I'd ask.
I stand corrected!
http://docstore.mik.ua/orelly/webprog/php/ch05_07.htm
It apparently is just fine to do this in PHP.
According to the reference, PHP operates on a copy of the array when you start a foreach iterator, meaning that the iterator will not be corrupted by operations on the original array within the body of the foreach!
You really don't want to mutate an object is being iterated on. It will break your iterator/loop and could possibly crash the script/program by accessing or changing memory that you don't have access to anymore, possibly because array size has reduced.
For example, is this safe?
foreach($opps_data as $k=>$v) {
$opps_data[$k.'_mixed'] = WXU::MixedCase($v);
}
It seems to work fine. Does that mean PHP makes a copy of the array before it starts looping?
Yes, foreach loop operates on a copy of original array. More info about internal behaviour of foreach can be found in this great blog.
foreach() uses iterators. Array is called then iterator is used for pointing to array which was called.
In this case $opps_data is called only once. Iterator will not reference original array it will be working on copy of $opps_data which was called.
This is a simple programming question, coming from my lack of knowledge of how PHP handles array copying and unsetting during a foreach loop. It's like this, I have an array that comes to me from an outside source formatted in a way I want to change. A simple example would be:
$myData = array('Key1' => array('value1', 'value2'));
But what I want would be something like:
$myData = array([0] => array('MyKey' => array('Key1' => array('value1', 'value2'))));
So I take the first $myData and format it like the second $myData. I'm totally fine with my formatting algorithm. My question lies in finding a way to conserve memory since these arrays might get a little unwieldy. So, during my foreach loop I copy the current array value(s) into the new format, then I unset the value I'm working with from the original array. E.g.:
$formattedData = array();
foreach ($myData as $key => $val) {
// do some formatting here, copy to $reformattedVal
$formattedData[] = $reformattedVal;
unset($myData[$key]);
}
Is the call to unset() a good idea here? I.e., does it conserve memory since I have copied the data and no longer need the original value? Or, does PHP automatically garbage collect the data since I don't reference it in any subsequent code?
The code runs fine, and so far my datasets have been too negligible in size to test for performance differences. I just don't know if I'm setting myself up for some weird bugs or CPU hits later on.
Thanks for any insights.
-sR
Use a reference to the variable in the foreach loop using the & operator. This avoids making a copy of the array in memory for foreach to iterate over.
edit: as pointed out by Artefacto unsetting the variable only decreases the number of references to the original variable, so the memory saved is only on pointers rather than the value of the variable. Bizarrely using a reference actually increases the total memory usage as presumably the value is copied to a new memory location instead of being referenced.
Unless the array is referenced,
foreach operates on a copy of the
specified array and not the array
itself. foreach has some side effects
on the array pointer. Don't rely on
the array pointer during or after the
foreach without resetting it.
Use memory_get_usage() to identify how much memory you are using.
There is a good write up on memory usage and allocation here.
This is useful test code to see memory allocation - try uncommenting the commented lines to see total memory usage in different scenarios.
echo memory_get_usage() . PHP_EOL;
$test = $testCopy = array();
$i = 0;
while ($i++ < 100000) {
$test[] = $i;
}
echo memory_get_usage() . PHP_EOL;
foreach ($test as $k => $v) {
//foreach ($test as $k => &$v) {
$testCopy[$k] = $v;
//unset($test[$k]);
}
echo memory_get_usage() . PHP_EOL;
I was running out of memory while processing lines of a text (xml) file within a loop. For anyone with a similar situation, this worked for me:
while($data = array_pop($xml_data)){
//process $data
}
Please remember the rules of Optimization Club:
The first rule of Optimization Club is, you do not Optimize.
The second rule of Optimization Club is, you do not Optimize without measuring.
If your app is running faster than the underlying transport protocol, the optimization is over.
One factor at a time.
No marketroids, no marketroid schedules.
Testing will go on as long as it has to.
If this is your first night at Optimization Club, you have to write a test case.
Rules #1 and #2 are especially relevant here. Unless you know that you need to optimize, and unless you have measured that need to optimize, then don't do it. Adding the unset will add a run-time hit and will make future programmers why you are doing it.
Leave it alone.
If at any point in the "formatting" you do something like:
$reformattedVal['a']['b'] = $myData[$key];
Then doing unset($myData[$key]); is irrelevant memory-wise because you are only decreasing the reference count of the variable, which now exists in two places (inside $myData[$key] and $reformattedVal['a']['b']). Actually, you save the memory of indexing the variable inside the original array, but that's almost nothing.
Unless you're accessing the element by reference unsetting will do nothing whatsoever, as you can't alter the array during within the iterator.
That said, it's generally considered bad practice to modify the collection you're iterating over - a better approach would be to break down the source array into smaller chunks (by only loading a portion of the source data at a time) and process these, unsetting each entire array "chunk" as you go.
I'm trying to look through an array of records (staff members), in this loop, I call a function which returns another array of records (appointments for each staff member).
foreach($staffmembers as $staffmember)
{
$staffmember['appointments'] = get_staffmember_appointments_for_day($staffmember);
// print_r($staffmember['appointments'] works fine
}
This is working OK, however, later on in the script, I need to loop through the records again, this time making use of the appointment arrays, however they are unavailable.
foreach ($staffmembers as $staffmember)
{
//do some other stuff
//print_r($staffmember['appointments'] no longer does anything
}
Normally, I would perform the function from the first loop, within the second, however this loop is already nested within two others, which would cause the same sql query to be run 168 times.
Can anyone suggest a workaround?
Any advice would be greatly appreciated.
Thanks
foreach iterates over a copy of the array. If you want to change the value, you need to reference it:
foreach($staffmembers as &$staffmember) // <-- note the &
{
$staffmember['appointments'] = get_staffmember_appointments_for_day($staffmember);
// print_r($staffmember['appointments'] works fine
}
From the documentation:
Note: Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it.
and
As of PHP 5, you can easily modify array's elements by preceding $value with &. This will assign reference instead of copying the value.
It's about PHP but I've no doubt many of the same comments will apply to other languages.
Simply put, what are the differences in the different types of loop for PHP? Is one faster/better than the others or should I simply put in the most readable loop?
for ($i = 0; $i < 10; $i++)
{
# code...
}
foreach ($array as $index => $value)
{
# code...
}
do
{
# code...
}
while ($flag == false);
For loop and While loops are entry condition loops. They evaluate condition first, so the statement block associated with the loop won't run even once if the condition fails to meet
The statements inside this for loop block will run 10 times, the value of $i will be 0 to 9;
for ($i = 0; $i < 10; $i++)
{
# code...
}
Same thing done with while loop:
$i = 0;
while ($i < 10)
{
# code...
$i++
}
Do-while loop is exit-condition loop. It's guaranteed to execute once, then it will evaluate condition before repeating the block
do
{
# code...
}
while ($flag == false);
foreach is used to access array elements from start to end. At the beginning of foreach loop, the internal pointer of the array is set to the first element of the array, in next step it is set to the 2nd element of the array and so on till the array ends. In the loop block The value of current array item is available as $value and the key of current item is available as $index.
foreach ($array as $index => $value)
{
# code...
}
You could do the same thing with while loop, like this
while (current($array))
{
$index = key($array); // to get key of the current element
$value = $array[$index]; // to get value of current element
# code ...
next($array); // advance the internal array pointer of $array
}
And lastly: The PHP Manual is your friend :)
This is CS101, but since no one else has mentioned it, while loops evaluate their condition before the code block, and do-while evaluates after the code block, so do-while loops are always guaranteed to run their code block at least once, regardless of the condition.
PHP Benchmarks
#brendan:
The article you cited is seriously outdated and the information is just plain wrong. Especially the last point (use for instead of foreach) is misleading and the justification offered in the article no longer applies to modern versions of .NET.
While it's true that the IEnumerator uses virtual calls, these can actually be inlined by a modern compiler. Furthermore, .NET now knows generics and strongly typed enumerators.
There are a lot of performance tests out there that prove conclusively that for is generally no faster than foreach. Here's an example.
I use the first loop when iterating over a conventional (indexed?) array and the foreach loop when dealing with an associative array. It just seems natural and helps the code flow and be more readable, in my opinion. As for do...while loops, I use those when I have to do more than just flip through an array.
I'm not sure of any performance benefits, though.
Performance is not significantly better in either case. While is useful for more complex tasks than iterating, but for and while are functionally equivalent.
Foreach is nice, but has one important caveat: you can't modify the enumerable you're iterating. So no removing, adding or replacing entries to/in it. Modifying entries (like changing their properties) is OK, of course.
With a foreach loop, a copy of the original array is made in memory to use inside. You shouldn't use them on large structures; a simple for loop is a better choice. You can use a while loop more efficiently on a large non-numerically indexed structure like this:
while(list($key, $value) = each($array)) {
But that approach is particularly ugly for a simple small structure.
while loops are better suited for looping through streams, or as in the following example that you see very frequently in PHP:
while ($row = mysql_fetch_array($result)) {
Almost all of the time the different loops are interchangeable, and it will come down to either a) efficiency, or b) clarity.
If you know the efficiency trade-offs of the different types of loops, then yes, to answer your original question: use the one that looks the most clean.
Each looping construct serves a different purpose.
for - This is used to loop for a specific number of iterations.
foreach - This is used to loop through all of the values in a collection.
while - This is used to loop until you meet a condition.
Of the three, "while" will most likely provide the best performance in most situations. Of course, if you do something like the following, you are basically rewriting the "for" loop (which in c# is slightly more performant).
$count = 0;
do
{
...
$count++;
}
while ($count < 10);
They all have different basic purposes, but they can also be used in somewhat the same way. It completely depends on the specific problem that you are trying to solve.
With a foreach loop, a copy of the original array is made in memory to use inside.
Foreach is nice, but has one important caveat: you can't modify the enumerable you're iterating.
Both of those won't be a problem if you pass by reference instead of value:
foreach ($array as &$value) {
I think this has been allowed since PHP 5.
When accessing the elements of an array, for clarity I would use a foreach whenever possible, and only use a for if you need the actual index values (for example, the same index in multiple arrays). This also minimizes the chance for typo mistakes since for loops make this all too easy. In general, PHP might not be the place be worrying too much about performance. And last but not least, for and foreach have (or should have; I'm not a PHP-er) the same Big-O time (O(n)) so you are looking possibly at a small amount more of memory usage or a slight constant or linear hit in time.
In regards to performance, a foreach is more consuming than a for
http://forums.asp.net/p/1041090/1457897.aspx