empty() not a valid callback? - php

I'm trying to use empty() in array mapping in php. I'm getting errors that it's not a valid callback.
$ cat test.php
<?
$arrays = array(
'arrEmpty' => array(
'','',''
),
);
foreach ( $arrays as $key => $array ) {
echo $key . "\n";
echo array_reduce( $array, "empty" );
var_dump( array_map("empty", $array) );
echo "\n\n";
}
$ php test.php
arrEmpty
Warning: array_reduce(): The second argument, 'empty', should be a valid callback in /var/www/authentication_class/test.php on line 12
Warning: array_map(): The first argument, 'empty', should be either NULL or a valid callback in /var/www/authentication_class/test.php on line 13
NULL
Shouldn't this work?
Long story: I'm trying to be (too?) clever and checking that all array values are not empty strings.

It's because empty is a language construct and not a function. From the manual on empty():
Note: Because this is a language construct and not a function, it cannot be called using variable functions

Try array_filter with no callback instead:
If no callback is supplied, all entries of input equal to FALSE (see converting to boolean) will be removed.
You can then use count(array_filter($array)) to see if it still has values.
Or simply wrap empty into a callable, like this:
array_reduce($array, create_function('$x', 'return empty($x);'));
or as of PHP 5.3
array_reduce($array, function($x) { return empty($x); });

To add to the others, it's common for PHP developers to create a function like this:
function isEmpty($var)
{
return empty($var);
}

Empty can't be used as a callback, it needs to operate on a variable. From the manual:
Note: empty() only checks variables as anything else will result in a parse error. In other words, the following will not work: empty(trim($name)).

I don't know why, somehow empty() worked for me inside a callback.
The reason why I was originally getting this error was because I was trying to callback as an independent function, whereas it was inside my class and I had to call it using array(&$this ,'func_name')
See code below. It works for me. I am php 5.2.8, if that matters...
$table_values = array_filter( $table_values, array(&$this, 'remove_blank_rows') );
function remove_blank_rows($row){
$not_blank = false;
foreach($row as $col){
$cell_value = trim($col);
if(!empty( $cell_value )) {
$not_blank = true;
break;
}
}
return $not_blank;
}

Related

Using array_map with multi-argument function?

I am building a recursive function, which executes any function for any-deep-nested-array. For example, I want to STRIPSLASH all array values in this:
function RECURSER($array,$function_name){
return is_array($array) ? array_map('RECURSER', $array, $function_name) : $function_name($array);
}
but when I execute:
recursive_for_array_value( $MY_ARRAY, 'stripslashes')
the above function can't send second parameter to array_map.
Now I'll start off by saying that I'd probably never use this in any real projects, but this is an interesting challenge/question and T.Todua's answer works but using $GLOBALS can be avoided.
My position is that array_walk_recursive() is a better suited function versus recursively calling array_map() -- after all, array_walk_recursive() was specifically designed to visit leaf nodes and avoid the tedium of checking the current item's type as "array". use() is effective in passing the function string into the recursive function's scope.
*Note: You could only pass the function string as a string in a SUPER fringe case where the function prints to screen AND requires two arguments -- the first arg being the element value and the second arg being the element key.
Because you want to only process the element values AND modify them by reference, &$v is necessary.
Here is a relevant post to read regarding checking the dynamic function name: What exactly is the difference between the is_callable and function_exists in PHP?
Here is my working alternative:
Code: (Demo)
$multidim_array = ['a' => [' \one ', ['b' => 'two\\', [['c' => 'thr\ee']]]]];
$func = 'stripslashes';
if (function_exists($func)) {
array_walk_recursive($multidim_array, function(&$v)use($func){$v = $func($v);});
var_export($multidim_array);
} else {
echo "not callable";
}
If you wanted to go down this rabbit hole further, you could extend its potential utility by setting up the option to pass multiple arguments:
Code: (Demo)
$func = 'strpos';
if (function_exists($func)) {
$more = true;
$param2 = 'o';
array_walk_recursive($multidim_array, function(&$v)use($func, $more, $param2) {
if ($more) {
$v = $func($v, $param2);
} else {
$v = $func($v);
}
});
var_export($multidim_array);
} else {
echo "um... I'm not calling $func";
}
Finally, the approach that I whole-heartedly do NOT endorse is the use of eval() -- because you can see the tail, horns, and pitchfork a mile away.
Caution
The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged. If you have carefully verified that there is no other option than to use this construct, pay special attention not to pass any user provided data into it without properly validating it beforehand.
This works, but really should not be entertained:
if (function_exists($func)) {
array_walk_recursive($multidim_array, function(&$v)use($func) {eval("\$v = $func(\$v);"); });
var_export($multidim_array);
} else {
echo "um... I'm not calling $func";
}
RECURSOR of any function:
$result= Recursiver_of_Array($array, 'stripslashes');
code:
function Recursiver_of_Array($array,$function_name=false){
if ($function_name) { $GLOBALS['current_func_name']= $function_name; }
return is_array($array) ? array_map('Recursiver_of_Array', $array) : $GLOBALS['current_func_name']($array);
}
array_map accepts one function and multiple arrays as arguments. Perhaps you need to recursively call recurser via an anonymous function instead.
function RECURSER($array,$function_name){
if (is_array($array))
return array_map(function ($element) use ($function_name) {
return RECURSER($element,$function_name);
},$array);
return $function_name($array);
}
The usecase of stripslashes as a one-line PHP function can be written as :
array_walk_recursive($array, function (&$value) { $value = stripslashes($value); });

