foreach changes in PHP7 - php

foreach in PHP7 by default, when iterating by value, operates on a copy of the array according to: http://php.net/manual/en/migration70.incompatible.php
Does it lazily create a copy only if there are changes made to the array or a value or will it always make a copy and in essence make looping over references a performance optimization?
Also, do arrays of objects still loop over/give you references of the objects? Or will they actually also create copies for the foreach and return the objects by value?

In PHP 7, if you iterate an array by value, the copy will be done lazily, only when and if the array is actually modified.
If you iterate an array by reference instead, a separation will be performed at the start of the loop. If the array is currently used in more than one place, this separation will lead to a copy.
Furthermore iterating by reference means that a) the array has to be wrapped into a reference and b) each element has to be wrapped in a reference as well. Creating a reference wrapper is an expensive operation, because it requires allocation.
Additionally iteration by reference requires us to use a modification-safe iteration mechanism. This works by registering the iterator with the array and checking for potentially affected iterators in various array modification operations.
So no, iterating by reference is certainly not an optimization, it's a de-optimization. Using references usually is.

Related

Does PHP garbage collection destroy objects with no reference?

I'm having a hard time finding clear information on PHP's garbage collection as it relates to objects referencing other objects. Specifically, I'm trying to understand what happens when I have a chain of objects that reference each other, and I destroy or unset() the first object in the chain. Will PHP know to GC the remaining objects in the chain or do I have to destroy them all individually to prevent a memory leak?
For example, Say I have a class BinaryTree with a variable rootNode and a Node class that extends BinaryTree and has 3 variables, a parent Node, a leftChild Node, and a rightChild Node. The rootNode will be the Node with a parent that is null, but every other Node created will reference either the rootNode or a subsequent child Node before it as the parent and any Nodes after it as either leftChild or rightChild. The rootNode, therefore, is the only way to reference any of the child nodes.
If I unset() the rootNode, will all other Nodes remain in memory? Would I need to iterate through the entire tree unsetting each Node individually to prevent them from just existing without a way to reference them anymore, or will PHP know that those objects can no longer be referenced and GC them?
So far what I'm reading leads me to believe I would need to iterate the entire list of objects to destroy them individually, but I'm wondering if anyone has any experience with this and can offer some clarity. TIA.
The PHP manual has a section on this:
...a zval container also has an internal reference counting mechanism to optimize memory usage. This second piece of additional information, called "refcount", contains how many variable names (also called symbols) point to this one zval container.
[...]
...when the "refcount" reaches zero, the variable container is removed from memory
[...]
...unsetting a variable removes the symbol, and the reference count of the variable container it points to is decreased by one.
And so you get a cascade effect. Initially all the nodes of your binary tree node have a reference count of 1, but by unsetting the rootNode, the reference count of the root object becomes 0, and the memory used for the root is freed. This involves unsetting the references it has to other objects, whose reference counters are therefore also reduced, ...etc.
So, no, you do not have to unlink the nodes or destroy them yourself. The reference count mechanism will trigger a cascade of memory being freed.

PHP: Array to object standardization good or bad?

I'm currently working on a project which requires me to only output objects when querying database and/or processing data. I have been hearing from other senior developers that, objects are "cheap" in term of memory and, I "kind of" agree with that.
So, my questions:
If I have an array as result of a query. Why not use the array "as is" since it already exists?
Is it really a good practice (besides standardization) to convert it into an object?
Does it really increase performance? (I can't grasp it because by converting the result into an object, I'm basically creating another entity, besides the already existing array, containing the same data).
In my opinion, I would say that you should NOT convert them into objects.
You already have them as arrays, so transforming/copying them into object would require more memory as at a given time, you end up with both, the objects and arrays. If you absolutely do not need any OOP functionality (inheritance, private proprieties/methods, ...) you should stick with your arrays.
But keep in mind: You can often fetch the database and get objects instead of arrays as result. (See PDO fetchAll) Then you already have your objects without having the array.
If you wonder how these both parties work in terms of performance, have a look at
Using arrays VS objects for storing data.
From what I understand, (but I might as well be wrong) PHP arrays are not arrays compared to the classical C arrays. A PHP array is a sort of object, since you have freedoms to i.e. change an array size (simply add a value). So you do not have the exact same performance as you would have with C arrays.
Conclusion: Feel free to keep your arrays. If you do not need the advantages of objects, no need to use ressources to convert them. After all, it's also a question of programming style, best practices and project guidelines.
It depends on what next is the code doing with the array.
If you leave the data in array you should be careful where you pass it next, because you do not want that array go all over the code base back and forth.
With array you cannot define any definable interface between classes or logic code blocks and that is not very predictable.
When you have array passed through 10 methods in 5 classes modifying the contents of that array its very hard to track what and where is happening to the contents.
I like this answer: https://www.reddit.com/r/PHP/comments/29eope/stop_abusing_arrays_in_php/cik8tet/

