PHP 7.2 deprecated: while = each() loop without $value [duplicate] - php

With PHP 7.2, each is deprecated. The documentation says:
Warning This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.
How can I update my code to avoid using it? Here are some examples:
$ar = $o->me;
reset($ar);
list($typ, $val) = each($ar);
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = each($out);
for(reset($broken);$kv = each($broken);) {...}
list(, $this->result) = each($this->cache_data);
// iterating to the end of an array or a limit > the length of the array
$i = 0;
reset($array);
while( (list($id, $item) = each($array)) || $i < 30 ) {
// code
$i++;
}
When I execute the code on PHP 7.2 I receive the following error:
Deprecated: The each() function is deprecated. This message will be suppressed on further calls

For your first two example cases, you could use key() and current() to assign the values you need.
$ar = $o->me; // reset isn't necessary, since you just created the array
$typ = key($ar);
$val = current($ar);
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = [key($out), current($out)];
In those cases, you can use next() to advance the cursor afterward, but it may not be necessary if the rest of your code doesn't depend on that.
For the third case, I'd suggest just using a foreach() loop instead and assigning $kv inside the loop.
foreach ($broken as $k => $v) {
$kv = [$k, $v];
}
For the fourth case, it looks like the key is disregarded in list(), so you can assign the current value.
$this->result = current($this->cache_data);
Like the first two cases, it may be necessary to advance the cursor with next() depending on how the rest of your code interacts with $this->cache_data.
Fifth can be replaced with a for() loop.
reset($array);
for ($i = 0; $i < 30; $i++) {
$id = key($array);
$item = current($array);
// code
next($array);
}

2019+ Instant Upgrade of each()
There are actually plenty of cases that each() can be replaced, that's why there are so many different upvoted answers in this question.
-while (list($key, $callback) = each($callbacks)) {
+foreach ($callbacks as $key => $callback) {
// ...
}
And:
-while (list($key) = each($callbacks)) {
+foreach (array_keys($callbacks) as $key) {
// ...
}
You can replace one by one manually. But isn't there a better way?
I help to migrate projects, where are over 150+ cases like this. I'm lazy so I made a tool called Rector, that converts the code the way above (+ there are more cases, but I don't want to spam the answer).
It's part of the PHP_72 set.
4 Steps to Upgrade your Code
1. Install it
composer require rector/rector --dev
2. Create rector.php config
vendor/bin/rector init
3. Add PHP_72 set
<?php
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters->set(Option::SETS, [
Setlist::PHP_72,
]);
};
4. Run it on your code
vendor/bin/rector process src --set php72
I hope it helps you with your migration.
If there is some bug or anomaly, it's Rector missed case. Create an issue, so we can fix it and make it work for every case possible.