Looking for function similar to array_map but which the same arg each time to the callback

As someone who is learning PHP I was experimenting with the arrap_map function. I was hoping that it would pass the same 3rd arg each time through to the called function. As below, this is not the behaviour of array_map. Is there an alternative function I can use to achieve this?
$arr = [['a'], ['b'], ['c']];
$args = ['set'];
function mapper($item, $arg){
return $item[] = $arg;
}
$result = array_map('mapper', $arr, $args);
only the first element has 'set' as a value
$arr = [['a'], ['b'], ['c']];
$args = ['set', 'set', 'set'];
function mapper($item, $arg){
return $item[] = $arg;
}
$result = array_map('mapper', $arr, $args);
all three elements have 'set' as a value
Your code is incorrect, $a[$b] doesn't make any sense. Both variables are strings.
Your output also doesn't make sense, quoting from the manual:
If more than one argument is passed then the returned array always has
integer keys.
To answer your question, it's a language design choice.
It could
pass NULL for missing elements (that was PHP does).
throw an error if the inputs don't have the same size.
cycle the smaller inputs.
All these have valid applications and their own problems.

PHP Array Search - key => string

I got some trouble with in_array()
$list = array(
"files/" => "/system/application/files/_index.php",
"misc/chat/" => "/system/application/misc/chat/_index.php"
);
I have this $_GET['f'] which holds the string files/.
How can I search through the array for possible matches?
If the string is found in the array, then the file should be included
Thanks for any help :)
It's really simple. All you need to do is check if the array element is set. The language construct that's usually used is isset() (yes, it's that obvious)...
if (isset($list[$_GET['f']])) {
}
There's no need to call a function for this, isset is cleaner and easier to read (IMHO)...
Note that isset is not actually a function. It's a language construct. That has a few implications:
You can't use isset on the return from a function (isset(foo()) won't work). It will only work on a variable (or a composition of variables such as array accessing or object accessing).
It doesn't have the overhead of a function call, so it's always fast. The overall overhead of a function call is a micro-optimization to worry about, but it's worth mentioning if you're in a tight loop, it can add up.
You can't call isset as a variable function. This won't work:
$func = 'isset';
$func($var);
array_key_exists is a function that returns true of the supplied key is in the array.
if(array_key_exists( $_GET['f'], $list )) {
echo $list[$_GET['f']];
}
You can use in_array() in conjunction with array_keys():
if (in_array($_GET['f'], array_keys($list))) {
// it's in the array
}
array_keys() returns an array of the keys from its input array. Using $list as input, it would produce:
array("files/", "misc/chat/");
Then you use in_array() to search the output from array_keys().
Use array_key_exists.
if(array_key_exists($_GET['f'], $list)){
// Do something with $list[$_GET['f']];
}
in_array() searches array for key using loose comparison unless strict is set.
it's like below.
foreach ($array as $value){
if ($key == $value){
return true;
}
}
My way.
function include_in_array($key, $array)
{
foreach($array as $value){
if ( strpos($value, $key) !== false ){
return false;
}
}
}

Function to set default value of associative array if the key is not present