Custom iteration for SplObjectStorage

I want to modify the way to iterate an SPLObjectStorage object, such as sorting it first by the data (info).
So, in a loop, it goes numerically from a to z (using sort() function)
But, in SPLObjectStorage, there is no access to the array, right ?
Is it possible to do it since we don't have the access to the array of objects ??
No, it's not possible. Internally SplobjectStorage use the same data structure as an array (the HashTable), but it's not an "array-array" as we know from the PHP userland: we only add values and not keys, as keys are actually generated from the values by hashing them (you can even overwrite this by overwriting the getHash method). Another difference is that you can additionally add information to the object.
In short, SplObjectStorage should not be used as an array, but as either a set or a map, there lies its strength.

Eloquent collections: each vs foreach

Might not be a question specific to Eloquent collections, but it just hit me while working with them. Let's just assume we have a $collection object which is an instance of Illuminate\Support\Collection.
Now if we want to iterate over it, what are the pros and cons of using each() with a closure versus a regular foreach. Are there any?
foreach ($collection as $item) {
// Some code
}
versus
$collection->each(function ($item) {
// Some code
});
A foreach statement should be used as a sort of a way to cycle through a collection and perform some sort of logic on it. If what is in it effects other things in the program, then use this loop.
The .each method uses array_map to cycle through each of the objects in the collection and perform a closure on each one. It then returns the resulting array. That is the key! .each should be used if you want to change the collection in some way. Maybe it's an array of cars and you want to make the model upper case or lower case. You would just pass a closure to the .each method that takes the object and calls strtoupper() on the model of each Car object. It then returns the collection with the changes that have been made.
Morale of the story is this: use the .each method to change each item in the array in some way; use the foreach loop to use each object to affect some other part of the program (using some logic statement).
UPDATE (June 7, 2015)
As stated so Eloquently (see what I did there?) below, the above answer is slightly off. The .each method using array_map never actually used the output from the array_map call. So, the new array created by array_map would not be saved on the Collection. To change it, you're better off using the .map method, which also exists on a Collection object.
Using a foreach statement to iterate over each of them makes a bit more sense because you won't be able to access variables outside the Closure unless you make sure to use a use statement, which seems awkward to me.
The implementation when the above answer was originally written can be found here.
.each in Laravel 5.1
The new .each that they are talking about below no longer uses array_map. It simply iterates through each item in the collection and calls the passed in $callback, passing it the item and its key in the array. Functionally, it seems to work the same. I believe using a foreach loop would make more sense when reading the code. However, I see the benefits of using .each because it allows you to chain methods together if that tickles your fancy. It also allows you to return false from the callback to leave the loop early if your business logic demands you to be able to.
For more info on the new implementation, check out the source code.
There is a lot of confusing misinformation in the existing answers.
The Short Answer
The short answer is: There is no major difference between using .each() vs. foreach to iterate over a Laravel collection. Both techniques achieve the same result.
What about modifying items?
Whether or not you're modifying items is irrelevant to whether you use .each() vs. foreach. They both do (and don't!) allow you to modify items in the collection depending on what type of items we're talking about.
Modifying items if the Collection contains objects: If the Collection is a set of PHP objects (such as an Eloquent Collection), either .each() or foreach allow you to modify properties of the objects (such as $item->name = 'foo'). That's simply because of how PHP objects always act like references. If you're trying to replace the entire object with a different object (a less common scenario), use .map() instead.
Modifying items if the Collection contains non-objects: This is less common, but if your Collection contains non-objects, such as strings, .each() doesn't give you a way to modify the values of the collection items. (The return value of the closure is ignored.) Use .map() instead.
So... which one should I use?
In case you're wondering about performance, I did several tests with large collections of both Eloquent items and a collection of strings. In both cases, using foreach was faster than .each(). But we're talking about microseconds. In most real-life scenarios the speed difference wouldn't be significant compared to the time it takes to access the database, etc.
It mostly comes down to your personal preference. Using .each() is nice because you can chain several operations together (for example .where(...).each(...)). I tend to use both in my own code just depending on what seems the cleanest for each situation.
Contrary to what the two other answers say, Collection::each() does not change the values of the items in the Collection, technically speaking. It does use array_map(), but it doesn't store the result of that call.
If you want to modify each item in a collection (such as to cast them to objects as Damon in a comment to the crrently accepted answer), then you should use Collection::map(). This will create a new Collection based on the result of the underlying call to array_map().
It is more beneficial to use the latter: each().
You can chain conditions and write clearer more expressive code, eg:
$example->each()->map()->filter();
This takes you closer to Declarative Programming where you tell the computer what to accomplish instead of how to accomplish.
Some useful articles:
https://martinfowler.com/articles/collection-pipeline/
https://adamwathan.me/refactoring-to-collections/
->each() is the same as foreach(...) but worse.
The main difference here is Variable scope.
In classical foreach you can easily operate variables declared before the foreach. If you are dealing with closure, you would need to inheriting variables from the parent scope with use. This feature is confusing, because when you use it, your function becomes scope dependant...
You can chain ->each() with other functions, but since it does not change the values in the collection, it has very limited use case. And because it is "rare to use", it is not always easily recognizable by developers when they read your code.
Not many people will read your code, cherish those who will.
The foreach() construct does not allow you to change the value of the array item being iterated over, unless you pass the array by reference.
foreach ($array as &$value) $value *= $value;
The each() eloquent method wraps the PHP array_map() function, which allows this.
Like the previous answer states, you should use foreach() if you have any other motivation. This is because the performance of foreach() is much better than array_map().
http://willem.stuursma.name/2010/11/22/a-detailed-look-into-array_map-and-foreach/

