Break array_walk from anonymous function - php

Is there a way to stop an array_walk from inside the anonymous function ?
Here is some sample code (that works) to show what I mean, that checks if an array has only numeric values.
$valid = true;
array_walk($parent, function ($value) use (&$valid) {
if (!is_numeric($value)) {
$valid = false;
}
});
return $valid ? 'Valid' : 'Invalid';
If I have a big enough array, and the first entry is invalid, the rest of the (redundant) checks are still done, so I would like to stop the execution.
Using break / continue doesn't work (error: Fatal error: Cannot break/continue 1 level in ...).
Note: I don't want to rewrite the code, I just want to know IF this is possible.

As stated, theoretically it's possible but I'd advise against it. Here's how to use an Exception to break out of the array_walk.
<?php
$isValid = false;
$array = range(1, 5);
try {
array_walk($array, function($value) {
$isAMagicNumber = 3 === $value;
if ($isAMagicNumber) {
throw new Exception;
}
});
}catch(Exception $exception) {
$isValid = true;
}
var_dump($isValid);
/*
bool(true)
*/

You can put a static flag inside the anonymous function:
array_walk($ary, function($item) {
static $done = false;
if($done) {
return;
}
// … your code
if($myBreakCondition) {
$done = true;
return;
}
});
This doesn’t actually stop the iteration, but all further cycles after the flag is set simply do nothing. Not very efficient, but it might work without any greater performance impact if the arrays iterated are not too large.
In your case, the code would be:
$valid = true;
array_walk($parent, function($value) use(&$valid) {
static $done = false;
if($done) {
return;
}
if(!is_numeric($value)) {
$valid = false;
$done = true;
return;
}
});
return $valid ? 'Valid' : 'Invalid';
But actually it won’t be much difference if there was no “break” at all. Only the “false” would be assigned for every invalid value, which does not matter as the result would be still false. Maybe it would be even more efficient that my static variable cheat.
Personally in your case I would use array_filter instead:
$valid = count(array_filter($parent, 'is_numeric')) == count($parent);
or just
$valid = array_filter($parent, 'is_numeric')) == $parent;
If all values in the $parent array are numeric, they would be all present after the filtering. On the other hand, any non-numeric value in the array would affect the contents (decreasing the item count) in the filtered array and the comparison would yield false.

Related

php array_filter is filtering too much

Here is the code :
<?php
$a_campagnes = $this->campagne->get_campagnes_client();
foreach($a_campagnes as $o_camp){
if($o_camp->groupes){
foreach($o_camp->groupes as $o_groupe){
if($o_groupe->IDGroupe == $this->session->o_user->IDGroupe){ echo 'ok';}
}
}
}
$a_campagnes = array_filter($a_campagnes, function($o_camp){
if($o_camp->groupes){
foreach($o_camp->groupes as $o_groupe){
if($o_groupe->IDGroupe == $this->session->o_user->IDGroupe) return true;
}
}
return false;
});
$a_campagnes contains at first 10 objects
The result of the first foreach is okokokok
The result of $a_campagnes after the array_filter (which is the same code as the first foreach) is null
Where are the four objects matching my first foreach?
EDIT
Just tried that piece of code:
$i_id_groupe_user = $this->session->o_user->IDGroupe;
foreach($a_campagnes as $o_camp){
if($o_camp->groupes){
foreach($o_camp->groupes as $o_groupe){
if($o_groupe->IDGroupe == $i_id_groupe_user){ echo 'ok';}
}
}
}
$a_campagnes = array_filter($a_campagnes, function($o_camp) use ($i_id_groupe_user){
if($o_camp->groupes){
foreach($o_camp->groupes as $o_groupe){
if($o_groupe->IDGroupe == $i_id_groupe_user) return true;
}
}
return false;
});
It gives the same result as before
$this doesn't exist inside anonymous functions, and you're trying to use it as if it was inside your class scope, which would be even less logical.
If you want to use whatever $this->session is inside your array_filter() callback, you'll have to either declare a class method specifically for that, or tell the anonymous function that it can use it, like this:
$session = $this->session;
$a_campagnes = array_filter($a_campagnes, function($o_camp) use ($session) {
if ($o_camp->groupes) {
foreach($o_camp->groupes as $o_groupe) {
if ($o_groupe->IDGroupe == $session->o_user->IDGroupe) return true;
}
}
return false;
});

use the functions without its parameters