Is there a function in PHP to set default value of a variable if it is not set ?
Some inbuilt function to replace something like:
$myFruit = isset($_REQUEST['myfruit']) ? $_REQUEST['myfruit'] : "apple" ;
PHP kind of has an operator for this (since 5.3 I think) which would compress your example to:
$myFruit = $_REQUEST['myfruit'] ?: "apple";
However, I say "kind of" because it only tests if the first operand evaluates to false, and won't suppress notices if it isn't set. So if (as in your example) it might not be set then your original code is best.
The function analogous to dictionary.get is trivial:
function dget($dict, $key, $default) {
return isset($dict[$key]) ? $dict[$key] : $default;
}
For clarity, I'd still use your original code.
Edit: The userland implementation #2 of ifsetor() at http://wiki.php.net/rfc/ifsetor is a bit neater than the above function and works with non-arrays too, but has the same caveat that the default expression will always be evaluated even if it's not used:
function ifsetor(&$variable, $default = null) {
if (isset($variable)) {
$tmp = $variable;
} else {
$tmp = $default;
}
return $tmp;
}
As far as i know there exists nothing like this in PHP.
You may implement something like this yourself like
$myVar = "Using a variable as a default value!";
function myFunction($myArgument=null) {
if($myArgument===null)
$myArgument = $GLOBALS["myVar"];
echo $myArgument;
}
// Outputs "Hello World!":
myFunction("Hello World!");
// Outputs "Using a variable as a default value!":
myFunction();
// Outputs the same again:
myFunction(null);
// Outputs "Changing the variable affects the function!":
$myVar = "Changing the variable affects the function!";
myFunction();
You could also create a class implementing the ArrayAccess, which you pass 2 arrays during construction ($_REQUEST and an array with defaults) and make it choose the default value transparently.
Btw., relying on $_REQUEST is not a wise idea. See the manual on $_REQUEST for further information.
Instead of testing, if a key not exists and then return a default value, you can also fill your array with this values, before accessing it.
$expectedKeys = array('myfruit');
$requestData = array_merge (
array_combine(
$expectedKeys,
array_fill(0, count($expectedKeys), null)),
$_REQUEST);
$postData is now an array with all keys you expect (specified by $expectedKeys), but any entry, that is missing in $_REQUEST is null.
$myFruit = $requestData['myfruit'];
if (is_null($myFruit)) {
// Value not exists
}
But I also recommend to just stay with the ternary operator ?:.
There is a function called ife() in the CakePHP framework, you can find it here http://api13.cakephp.org/view_source/basics.php/, it is the last function!
You can use it like this:
echo ife($variable, $variable, 'default');

Is it possible to pass parameters by reference using call_user_func_array()?

When using call_user_func_array() I want to pass a parameter by reference. How would I do this. For example
function toBeCalled( &$parameter ) {
//...Do Something...
}
$changingVar = 'passThis';
$parameters = array( $changingVar );
call_user_func_array( 'toBeCalled', $parameters );
To pass by reference using call_user_func_array(), the parameter in the array must be a reference - it does not depend on the function definition whether or not it is passed by reference. For example, this would work:
function toBeCalled( &$parameter ) {
//...Do Something...
}
$changingVar = 'passThis';
$parameters = array( &$changingVar );
call_user_func_array( 'toBeCalled', $parameters );
See the notes on the call_user_func_array() function documentation for more information.
Directly, it may be impossible -- however, if you have control both over the function you are implementing and of the code that calls it - then there is one work-around that you might find suitable.
Would you be okay with having to embed the variable in question into an object? The code would look (somewhat) like this if you did so.
function toBeCalled( $par_ref ) {
$parameter = $par_ref->parameter;
//...Do Something...
$par_ref->parameter = $parameter;
}
$changingVar = 'passThis';
$parembed = new stdClass; // Creates an empty object
$parembed->parameter = array( $changingVar );
call_user_func_array( 'toBeCalled', $parembed );
You see, an object variable in PHP is merely a reference to the contents of the object --- so if you pass an object to a function, any changes that the function makes to the content of the object will be reflected in what the calling function has access to as well.
Just make sure that the calling function never does an assignment to the object variable itself - or that will cause the function to, basically, lose the reference. Any assignment statement the function makes must be strictly to the contents of the object.
This works by double referencing,the original variable is modified when the $parameter variable is modified.
$a = 2;
$a = toBeCalled($a);
echo $a //50
function toBeCalled( &$par_ref ) {
$parameter = &$par_ref;
$parameter = $parameter*25;
}
Except you are using deprecated functionality here. You'll generate a warning in PHP5 making it less than perfect.
Warning: Call-time pass-by-reference has been deprecated; If you would like to pass it by reference, modify the declaration of runtime function name. If you would like to enable call-time pass-by-reference, you can set allow_call_time_pass_reference to true in your INI file in ...
Unfortunately, there doesn't appear to be any other option as far as I can discover.

Categories