Associative Array versus SplObjectStorage

I'm working on code to manage a collection of unique objects. The first prototype of this code utilises an associative array, basically as that's the way I've always done it.
However, I'm also keen on taking advantage of functionality that's been added to more modern versions of PHP such as [SplObjectStorage][1] for doing this instead, partly as a learning experience, partly because it's bound to offer advantages (benchmarks I've seen suggest that SplObjectStorage can be faster than arrays in a lot of cases).
The current implementation has an associative array that I check with in_array() to see if an object is already in the array before adding a new object to it.
The big problem I can see with SplObjectStorage is that it doesn't seem (at first glance) to support key/value associative array behaviour, and can only be treated as an indexed array. However, the documentation for the newer features of PHP isn't up to the standards of the documentation of more established parts of the language and I might simply be missing something.
Can I use SplObjectStorage in place of an associative array? If so, how do I define the key when adding a new object? More importantly, what are the relative advantages and disadvantages of SplObjectStorage when compared to associative arrays?
You shouldn't see the SplObjectStorage as a key-value store, but merely a set of objects. Something is in the set or not, but its position is not important.
The "key" of an element in the SplObjectStorage is in fact the hash of the object. It makes it that it is not possible to add multiple copies of the same object instance to an SplObjectStorage, so you don't have to check if a copy already exists before adding.
However, in PHP 5.4 there is a new method called getHash() which you can override that will return the "hash" of the object. This - in a sense - returns/set the key so you can allow it to store under different conditions.
The main advantage of SplObjectStorage is the fact that you gain lots of methods for dealing and interacting with different sets (contains(), removeAll(), removeAllExcept() etc). Its speed is marginally better, but the memory usage is worse than normal PHP arrays.
Results after running this benchmark with 10,000 iterations on PHP 5.6.13:
Type
Time to fill
Time to check
Memory
SplObjectStorage
0.021285057068
0.019490000000
2131984
Array
0.021125078201
0.020912000000
1411440
Arrays use 34% less memory and are about the same speed as SplObjectStorage.
Results with PHP 7.4.27:
Type
Time to fill
Time to check
Memory
SplObjectStorage
0.019295692444
0.016039848328
848384
Array
0.024008750916
0.022011756897
3215416
Arrays use 3.8 times more memory and are 24% slower than SplObjectStorage.
Results with PHP 8.1.1:
Type
Time to fill
Time to check
Memory
SplObjectStorage
0.009704589844
0.003775596619
768384
Array
0.014604568481
0.012760162354
3215416
Arrays use 4.2 times more memory and are 50% slower than SplObjectStorage.
When all the memory allocated to array is used up, the memory allocated to it will be doubled. In this context, a collection of objects may be more effective structure.

Categories