I found a way to fix it and thought to share the information. Here are also other cases about how to upgrade each() loops to foreach().
Case 1: Missing $value
reset($array);
while (list($key, ) = each($array)) {
Update to:
foreach(array_keys($array) as $key) {
Case 2: Missing $key
reset($array);
while (list(, $value) = each($array)) {
Update to:
foreach($array as $value) {
Case 3: Not missing anything
reset($array);
while (list($key, $value) = each($array)) {
Update to:
foreach($array as $key => $value) {

you could create your own each() function using key(), current() and next(). then replace your calls with that function, like this:
<?php
function myEach(&$arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
1.
$ar = $o->me;
reset($ar);
list($typ, $val) = myEach($ar);
2.
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = myEach($out);
3.
for(reset($broken);$kv = myEach($broken);) {...}

reset($array);
while (list($key, $value) = each($array)) {
UPDATE
reset($array);
foreach($array as $key => $value) {

Here are some ways to do it:
The standard foreach loop (very readable):
foreach($this->contents as list($products_id)) {
$total_items += $this->get_quantity($products_id);
}
Or, reducing:
$total_items = array_reduce($this->contents, function($acc, $item) {
return $acc + $this->get_quantity($products_id[0]);
});
Or, in a functional expression:
$total_items = array_sum(array_map([$this, 'get_quantity'],
array_column($this->contents, 0)));
None of these methods need reset($this->contents); preceding it.

The way you most definitely shouldn't do is put the function "back into php" by adding it to the auto_prepend_file setting in php.ini
auto_prepend_file = "/var/www/php/auto_prepend.php"
Then make the file and enter in the function with an function_exists wrapper.
<?php
/**
* Adds the depreciated each() function back into 7.2
*/
if (!function_exists('each')) {
function each($arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
}
This essentially declares the function before your php application runs. When your application tries to run the each function it'll use your version instead.
This is absolutely not the way you should be approaching this problem, especially in production! However you're a developer with time constraints and you just want to try arbitrary frameworks for your next project and they haven't been updated to work on your local development server without winding back your php version.
When you've committed to a code base for your project please go ahead and implement the changes in the accepted answer because they do work.
I used Wee Zel's emulation of the each function

To expand on Petro Mäntylä excellent correct answer for Case 3:
Here is a full example of a "Case 3" situation, because I find full examples far more informative that one line code fragments:
This is genuine code from a 3rd party old code base (TCPDF)
DEPRECATED:
while (list($id, $name) = each($attr_array)) {
$dom[$key]['attribute'][$name] = $attr_array[$id];
...
...
}
FIXED:
// while (list($id, $name) = each($attr_array)) {
foreach($attr_array as $feKey => $feRow){
// $dom[$key]['attribute'][$name] = $attr_array[$id];
$dom[$key]['attribute'][$feRow] = $attr_array[$feKey];
...
...
}
unset($feKey,$feRow);

Replace this code
while (list($_key,$_resourceTypeNode) = each($GLOBALS['config']['ResourceType'])) {
// if ($_resourceTypeNode['name'] === $resourceTypeName) {
// $this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
// return $this->_resourceTypeConfigCache[$resourceTypeName];
// }
// }
with this one
foreach ($GLOBALS['config']['ResourceType'] as $key => $_resourceTypeNode) {
if (isset($_resourceTypeNode['name'])) {
if ($_resourceTypeNode['name'] === $resourceTypeName) {
$this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
return $this->_resourceTypeConfigCache[$resourceTypeName];
}
}
}

// while (list($products_id, ) = each($this->contents)) {
// $total_items += $this->get_quantity($products_id);
// }
Update To :
foreach(array_keys($this->contents) as $products_id) {
$total_items += $this->get_quantity($products_id);
}
Other Condition:
foreach($this->contents as $key =>$value) {
$total_items += $this->get_quantity($products_id);
}

For all commentators. Function foreach not work with dinamic arrays with changing of elements count. I think using custom function "each" John Tilley - single right way for dinamic arrays. For static arrays not use "each" nobody. "Foreach" know all.

What about using this function?
function array_fetch(array $a) {
$element = current($a);
next($a);
return $element;
}

Related

An alternative to each() function [duplicate]

With PHP 7.2, each is deprecated. The documentation says:
Warning This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.
How can I update my code to avoid using it? Here are some examples:
$ar = $o->me;
reset($ar);
list($typ, $val) = each($ar);
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = each($out);
for(reset($broken);$kv = each($broken);) {...}
list(, $this->result) = each($this->cache_data);
// iterating to the end of an array or a limit > the length of the array
$i = 0;
reset($array);
while( (list($id, $item) = each($array)) || $i < 30 ) {
// code
$i++;
}
When I execute the code on PHP 7.2 I receive the following error:
Deprecated: The each() function is deprecated. This message will be suppressed on further calls
For your first two example cases, you could use key() and current() to assign the values you need.
$ar = $o->me; // reset isn't necessary, since you just created the array
$typ = key($ar);
$val = current($ar);
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = [key($out), current($out)];
In those cases, you can use next() to advance the cursor afterward, but it may not be necessary if the rest of your code doesn't depend on that.
For the third case, I'd suggest just using a foreach() loop instead and assigning $kv inside the loop.
foreach ($broken as $k => $v) {
$kv = [$k, $v];
}
For the fourth case, it looks like the key is disregarded in list(), so you can assign the current value.
$this->result = current($this->cache_data);
Like the first two cases, it may be necessary to advance the cursor with next() depending on how the rest of your code interacts with $this->cache_data.
Fifth can be replaced with a for() loop.
reset($array);
for ($i = 0; $i < 30; $i++) {
$id = key($array);
$item = current($array);
// code
next($array);
}
2019+ Instant Upgrade of each()
There are actually plenty of cases that each() can be replaced, that's why there are so many different upvoted answers in this question.
-while (list($key, $callback) = each($callbacks)) {
+foreach ($callbacks as $key => $callback) {
// ...
}
And:
-while (list($key) = each($callbacks)) {
+foreach (array_keys($callbacks) as $key) {
// ...
}
You can replace one by one manually. But isn't there a better way?
I help to migrate projects, where are over 150+ cases like this. I'm lazy so I made a tool called Rector, that converts the code the way above (+ there are more cases, but I don't want to spam the answer).
It's part of the PHP_72 set.
4 Steps to Upgrade your Code
1. Install it
composer require rector/rector --dev
2. Create rector.php config
vendor/bin/rector init
3. Add PHP_72 set
<?php
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters->set(Option::SETS, [
Setlist::PHP_72,
]);
};
4. Run it on your code
vendor/bin/rector process src --set php72
I hope it helps you with your migration.
If there is some bug or anomaly, it's Rector missed case. Create an issue, so we can fix it and make it work for every case possible.
I found a way to fix it and thought to share the information. Here are also other cases about how to upgrade each() loops to foreach().
Case 1: Missing $value
reset($array);
while (list($key, ) = each($array)) {
Update to:
foreach(array_keys($array) as $key) {
Case 2: Missing $key
reset($array);
while (list(, $value) = each($array)) {
Update to:
foreach($array as $value) {
Case 3: Not missing anything
reset($array);
while (list($key, $value) = each($array)) {
Update to:
foreach($array as $key => $value) {
you could create your own each() function using key(), current() and next(). then replace your calls with that function, like this:
<?php
function myEach(&$arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
1.
$ar = $o->me;
reset($ar);
list($typ, $val) = myEach($ar);
2.
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = myEach($out);
3.
for(reset($broken);$kv = myEach($broken);) {...}
reset($array);
while (list($key, $value) = each($array)) {
UPDATE
reset($array);
foreach($array as $key => $value) {
Here are some ways to do it:
The standard foreach loop (very readable):
foreach($this->contents as list($products_id)) {
$total_items += $this->get_quantity($products_id);
}
Or, reducing:
$total_items = array_reduce($this->contents, function($acc, $item) {
return $acc + $this->get_quantity($products_id[0]);
});
Or, in a functional expression:
$total_items = array_sum(array_map([$this, 'get_quantity'],
array_column($this->contents, 0)));
None of these methods need reset($this->contents); preceding it.
The way you most definitely shouldn't do is put the function "back into php" by adding it to the auto_prepend_file setting in php.ini
auto_prepend_file = "/var/www/php/auto_prepend.php"
Then make the file and enter in the function with an function_exists wrapper.
<?php
/**
* Adds the depreciated each() function back into 7.2
*/
if (!function_exists('each')) {
function each($arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
}
This essentially declares the function before your php application runs. When your application tries to run the each function it'll use your version instead.
This is absolutely not the way you should be approaching this problem, especially in production! However you're a developer with time constraints and you just want to try arbitrary frameworks for your next project and they haven't been updated to work on your local development server without winding back your php version.
When you've committed to a code base for your project please go ahead and implement the changes in the accepted answer because they do work.
I used Wee Zel's emulation of the each function
To expand on Petro Mäntylä excellent correct answer for Case 3:
Here is a full example of a "Case 3" situation, because I find full examples far more informative that one line code fragments:
This is genuine code from a 3rd party old code base (TCPDF)
DEPRECATED:
while (list($id, $name) = each($attr_array)) {
$dom[$key]['attribute'][$name] = $attr_array[$id];
...
...
}
FIXED:
// while (list($id, $name) = each($attr_array)) {
foreach($attr_array as $feKey => $feRow){
// $dom[$key]['attribute'][$name] = $attr_array[$id];
$dom[$key]['attribute'][$feRow] = $attr_array[$feKey];
...
...
}
unset($feKey,$feRow);
Replace this code
while (list($_key,$_resourceTypeNode) = each($GLOBALS['config']['ResourceType'])) {
// if ($_resourceTypeNode['name'] === $resourceTypeName) {
// $this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
// return $this->_resourceTypeConfigCache[$resourceTypeName];
// }
// }
with this one
foreach ($GLOBALS['config']['ResourceType'] as $key => $_resourceTypeNode) {
if (isset($_resourceTypeNode['name'])) {
if ($_resourceTypeNode['name'] === $resourceTypeName) {
$this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
return $this->_resourceTypeConfigCache[$resourceTypeName];
}
}
}
// while (list($products_id, ) = each($this->contents)) {
// $total_items += $this->get_quantity($products_id);
// }
Update To :
foreach(array_keys($this->contents) as $products_id) {
$total_items += $this->get_quantity($products_id);
}
Other Condition:
foreach($this->contents as $key =>$value) {
$total_items += $this->get_quantity($products_id);
}
For all commentators. Function foreach not work with dinamic arrays with changing of elements count. I think using custom function "each" John Tilley - single right way for dinamic arrays. For static arrays not use "each" nobody. "Foreach" know all.
What about using this function?
function array_fetch(array $a) {
$element = current($a);
next($a);
return $element;
}

Deprecated : The each() function is deprecated. This message will be suppressed on further calls in [duplicate]

With PHP 7.2, each is deprecated. The documentation says:
Warning This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.
How can I update my code to avoid using it? Here are some examples:
$ar = $o->me;
reset($ar);
list($typ, $val) = each($ar);
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = each($out);
for(reset($broken);$kv = each($broken);) {...}
list(, $this->result) = each($this->cache_data);
// iterating to the end of an array or a limit > the length of the array
$i = 0;
reset($array);
while( (list($id, $item) = each($array)) || $i < 30 ) {
// code
$i++;
}
When I execute the code on PHP 7.2 I receive the following error:
Deprecated: The each() function is deprecated. This message will be suppressed on further calls
For your first two example cases, you could use key() and current() to assign the values you need.
$ar = $o->me; // reset isn't necessary, since you just created the array
$typ = key($ar);
$val = current($ar);
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = [key($out), current($out)];
In those cases, you can use next() to advance the cursor afterward, but it may not be necessary if the rest of your code doesn't depend on that.
For the third case, I'd suggest just using a foreach() loop instead and assigning $kv inside the loop.
foreach ($broken as $k => $v) {
$kv = [$k, $v];
}
For the fourth case, it looks like the key is disregarded in list(), so you can assign the current value.
$this->result = current($this->cache_data);
Like the first two cases, it may be necessary to advance the cursor with next() depending on how the rest of your code interacts with $this->cache_data.
Fifth can be replaced with a for() loop.
reset($array);
for ($i = 0; $i < 30; $i++) {
$id = key($array);
$item = current($array);
// code
next($array);
}
2019+ Instant Upgrade of each()
There are actually plenty of cases that each() can be replaced, that's why there are so many different upvoted answers in this question.
-while (list($key, $callback) = each($callbacks)) {
+foreach ($callbacks as $key => $callback) {
// ...
}
And:
-while (list($key) = each($callbacks)) {
+foreach (array_keys($callbacks) as $key) {
// ...
}
You can replace one by one manually. But isn't there a better way?
I help to migrate projects, where are over 150+ cases like this. I'm lazy so I made a tool called Rector, that converts the code the way above (+ there are more cases, but I don't want to spam the answer).
It's part of the PHP_72 set.
4 Steps to Upgrade your Code
1. Install it
composer require rector/rector --dev
2. Create rector.php config
vendor/bin/rector init
3. Add PHP_72 set
<?php
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters->set(Option::SETS, [
Setlist::PHP_72,
]);
};
4. Run it on your code
vendor/bin/rector process src --set php72
I hope it helps you with your migration.
If there is some bug or anomaly, it's Rector missed case. Create an issue, so we can fix it and make it work for every case possible.
I found a way to fix it and thought to share the information. Here are also other cases about how to upgrade each() loops to foreach().
Case 1: Missing $value
reset($array);
while (list($key, ) = each($array)) {
Update to:
foreach(array_keys($array) as $key) {
Case 2: Missing $key
reset($array);
while (list(, $value) = each($array)) {
Update to:
foreach($array as $value) {
Case 3: Not missing anything
reset($array);
while (list($key, $value) = each($array)) {
Update to:
foreach($array as $key => $value) {
you could create your own each() function using key(), current() and next(). then replace your calls with that function, like this:
<?php
function myEach(&$arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
1.
$ar = $o->me;
reset($ar);
list($typ, $val) = myEach($ar);
2.
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = myEach($out);
3.
for(reset($broken);$kv = myEach($broken);) {...}
reset($array);
while (list($key, $value) = each($array)) {
UPDATE
reset($array);
foreach($array as $key => $value) {
Here are some ways to do it:
The standard foreach loop (very readable):
foreach($this->contents as list($products_id)) {
$total_items += $this->get_quantity($products_id);
}
Or, reducing:
$total_items = array_reduce($this->contents, function($acc, $item) {
return $acc + $this->get_quantity($products_id[0]);
});
Or, in a functional expression:
$total_items = array_sum(array_map([$this, 'get_quantity'],
array_column($this->contents, 0)));
None of these methods need reset($this->contents); preceding it.
The way you most definitely shouldn't do is put the function "back into php" by adding it to the auto_prepend_file setting in php.ini
auto_prepend_file = "/var/www/php/auto_prepend.php"
Then make the file and enter in the function with an function_exists wrapper.
<?php
/**
* Adds the depreciated each() function back into 7.2
*/
if (!function_exists('each')) {
function each($arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
}
This essentially declares the function before your php application runs. When your application tries to run the each function it'll use your version instead.
This is absolutely not the way you should be approaching this problem, especially in production! However you're a developer with time constraints and you just want to try arbitrary frameworks for your next project and they haven't been updated to work on your local development server without winding back your php version.
When you've committed to a code base for your project please go ahead and implement the changes in the accepted answer because they do work.
I used Wee Zel's emulation of the each function
To expand on Petro Mäntylä excellent correct answer for Case 3:
Here is a full example of a "Case 3" situation, because I find full examples far more informative that one line code fragments:
This is genuine code from a 3rd party old code base (TCPDF)
DEPRECATED:
while (list($id, $name) = each($attr_array)) {
$dom[$key]['attribute'][$name] = $attr_array[$id];
...
...
}
FIXED:
// while (list($id, $name) = each($attr_array)) {
foreach($attr_array as $feKey => $feRow){
// $dom[$key]['attribute'][$name] = $attr_array[$id];
$dom[$key]['attribute'][$feRow] = $attr_array[$feKey];
...
...
}
unset($feKey,$feRow);
Replace this code
while (list($_key,$_resourceTypeNode) = each($GLOBALS['config']['ResourceType'])) {
// if ($_resourceTypeNode['name'] === $resourceTypeName) {
// $this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
// return $this->_resourceTypeConfigCache[$resourceTypeName];
// }
// }
with this one
foreach ($GLOBALS['config']['ResourceType'] as $key => $_resourceTypeNode) {
if (isset($_resourceTypeNode['name'])) {
if ($_resourceTypeNode['name'] === $resourceTypeName) {
$this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
return $this->_resourceTypeConfigCache[$resourceTypeName];
}
}
}
// while (list($products_id, ) = each($this->contents)) {
// $total_items += $this->get_quantity($products_id);
// }
Update To :
foreach(array_keys($this->contents) as $products_id) {
$total_items += $this->get_quantity($products_id);
}
Other Condition:
foreach($this->contents as $key =>$value) {
$total_items += $this->get_quantity($products_id);
}
For all commentators. Function foreach not work with dinamic arrays with changing of elements count. I think using custom function "each" John Tilley - single right way for dinamic arrays. For static arrays not use "each" nobody. "Foreach" know all.
What about using this function?
function array_fetch(array $a) {
$element = current($a);
next($a);
return $element;
}

Replacing deprecated function breaks code [duplicate]

With PHP 7.2, each is deprecated. The documentation says:
Warning This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.
How can I update my code to avoid using it? Here are some examples:
$ar = $o->me;
reset($ar);
list($typ, $val) = each($ar);
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = each($out);
for(reset($broken);$kv = each($broken);) {...}
list(, $this->result) = each($this->cache_data);
// iterating to the end of an array or a limit > the length of the array
$i = 0;
reset($array);
while( (list($id, $item) = each($array)) || $i < 30 ) {
// code
$i++;
}
When I execute the code on PHP 7.2 I receive the following error:
Deprecated: The each() function is deprecated. This message will be suppressed on further calls
For your first two example cases, you could use key() and current() to assign the values you need.
$ar = $o->me; // reset isn't necessary, since you just created the array
$typ = key($ar);
$val = current($ar);
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = [key($out), current($out)];
In those cases, you can use next() to advance the cursor afterward, but it may not be necessary if the rest of your code doesn't depend on that.
For the third case, I'd suggest just using a foreach() loop instead and assigning $kv inside the loop.
foreach ($broken as $k => $v) {
$kv = [$k, $v];
}
For the fourth case, it looks like the key is disregarded in list(), so you can assign the current value.
$this->result = current($this->cache_data);
Like the first two cases, it may be necessary to advance the cursor with next() depending on how the rest of your code interacts with $this->cache_data.
Fifth can be replaced with a for() loop.
reset($array);
for ($i = 0; $i < 30; $i++) {
$id = key($array);
$item = current($array);
// code
next($array);
}
2019+ Instant Upgrade of each()
There are actually plenty of cases that each() can be replaced, that's why there are so many different upvoted answers in this question.
-while (list($key, $callback) = each($callbacks)) {
+foreach ($callbacks as $key => $callback) {
// ...
}
And:
-while (list($key) = each($callbacks)) {
+foreach (array_keys($callbacks) as $key) {
// ...
}
You can replace one by one manually. But isn't there a better way?
I help to migrate projects, where are over 150+ cases like this. I'm lazy so I made a tool called Rector, that converts the code the way above (+ there are more cases, but I don't want to spam the answer).
It's part of the PHP_72 set.
4 Steps to Upgrade your Code
1. Install it
composer require rector/rector --dev
2. Create rector.php config
vendor/bin/rector init
3. Add PHP_72 set
<?php
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters->set(Option::SETS, [
Setlist::PHP_72,
]);
};
4. Run it on your code
vendor/bin/rector process src --set php72
I hope it helps you with your migration.
If there is some bug or anomaly, it's Rector missed case. Create an issue, so we can fix it and make it work for every case possible.
I found a way to fix it and thought to share the information. Here are also other cases about how to upgrade each() loops to foreach().
Case 1: Missing $value
reset($array);
while (list($key, ) = each($array)) {
Update to:
foreach(array_keys($array) as $key) {
Case 2: Missing $key
reset($array);
while (list(, $value) = each($array)) {
Update to:
foreach($array as $value) {
Case 3: Not missing anything
reset($array);
while (list($key, $value) = each($array)) {
Update to:
foreach($array as $key => $value) {
you could create your own each() function using key(), current() and next(). then replace your calls with that function, like this:
<?php
function myEach(&$arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
1.
$ar = $o->me;
reset($ar);
list($typ, $val) = myEach($ar);
2.
$out = array('me' => array(), 'mytype' => 2, '_php_class' => null);
$expected = myEach($out);
3.
for(reset($broken);$kv = myEach($broken);) {...}
reset($array);
while (list($key, $value) = each($array)) {
UPDATE
reset($array);
foreach($array as $key => $value) {
Here are some ways to do it:
The standard foreach loop (very readable):
foreach($this->contents as list($products_id)) {
$total_items += $this->get_quantity($products_id);
}
Or, reducing:
$total_items = array_reduce($this->contents, function($acc, $item) {
return $acc + $this->get_quantity($products_id[0]);
});
Or, in a functional expression:
$total_items = array_sum(array_map([$this, 'get_quantity'],
array_column($this->contents, 0)));
None of these methods need reset($this->contents); preceding it.
The way you most definitely shouldn't do is put the function "back into php" by adding it to the auto_prepend_file setting in php.ini
auto_prepend_file = "/var/www/php/auto_prepend.php"
Then make the file and enter in the function with an function_exists wrapper.
<?php
/**
* Adds the depreciated each() function back into 7.2
*/
if (!function_exists('each')) {
function each($arr) {
$key = key($arr);
$result = ($key === null) ? false : [$key, current($arr), 'key' => $key, 'value' => current($arr)];
next($arr);
return $result;
}
}
This essentially declares the function before your php application runs. When your application tries to run the each function it'll use your version instead.
This is absolutely not the way you should be approaching this problem, especially in production! However you're a developer with time constraints and you just want to try arbitrary frameworks for your next project and they haven't been updated to work on your local development server without winding back your php version.
When you've committed to a code base for your project please go ahead and implement the changes in the accepted answer because they do work.
I used Wee Zel's emulation of the each function
To expand on Petro Mäntylä excellent correct answer for Case 3:
Here is a full example of a "Case 3" situation, because I find full examples far more informative that one line code fragments:
This is genuine code from a 3rd party old code base (TCPDF)
DEPRECATED:
while (list($id, $name) = each($attr_array)) {
$dom[$key]['attribute'][$name] = $attr_array[$id];
...
...
}
FIXED:
// while (list($id, $name) = each($attr_array)) {
foreach($attr_array as $feKey => $feRow){
// $dom[$key]['attribute'][$name] = $attr_array[$id];
$dom[$key]['attribute'][$feRow] = $attr_array[$feKey];
...
...
}
unset($feKey,$feRow);
Replace this code
while (list($_key,$_resourceTypeNode) = each($GLOBALS['config']['ResourceType'])) {
// if ($_resourceTypeNode['name'] === $resourceTypeName) {
// $this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
// return $this->_resourceTypeConfigCache[$resourceTypeName];
// }
// }
with this one
foreach ($GLOBALS['config']['ResourceType'] as $key => $_resourceTypeNode) {
if (isset($_resourceTypeNode['name'])) {
if ($_resourceTypeNode['name'] === $resourceTypeName) {
$this->_resourceTypeConfigCache[$resourceTypeName] = new CKFinder_Connector_Core_ResourceTypeConfig($_resourceTypeNode);
return $this->_resourceTypeConfigCache[$resourceTypeName];
}
}
}
// while (list($products_id, ) = each($this->contents)) {
// $total_items += $this->get_quantity($products_id);
// }
Update To :
foreach(array_keys($this->contents) as $products_id) {
$total_items += $this->get_quantity($products_id);
}
Other Condition:
foreach($this->contents as $key =>$value) {
$total_items += $this->get_quantity($products_id);
}
For all commentators. Function foreach not work with dinamic arrays with changing of elements count. I think using custom function "each" John Tilley - single right way for dinamic arrays. For static arrays not use "each" nobody. "Foreach" know all.
What about using this function?
function array_fetch(array $a) {
$element = current($a);
next($a);
return $element;
}

Deleting unknown level of array dynamic(not using eval)

I will have a lot unknown levels of array; I would like to delete the selected ones.
Example:
$data = array(
'level1' => array(
'level2' => array(
'level3' => array(
'level4' => 'the end level at currently example, but not always',
'level4_remain' => 'this data will remain'
),
'remain' => 'this data would not delete in this example too'
)
)
'root' => 'this data would not delete in this example'
);
I have a class called vars; it stores all variables globally that I can fetch at anytime; add at anytime, but now that I'm going to make the delete method, I worry about the multi-level issue:
class vars {
public static $data = array();
public static function get($key){
$key = explode('/', $key);
$v = self::$data;
foreach($key as $k)
{
if(!isset($v[$k]))
{
return null;
}
$v = &$v[$k];
}
return $v;
}
public static function has($key){
.........
}
public static function del($key){
$key = explode('/', $key);
//....Any idea how to do this....//
}
}
and I will use the get method like this:
$blabla = vars::get('level1/level2/level3/level4');
and return correct data, but in the del method, I have no idea what to do:
$deleted = vars::del('level1/level2/level3/level4');
It needs to be finished after deleting the array:
unset($data['level1']['level2']['level3']['level4']);
After doing some research I found this, but this is just for "set" level of array, not automatically set as many level as it can be:
foreach ($data as $k1 => $arr) {
foreach ($arr as $k2 => $arr2) {
foreach ($arr2 as $k3 => $arr3) {
if ($k3 == $key) {
unset($rules[$k1][$k2][$k3]);
}
}
}
}
and I think it can be done like this but is quite ugly:
foreach($data as $k1 => $arr1){
if(is_array($arr1)){
foreach($arr1 as $k2 => $arr2){
//...keep writing the if(is_array()){}else{} and foreach(){} for more level deeper...//
}
}else{
//...the unset should be here...//
}
}
After some research, eval might be harmful, so anyone have any ideas how to do this using any method except eval?
If you don't mind using eval...
public static function del($levels)
{
$lvls = explode('/', $levels);
$count = count($lvls);
$eval = '';
for ($i=0; $i<$count; ++$i) {
// Current Array Key
$key = $lvls[$i];
$eval .= "['$key']";
}
$eval = 'self::data' . $eval;
eval("unset($eval);");
}
However, running untrusted (like User input) through eval can be dangerous.
To avoid eval, you can go the recursive route. The trick is to loop over your indices and pass a subarray reference repeatedly, similar to your get function, just recursively.
public static function del($key){
$key = array_reverse( explode('/', $key) ); // _reverse for _pop later
del_r(self::$data,$key); // start the recursive function
}
private static function del_r(&$array,$keys) { // shouldn't be used from outside
$key = array_pop($keys); // extract next key, this removes it from $keys
if (count($keys)) { // still keys left -> recurse further
del_r($array[$key],$keys); // pass subarray by reference
}
else { // no more keys -> delete the element at the current $key
unset($array[$key]);
}
}
Depending on the number of keys, you can use array_shift() instead of array_pop() and remove the array_reverse().

PHP combinations of array elements

I want to generate all possible combination of array elements to fill a placeholder, the placeholder size could vary.
Let say I have array $a = array(3, 2, 9, 7) and placeholder size is 6. I want to generate something like the following:
3,3,3,3,3,3
2,3,3,3,3,3
2,2,3,3,3,3
...........
...........
7,7,7,7,7,9
7,7,7,7,7,7
However (2,3,3,3,3,3) would be considered the same as (3,2,3,3,3,3) so the later one doesn't count.
Could anyone point me to the right direction? I know there is Math_Combinatorics pear package, but that one is only applicable to placeholder size <= count($a).
Edit
I am thinking that this one is similar to bits string combination though with different number base
I have no PHP source code for you but some sources that might help.
Some C code. Look at 2.1:
http://www.aconnect.de/friends/editions/computer/combinatoricode_g.html
Delphi code: combination without repetition of N elements without use for..to..do
Wiki article here
Well it took quit some time to figure this one out.
So i split the question into multiple parts
1.
I firsrt made an array with all the possible value options.
function create_all_array($placeholder, array $values)
{
if ($placeholder <= 0) {
return [];
}
$stack = [];
$values = array_unique($values);
foreach ($values as $value) {
$stack[] = [
'first' => $value,
'childs' => create_all_array($placeholder - 1, $values)
];
}
return $stack;
}
2.
Then I made a function to stransform this massive amount of data into string (no check for uniques).
function string($values, $prefix = '')
{
$stack = [];
foreach($values as $value) {
$sub_prefix = $prefix . $value['first'];
if (empty($value['childs'])) {
$stack[$sub_prefix] = (int)$sub_prefix;
} else {
$stack = array_merge($stack, string($value['childs'], $sub_prefix));
}
}
return $stack;
}
3.
Then the hard part came. Check for duplicates. This was harder than expected, but found some good anser to it and refactored it for my use.
function has_duplicate($string, $items)
{
$explode = str_split ($string);
foreach($items as $item) {
$item_explode = str_split($item);
sort($explode);
$string = implode('',$explode);
sort($item_explode);
$item = implode($item_explode);
if ($string == $item) {
return true;
}
}
return false;
}
4.
The last step was to combine the intel into a new funciton :P
function unique_string($placeholder, array $values)
{
$stack = string(create_all_array($placeholder, $values));
$check_stack = [];
foreach($stack as $key => $item) {
if (has_duplicate($item, $check_stack)) {
unset($stack[$key]);
}
$check_stack[] = $item;
}
return $stack;
}
Now you can use it simple as followed
unique_string(3 /* amount of dept */, [1,2,3] /* keys */);
Ps the code is based for PHP5.4+, to convert to lower you need to change the [] to array() but I love the new syntax so sorry :P

Categories