I am using 2 regex functions here and I wanna make another function which returns false when the 2 regex are both false and if not, then true.
The problem here is when I wanna use the 2 regex functions in the third one, I have to give them parameters, which is not necessary I think, because the third function will only return a simple true or false. I get an undefined variable whenever I give parameters to the 2 regex functions in the 3rd one.
I tried using global variables which works but since its a bad practice I am looking for a better solution.
Code:
function regex1($input)
{
$regex= "/^[A-Za-z0-9 ]*$/";
if (!preg_match($regex, $input))
{
return false;
}
else
{
return true;
}
}
function regex2($input)
{
$regex= "/^[A-Za-z0-9 ]*$/";
if (!preg_match($regex, $input))
{
return false;
}
else
{
return true;
}
}
function checkBoth()
{
if (regex1($input) === false || regex2($input) === false)
{
return false;
}
else
{
return true;
}
}
EDIT:
The checkBoth function I am using in my other file like this together with the other 2 regex functions:
if (!regex1($input))
{
// show error at the same time
}
if (!regex2($input))
{
// show error at the same time
}
if(checkBoth())
{
// success
}
function regex2($input,$secondVar=false)
{....
Later in code in place where you need just add:
if($secondVar !== false){
// do whatever...
}
If you can't user "false" you can just empty string '' or any other value that will not appear there.

How to check (strpos(...)) all elements of an array efficiently?

I want to check all elements of an array and find out, whether at least one of them is prefixed by a given string:
public function validateStringByPrefix(string $string, $prefix)
{
$valid = false;
if (is_string($prefix)) {
if (strpos($string, $prefix) === 0) {
$valid = true;
}
} elseif (is_array($prefix)) {
foreach ($prefix as $partPrefix) {
if (strpos($string, $partPrefix) === 0) {
$valid = true;
break;
}
}
}
return $valid;
}
Is it possible / How to to achieve the same a more efficient way?
(It's a cheap method, but it's called a lot of times in my application, so even a minimal improvement might appreciably increase the application's performance.)
You can try next solution:
public function validateStringByPrefix(string $string, $prefix)
{
return (bool)array_filter((array)$prefix, function($prefix) use ($string) {
return strpos($string, $prefix)===0;
});
}
P.S. In case you have few large arrays (with prefixes), my solution is less efficient and you can combine our approaches like this:
public function validateStringByPrefix(string $string, $prefix)
{
if($string=='') {
return false;
}
foreach ((array)$prefix AS $subprefix) {
if (strpos($string, $subprefix)===0) {
return true;
}
}
return false;
}
There are many ways to rome....
//your array to test for
$array=[];
//set valid to false
$valid=false;
//setup prefixes array or not
$prefix='whatever';
//make array if you dont have one
!is_array($prefix) AND $prefix=array($prefix);
//prepare for use as REGEX
$prefix=implode('|',$prefix);
//do the work
array_walk($array,function($val,$key) use(&$valid,$prefix){
if (!$valid && preg_match("#^($prefix)#",$key)) {
$valid = true;
}
});
var_export($valid);
I used preg_match here, because $prefix can be an array, so the math would be: n+ strpos() calls vs. one preg_match() call
And after a single item matches, no more preg_match are called, just iteration to the end and out.

How to return control from callback function or break the processing of array in middle array_filter processing

Can we break execution of callback once condition satisfied with one element of array?
ex .
$a = array(1,2,3,4,5);
foreach($a as $val){
if ($val == 3){
break;
}
}
if we write call back for it, it will be as below
$result = array_filter($a, function(){
if ($val == 3){
return true;
}
});
In callback it will go through all array element, in spite of condition is being satisfied at 3. rest two elements 4, 5 will also go through callback
I want such function in callback, which will break callback one desired condition match and stop execution of rest of elements
Is is possible?
You can do that with a static variable. A static variable is of local scope inside the callback function but preserves its value between calls.
It behaves like a global variable in terms of its value, but with a local scope:
$callback = function($val)
{
static $filter = false;
if ($val == 3) {
$filter = true;
}
return $filter;
};
This callback will return false until $val == 3. It then will return true.
I dont think you can achieve this with array_filter, but you can do something like this:
$a = array(1,2,3,4,5);
try {
array_walk($a, function($value, $key) use(&$a) {
if ($value == 3){
throw new Exception("condition match");
}
unset($a[$key]);
});
}
catch(Exception $e) { }
var_dump($a);

Combine three "complex" PHP conditions in one perfect php snippet

I'm stuck in Drupal Panels / PHP Access plugins.
At least, now I found the three conditions to create my final snippet. the purpose of it is to return TRUE; if "condition1 is TRUE" OR "condition2 is TRUE" OR "condition3 is TRUE". I found a lot of similar questions, but the last condition force me to post here to find the right way to do this.
Condition 1:
// At least $view1->result has result.
$view1 = views_get_view('sp_onglet_videos');
$view1->set_display('views-tab-embed_1');
$output1 = $view1->preview();
if ($view1->result) {
return TRUE;
}
Condition 2 (same thing):
// At least $view2->result has result.
$view2 = views_get_view('sp_onglet_audio');
$view2->set_display('views-tab-default');
$output2 = $view2->preview();
if ($view2->result) {
return TRUE;
}
Condition 3 is more complex:
// Checks for content in the field field_txt_videos.
if (isset($contexts['argument_nid_1']->data-> field_txt_videos)) {
$field = $contexts['argument_nid_1']->data-> field_txt_videos;
if (is_null($field)) {
return FALSE;
}
if (is_array($field)) {
foreach ($field as $key => $val) {
if (is_array($val)) {
$field[$key] = array_filter($val);
}
}
$field = array_filter($field);
return count($field);
}
if (is_string($field) && trim($field) == '') {
return FALSE;
}
if ($field) {
return TRUE;
}
return FALSE;
}
I would like to have something clean (and functional) like this:
if ($view1->result && $view2->result && $field) {
return TRUE;
}
But it's to tricky for my php knowledge. Need a little help !
You want to save the result of the 3rd condition (into a variable) and use this result to run your final condition/query. But you can query the 3rd condition if it is a function.
It is better to properly space your code and use plenty of newlines.
However, PHP does have some pretty cool tricks to do assignment inside conditional statements.
if(($view1 = views_get_view('sp_onglet_videos')) AND $view1->set_display('views-tab-embed_1') AND ($output1 = $view1->preview()) AND $view1->result) return TRUE;
However, as you can see this code is a mess - don't do it unless your assignment is really small. Take this simple security check at the top of a PHP file:
<?php defined('BASE_PATH') OR die('Not Allowed');

Categories