<?php
namespace foo;
class bar{
public static function runner( $data )
{
extract ($data);
foreach( $elements as $element )
{
$varsToWipe = array_keys($element);
extract( $element );
/*
bunch of code using the variables extracted from $element, eg if( isset($runnerSpeed) )
*/
//now i want to be able to unset all the elements set from the extract within the foreach loop
foreach( $varsToWipe as $var )
{
//but what goes here in the unset function?
\unset( );
}
}
}
}
How can I unset the variables extracted from within the foreach loop in the runner method?
The contents of $data can vary and the vars need to be unset so as not used again on the next loop iteration. I know i can ref the array itself but would be quicker to write if this could work...
Thanks,
John
A simpler foreach example:
$array = range(0,6);
$i = 0;
foreach( $array as $a )
{
echo $i.' ';
if( $i == 0 )
{
$egg = true;
}
++$i;
if( isset($egg) )
{
echo 'eggs ';
}
}
will print
0 eggs 1 eggs 2 eggs 3 eggs 4 eggs 5 eggs 6 eggs
You could then add a removal of $egg from the globals as this is where is sits:
$array = range(0,6);
$i = 0;
foreach( $array as $a )
{
echo $i.' ';
if( $i == 0 )
{
$egg = true;
}
else
{
unset( $GLOBALS['egg'] );
}
++$i;
if( isset($egg) )
{
echo 'eggs ';
}
}
Now it would print:
0 eggs 1 2 3 4 5 6
But what do you unset when you are within a class method, where is the variable actually stored?
Don't use the extract() function in the first place and simply iterate over the given array.
<?php
class Foo {
public function runner($data) {
foreach ($data as $delta => $element) {
if ($element === "something") {
// do something
}
}
}
}
Of course you could unset something inside the array now, but why would you want to? I can't see a reason why because the array isn't passed by reference and isn't used after the loop so it will go out of scope and is simply collected by the garbage collector.
In case of nested arrays:
foreach ($data as $delta => $element) {
if (is_array($element)) {
foreach ($element as $deltaInner => $elementInner) {
// ...
Pretty late now but for reference.
You can unset() them directly with:
unset($$var);
What extract does is equivalent to:
foreach($arr as $key => $value) {
$$key = $value;
}
So to unset you do:
foreach(array_keys($arr) as $key) {
unset($$key);
}
But like mentioned, you don't need to unset() them as they are deleted when the function completes as they will become out of scope.
Also, extract() is totally fine as it (most likely) does not copy the data values, it just creates new symbols for each array key. PHP internally will only copy the values if you actually modify it ie. copy-on-write with reference counting.
You can off course test this using memory_get_usage() before and after you do extract() on a large array.
Related
Guys I have an array with objects,
I want the last item in the foreach loop do something else then the rest.
How do I archive that?
if(sizeof($testDup) > 3){
} else {
foreach ($testDup as $d) {
}
}
$test array(3)
432 => test_id -> 21
431 => test_id -> 21
435 => test_id -> 21
This will process the array of objects and do something else with the last element:
$data = '';
$arrayWithObjects = array(
(object)array('test1', 'test2'),
(object)array('test1', 'test2'),
(object)array('test1', 'test2'),
);
foreach ($arrayWithObjects as $object) {
// Can't get next in the array, so is last element
if (!next($arrayWithObjects)) {
// Process last element
$data .= $object->{1};
} else {
// Process all other elements
$data .= $object->{0};
}
}
var_dump($data); // "test1test1test2"
you can compare the current one with the end():
class Test {
public function __construct(private string $name) {}
public function read(): string {
return sprintf('%s: hurray', $this->name);
}
public function readLast():string {
return sprintf('%s: am I last?', $this->name);
}
}
$array = [
new Test('first'),
new Test('second'),
new Test('third'),
new Test('fourth'),
];
foreach( $array as $object ){
if($object === end($array)) {
echo $object->readLast().PHP_EOL;
}else{
echo $object->read().PHP_EOL;
}
}
As an alternative to checking if the current item is the last one (which the other answers show), you could use array_slice() to get the start of the array to loop over and then end() to get the last element of the array.
$data = [/*...*/]
foreach ($item as array_splice($data, 0, -1, true) {
$item->foo();
}
if (($item = end($data) !== false) {
$item->bar();
}
In my opinion, this code is easier to read (and metrics like cyclomatic complexity agree) than the nested if $item === end($data) check. If the same is true on your specific case will depend on what, exactly is in the loop and how much of it is different.
In addition, if your array is large, this approach may offer (slightly) better performance (but if your array is large and a small performance difference is important, don't take my word for this - benchmark both solutions with read data).
It's so easy: When the loop is finished, you still got the last element!!
if (!empty($arr)) {
foreach ($arr as $item) {
; // Do something with $item
}
// Here you still got last $item
echo var_export($item, true);
}
I'm trying to loop through a PHP array but I only ever get back my original data. I think it has something to do with when I break the loop
$newData = $this->seperateKeyValuePairs($data);
private function seperateKeyValuePairs($array)
{
foreach($array as $key => $item)
{
if( is_array($item) ) $this->seperateKeyValuePairs($item);
if( is_string($key) && $this->stringStartsWith($key, 'is_') ) {
$item = $this->makeBoolean($item);
}
}
return $array;
}
I think the problem is on this line:
$item = $this->makeBoolean($item);
You change the value of item. Item is not a pointer to the value in the array, but a copy of it, so the value in the array remains unchanged. What you want to do instead is this:
$array[$key] = $this->makeBoolean($item);
In the same spirit, you have to change
if( is_array($item) ) $this->seperateKeyValuePairs($item);
to
if( is_array($item) ) $array[$key] = $this->seperateKeyValuePairs($item);
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().
I have a database with some questions and I want every time the page is opened, not refreshed to show them in different order.
The shuffling, it's ok :
function shuffle_keys( &$array ) {
$keys = array_keys($array);
shuffle($keys);
foreach($keys as $key) {
$new[$key] = $array[$key];
}
$array = $new;
}
Shuffle the array with values from database and printing it:
shuffle_keys($array_questions);
foreach( $array_questions as $key => $val ) {
$key_value = ++$key;
echo "<a href = '?id=$val'>".$key_value."</a> ";
}
Just now, when I refresh every time the shuffling is different I want it this way only when I first open the page.
If you want to have the same shuffling for the same session, (that's what I understood)
You can use a $_SESSION variable to store your array.
session_start();
if (isset($_SESSION['array_questions']))
$array_questions=$_SESSION['array_questions'];
else
{
shuffle_keys($array_questions);
foreach( $array_questions as $key => $val ) {
$key_value = ++$key;
echo "<a href = '?id=$val'>".$key_value."</a> ";
}
$_SESSION['array_questions']=$array_questions;
}
You cannot detect a page refresh on its own. Perhaps consider setting a cookie and asserting whether it exists on page open?
You can use a static variable for the purpose. Just create a class as below:
class Base {
protected static $variable = 0;
}
class child extends Base {
function set() {
self::$variable = 1; // let say 1 = 'sorted'
}
function show() {
echo(self::$variable);
}
}
Now when you enter your site,
Create an object and set the variable, calling the method
$c1 = new Child();
if($c1->show() == 0)
{
// sort your array here and set the static flag to sorted(1)
$c1->set();
}
Hope this help...
I have the following code:
if ($_POST['submit'] == "Next") {
foreach($_POST['info'] as $key => $value) {
echo $value;
}
}
How do I get the foreach function to start from the 2nd key in the array?
For reasonably small arrays, use array_slice to create a second one:
foreach(array_slice($_POST['info'],1) as $key=>$value)
{
echo $value;
}
foreach(array_slice($_POST['info'], 1) as $key=>$value) {
echo $value;
}
Alternatively if you don't want to copy the array you could just do:
$isFirst = true;
foreach($_POST['info'] as $key=>$value) {
if ($isFirst) {
$isFirst = false;
continue;
}
echo $value;
}
Couldn't you just unset the array...
So if I had an array where I didn't want the first instance,
I could just:
unset($array[0]);
and that would remove the instance from the array.
If you were working with a normal array, I'd say to use something like
foreach (array_slice($ome_array, 1) as $k => $v {...
but, since you're looking at a user request, you don't have any real guarantees on the order in which the arguments might be returned - some browser/proxy might change its behavior or you might simply decide to modify your form in the future. Either way, it's in your best interest to ignore the ordering of the array and treat POST values as an unordered hash map, leaving you with two options :
copy the array and unset the key you want to ignore
loop through the whole array and continue when seeing the key you wish to ignore
in loop:
if ($key == 0) //or whatever
continue;
Alternative way is to use array pointers:
reset($_POST['info']); //set pointer to zero
while ($value=next($_POST['info']) //ponter+1, return value
{
echo key($_POST['info']).":".$value."\n";
}
If you're willing to throw the first element away, you can use array_shift(). However, this is slow on a huge array. A faster operation would be
reset($a);
unset(key($a));
On a array filled with 1000 elements the difference is quite minimal.
Test:
<?php
function slice($a)
{
foreach(array_slice($a, 1) as $key)
{
}
return true;
}
function skip($a)
{
$first = false;
foreach($a as $key)
{
if($first)
{
$first = false;
continue;
}
}
return true;
}
$array = array_fill(0, 1000, 'test');
$t1 = time() + microtime(true);
for ($i = 0; $i < 1000; $i++)
{
slice($array);
}
var_dump((time() + microtime(true)) - $t1);
echo '<hr />';
$t2 = time() + microtime(true);
for ($i = 0; $i < 1000; $i++)
{
skip($array);
}
var_dump((time() + microtime(true)) - $t2);
?>
Output:
float(0.23605012893677)
float(0.24102783203125)
Working Code From My Website For Skipping The First Result and Then Continue.
<?php
$counter = 0;
foreach ($categoriest as $category) { if ($counter++ == 0) continue; ?>
It is working on opencart also in tpl file do like this in case you need.
foreach($_POST['info'] as $key=>$value) {
if ($key == 0) { //or what ever the first key you're using is
continue;
} else {
echo $value;
}
}
if you structure your form differently
<input type='text' name='quiz[first]' value=""/>
<input type='text' name='quiz[second]' value=""/>
...then in your PHP
if( isset($_POST['quiz']) AND
is_array($_POST['quiz'])) {
//...and we'll skip $_POST['quiz']['first']
foreach($_POST['quiz'] as $key => $val){
if($key == "first") continue;
print $val;
}
}
...you can now just loop over that particular structure and access rest normally
How about something like this? Read off the first key and value using key() and current(), then array_shift() to dequeue the front element from the array (EDIT: Don't use array_shift(), it renumbers any numerical indices in the array, which you don't always want!).
<?php
$arr = array(
'one' => "ONE!!",
'two' => "TWO!!",
'three' => "TREE",
4 => "Fourth element",
99 => "We skipped a few here.."
) ;
$firstKey = key( $arr ) ;
$firstVal = current( $arr ) ;
echo( "OK, first values are $firstKey, $firstVal" ) ;
####array_shift( $arr ) ; #'dequeue' front element # BAD! renumbers!
unset( $arr[ $firstKey ] ) ; # BETTER!
echo( "Now for the rest of them" ) ;
foreach( $arr as $key=>$val )
{
echo( "$key => $val" ) ;
}
?>