How to iterate over function arguments and modify values if null - php

I want to convert all parameters to empty string when NULL is passed. Something like this, but the original value of the parameters is not changed in my code.
function loopThroughArgs($a, $b) {
$args = func_get_args();
foreach ($args as &$arg) {
$arg = $arg === null ? "" : $arg;
}
var_dump($a); // should output an empty string
var_dump($b); // should output an empty string
}
$a = null;
$b = null;
loopThroughArgs($a, $b);

Since func_get_args() returns a copy of those arguments, it won't automatically change your function parameter variables.
You can use get_defined_vars() to reassign values. Loop over all declared variables and assign values to each of them from your $args. If the variable name would have been something different apart from args, that name will come in your if condition.
<?php
function loopThroughArgs($a, $b) {
$vars = get_defined_vars();
$args = func_get_args();
foreach ($args as &$arg) {
$arg = $arg === null ? "" : $arg;
}
$ptr = 0;
foreach($vars as $key => $val){
${$key} = $args[$ptr++];
}
var_dump($a); // should output an empty string
var_dump($b); // should output an empty string
}
$a = null;
$b = null;
loopThroughArgs($a, $b);
Online Demo

Related

PHP is there a true() function?

I'm writing a function named all to check all elements inside an array $arr, returning a single boolean value (based of $f return value). This is working fine passing custom functions (see the code with $gte0 been passed to all).
However sometimes one want just check that an array contains all true values: all(true, $arr) will not work becase true is passed as boolean (and true is not a function name). Does PHP have a native true() like function?
function all($f, array $arr)
{
return empty($arr) ? false : array_reduce($arr, function($v1, $v2) use ($f) {
return $f($v1) && $f($v2);
}, true);
}
$test = array(1, 6, 2);
$gte0 = function($v) { return $v >= 0; }
var_dump(all($gte0, $test)); // True
$test = array(true, true, false);
$id = function($v) { return $v; } // <-- this is what i would avoid
var_dump(all($id, $test)); // False
all(true, $test); // NOT WORKING because true is passed as boolean
all('true', $test); // NOT WORKING because true is not a function
EDIT: another way could be checking $f in all function:
$f = is_bool($f) ? ($f ? function($v) { return $v; }
: function($v) { return !$v; } ): $f;
After adding this, calling all with true is perfectly fine.
intval might do what you're looking for (especially as the array only contains integers in your example):
var_dump(all('intval', $test)); // False
However, many types to integer conversions are undefined in PHP and with float this will round towards zero, so this might not be what you want.
The more correct "function" would be the opposite of boolean true: empty, but it's not a function, so you can't use it (and invert the return value):
var_dump(!all('empty', $test)); // Does not work!
And there is no function called boolval or similar in PHP, so write it yourself if you need it ;)
Additionally your all function could be optimized. While iterating, if the current result is already FALSE, the end result will always be FALSE. And no need to call $f() n * 2 times anyway:
function all($f, array $arr)
{
$result = (bool) $arr;
foreach($arr as $v) if (!$f($v)) return FALSE;
return $result;
}
Edit: knittl is right pointing to array_filter, it converts to boolean with no function given which seems cool as there is no "boolval" function:
function all($f, array $arr)
{
return ($c = count($arr))
&& ($f ? $arr = array_map($f, $arr) : 1)
&& $c === count(array_filter($arr));
}
var_dump(all(0, $test)); // False
Making the first function parameter optional will do you a proper bool cast on each array element thanks to array_filter.
Better to pass in a function to map the values to booleans that you can then reduce to a final value.
function all($map, $data) {
if (empty($data)) { return false; }
$reduce = function($f, $n) {
return $f && $n;
};
return array_reduce(array_map($map, $data), $reduce, true);
}
$gte0 = function($v) { return $v >= 0; };
$gte2 = function($v) { return $v >= 2; };
$data = array(1, 2, 3, 4, 5);
var_dump(all($gte0, $data));
var_dump(all($gte2, $data));
Then the result of the function remains expectant but the test can be slotted in as needed. You can go a step further and allow both the map and reduce function to be passed in.
function mr($map, $reduce, $data) {
return array_reduce(array_map($map, $data), $reduce, true);
}
You could use PHP's array_filter function, it will remove all 'falsy' values from an array if no callback is specified:
$a = array ( true, true, false );
var_dump($a == array_filter($a));
There isn't any true() function in PHP, you should compare the value to true.
try
return ($f === $v1) && ($f === $v2);
instead of
return $f($v1) && $f($v2);
I think this should work:
function all($f, array $arr)
{
return empty($arr) ? false : array_reduce($arr, function($v1, $v2) use ($f) {
return $f($v1) && $f($v2);
}, true);
}
function isTrue($a)
{
return true === $a;
}
all("isTrue", $test);

PHP, isset and how to get rid of repetitive chains of variables?

I have strict error reporting. I have to use isset and it make me to write long, repetitive chains of variables in PHP. I have sometimes to write code like this:
if (isset($my_object->an_array[$a_variable])):
$other_variable = $my_object->an_array[$a_variable];
else:
$other_variable = false;
endif;
or
if (isset($my_object->an_array[$a_variable])):
return $my_object->an_array[$a_variable];
endif;
Sometimes it is longer and more complicated. It isn't readable and take too much time to type. I'd like to get rid of it.
The question
Is there a way to write $my_object->an_array[$a_variable] only once?
You can write functions to encapsulate repetitive code:
function get_variable(array $array, $variable_name, $default_value=FALSE){
if( isset($array[$variable_name]) ){
return $array[$variable_name];
}else{
return $default_value;
}
}
Tweak to your needs.
In the end I have found two solutions.
I. There is operator # in PHP. It is very dangerous, tough.
http://www.php.net/manual/en/language.operators.errorcontrol.php
However, it is acceptable in my situation.
This is not a fatal error.
The value of undefined variable is defined as null. I'm fine with testing for this or using implicit conversions.
I can use $php_errormsg in extreme situations.
The code example:
$tmp = $false_object->property->property; #Throw notice
$tmp = $false_array['a_field']['a_field']; #Throw notice
$tmp = #$false_object->property->property; #Quiet
$tmp = #$false_array['a_field']['a_field']; #Quiet
echo $php_errormsg; #I can print that notice
The downside is I don't receive information about lack of quotes in brackets.
$a = array('e'=>false);
$tmp = $a[e]; #Throw notice
$tmp = #$a[e]; #Quiet
echo $php_errormsg; #This variable still works
II. It is possible to use operator &.
The value of undefined variable will be NULL too.
The $php_errormsg variable doesn't work for undefined variables.
I get notice for lack of quotes in brackets, though.
The code example:
$tmp = $false_object->property->property; #Throw notice
$tmp = $false_array['a_field']['a_field']; #Throw notice
$tmp = &$false_object->property->property; #Quiet
$tmp = &$false_array['a_field']['a_field']; #Quiet
var_dump($tmp); #NULL;
The lack of quotes problem:
$array = array('a_field'=>true);
$tmp = $array[a_field]; #Throw notice
$tmp = #$array[a_field]; #Quiet
$tmp = &$array[a_field]; #Throw notice
function check($var)
{
if(isset[$var])
return $var;
else
return "";
}
Then each time you need to do checking call like:
$other_b = check($b);
I doubt you will get any suggestions that you will consider satisfactory. The best I can suggest is this, and I would add that I consider it quite ugly:
function ifset ($var) {
return is_null($var) ? false : $var;
}
Having defined this function, you can call it like this:
$other_variable = ifset(#$my_object->an_array[$a_variable]);
Note that you need the error suppression operator here, because otherwise you'll get an undefined variable notice if the variable indeed doesn't exist. (The reason why you don't need it for isset() is that isset() is really a special parser token rather than an ordinary function.)
now i get the same problem. i must check it and then get it ,it's so ugrly.
so i write the function like this
function get_val($arr,$key,$default_val=false){
if(!is_array($arr)) return $default_val;
$idx = explode('>>',$key);
$tmp = $arr;
$catched = true;
foreach($idx as $index) {
if(!isset($tmp[$index])){
$catched = false;
break;
}else{
$tmp = $tmp[$index];
}
}
if($catched) $default_val = $tmp;
return $default_val;
}
//for example
$arr = array('k1'=>array('k2'=>array(1,'k22'=>22,'k23'=>array('k3'=>1))));
get_val($arr,'k1>>k2>>k23>>k3');
A method to extract those variables would probably be better in your case, then:
class MyObject
{
private $an_array;
public function __construct()
{
$this->an_array = array();
}
public function get( $key )
{
if(isset($this->an_array[$key]))
return $this->an_array[$key];
return false; //or empty string
}
public function set( $key, $value )
{
$this->an_array[$key] = $value;
}
}
That way, you can do it like this:
$my_object->get($a_variable]);
I use these little helper functions to access properties of (multidimensional) arrays/objects without writing repetitive isset() statements. They might not be the fastest running solution, but they are very comfortable:
// AI(data,1,default) === data[1] or default
function AI( $a, $i, $d=null ){
if( is_array ($a)){ if( array_key_exists( $i, $a )) return $a[ $i ]; return $d; }
if( is_object($a)){ if( property_exists( $a, $i )) return $a -> $i; return $d; }
return $d;
}
// AII(data,1,2,3) === data[1][2][3] or null
function AII( $o ){
$a = func_get_args();
$al = func_num_args();
for( $i=1; $i < $al; $i++ ){
$k = $a[$i];
if( is_array ($o) && array_key_exists($k,$o)) $o =& $o[ $k ];
else if( is_object($o) && property_exists ($o,$k)) $o =& $o -> $k;
else return null; // nothing to access
}
return $o;
}
// AIID(data,1,2,3,default) == data[1][2][3] or default
function AIID( $o ){
$a = func_get_args();
$default = end( $a );
$al = count( $a ) - 1;
for( $i=1; $i < $al; $i++ ){
$k = $a[$i];
if( is_array ($o) && array_key_exists($k,$o)) $o =& $o[ $k ];
else if( is_object($o) && property_exists ($o,$k)) $o =& $o -> $k;
else return $default;
}
return $o;
}
// AAID(data,[1,2,3],default) == data[1][2][3] or default
function AAID( $o, $a, $default = null ){
foreach( $a as $k ){
if( is_array ($o) && array_key_exists($k,$o)) $o =& $o[ $k ];
else if( is_object($o) && property_exists ($o,$k)) $o =& $o -> $k;
else return $default;
}
return $o;
}

Convert delimited string into array key path and assign value

I have a string like this:
$string = 'one/two/three/four';
which I turn it into a array:
$keys = explode('/', $string);
This array can have any number of elements, like 1, 2, 5 etc.
How can I assign a certain value to a multidimensional array, but use the $keys I created above to identify the position where to insert?
Like:
$arr['one']['two']['three']['four'] = 'value';
Sorry if the question is confusing, but I don't know how to explain it better
This is kind of non-trivial because you want to nest, but it should go something like:
function insert_using_keys($arr, $keys, $value){
// we're modifying a copy of $arr, but here
// we obtain a reference to it. we move the
// reference in order to set the values.
$a = &$arr;
while( count($keys) > 0 ){
// get next first key
$k = array_shift($keys);
// if $a isn't an array already, make it one
if(!is_array($a)){
$a = array();
}
// move the reference deeper
$a = &$a[$k];
}
$a = $value;
// return a copy of $arr with the value set
return $arr;
}
$string = 'one/two/three/four';
$keys = explode('/', $string);
$arr = array(); // some big array with lots of dimensions
$ref = &$arr;
while ($key = array_shift($keys)) {
$ref = &$ref[$key];
}
$ref = 'value';
What this is doing:
Using a variable, $ref, to keep track of a reference to the current dimension of $arr.
Looping through $keys one at a time, referencing the $key element of the current reference.
Setting the value to the final reference.
You'll need to first make sure the key's exist, then assign the value. Something like this should work (untested):
function addValueByNestedKey(&$array, $keys, $value) {
$branch = &$array;
$key = array_shift($keys);
// add keys, maintaining reference to latest branch:
while(count($keys)) {
$key = array_pop($keys);
if(!array_key_exists($key, $branch) {
$branch[$key] = array();
}
$branch = &$branch[$key];
}
$branch[$key] = $value;
}
// usage:
$arr = array();
$keys = explode('/', 'one/two/three/four');
addValueByNestedKey($arr, $keys, 'value');
it's corny but:
function setValueByArrayKeys($array_keys, &$multi, $value) {
$m = &$multi
foreach ($array_keys as $k){
$m = &$m[$k];
}
$m = $value;
}
$arr['one']['two']['three']['four'] = 'value';
$string = 'one/two/three/four';
$ExpCheck = explode("/", $string);
$CheckVal = $arr;
foreach($ExpCheck AS $eVal){
$CheckVal = $CheckVal[$eVal]??false;
if (!$CheckVal)
break;
}
if ($CheckVal) {
$val =$CheckVal;
}
this will give u your value in array.

change empty array element to a value

i have an array like this
Array ([0]=>'some values'[1]=>'')
I want to change the empty elemnt of an array to a value of 5
how can I do that
thanks
5.3 version
$arr = array_map(function($n) { return !empty($n) ? $n : 5; }, $arr);
If you know at which position it is, just do:
$array[1] = 5;
If not, loop through it and check with === for value and type equality:
foreach($array as &$element) { //get element as a reference so we can change it
if($element === '') { // check for value AND type
$element = 5;
}
}
You can use array_map for this:
function emptyToFive($value) {
return empty($value) ? 5 : $value;
}
$arr = array_map(emptyToFive, $arr);
As of PHP 5.3, you can do:
$func = function($value) {
return empty($value) ? 5 : $value;
};
$arr = array_map($func, $arr);
EDIT: If empty does not fit your requirements, you should perhaps change the condition to $value === '' as per #Felix's suggestion.
This $array[1] = 5;

In PHP, How to Convert an Argument Name into a String

My goal is to echo the argument passed to a function. For example, how can this be done?
$contact_name = 'foo';
function do_something($some_argument){
// echo 'contact_name' .... How???
}
do_something($contact_name);
You can't. If you want to do that, you need to pass the names as well, e.g:
$contact_name = 'foo';
$contact_phone = '555-1234';
function do_something($args = array()) {
foreach ($args as $name => $value) {
echo "$name: $value<br />";
}
}
do_something(compact('contact_name', 'contact_phone'));
Straight off the PHP.net variables page:
<?php
function vname(&$var, $scope=false, $prefix='unique', $suffix='value')
{
if($scope) $vals = $scope;
else $vals = $GLOBALS;
$old = $var;
$var = $new = $prefix.rand().$suffix;
$vname = FALSE;
foreach($vals as $key => $val) {
if($val === $new) $vname = $key;
}
$var = $old;
return $vname;
}
?>
Not possible.
Variables are just means to address values or areas in the memory. You cannot get the variable name that’s value has been passed to a function.
Disclaimer: this will oonly work if you pass a variable to the function, not a value, and it only works when your not in a function or a class. So only the GLOBAL scope works :)
Good funct($var)
Bad funct(1)
You can do it actually contrary to popular believe ^_^. but it involves a few lookup tricks with the $GLOBALS variable.
you do it like so:
$variable_name = "some value, better if its unique";
function funct($var) {
foreach ($GLOBALS as $name => $value) {
if ($value == $var) {
echo $name; // will echo variable_name
break;
}
}
}
this method is not fool proof tho. Because if two variables have the same value, the function will get the name of the first one it finds. Not the one you want :P
Its best to make the variable value unique before hand if you want accuracy on variable names
Another method would be to use reference to be accurate like so
$variable_name = 123;
function funct(&$var) {
$old = $var;
$var = $checksum = md5(time()); // give it unique value
foreach ($GLOBALS as $name => $value) {
if ($value == $var) {
echo $name; // will echo variable_name
$var = $old; // reassign old value
break;
}
}
}
so it is entirely possible :)
Based on PTBNL's (most definately correct) answer i came up with a more readable (at least i think so) approach:
/**
* returns the name of the variable posted as the first parameter.
* If not called from global scope, pass in get_defined_vars() as the second parameter
*
* behind the scenes:
*
* this function only works because we are passing the first argument by reference.
* 1. we store the old value in a known variable
* 2. we overwrite the argument with a known randomized hash value
* 3. we loop through the scope's symbol table until we find the known value
* 4. we restore the arguments original value and
* 5. we return the name of the symbol we found in the table
*/
function variable_name( & $var, array $scope = null )
{
if ( $scope == null )
{
$scope = $GLOBALS;
}
$__variable_name_original_value = $var;
$__variable_name_temporary_value = md5( number_format( microtime( true ), 10, '', '' ).rand() );
$var = $__variable_name_temporary_value;
foreach( $scope as $variable => $value )
{
if ( $value == $__variable_name_temporary_value && $variable != '__variable_name_original_value' )
{
$var = $__variable_name_original_value;
return $variable;
}
}
return null;
}
// prove that it works:
$test = 1;
$hello = 1;
$world = 2;
$foo = 100;
$bar = 10;
$awesome = 1;
function test_from_local_scope()
{
$local_test = 1;
$local_hello = 1;
$local_world = 2;
$local_foo = 100;
$local_bar = 10;
$local_awesome = 1;
return variable_name( $local_awesome, get_defined_vars() );
}
printf( "%s\n", variable_name( $awesome, get_defined_vars() ) ); // will echo 'awesome'
printf( "%s\n", test_from_local_scope() ); // will also echo awesome;
Sander has the right answer, but here is the exact thing I was looking for:
$contact_name = 'foo';
function do_something($args = array(), $another_arg) {
foreach ($args as $name => $value) {
echo $name;
echo '<br>'.$another_arg;
}
}
do_something(compact(contact_name),'bar');
class Someone{
protected $name='';
public function __construct($name){
$this->name=$name;
}
public function doSomthing($arg){
echo "My name is: {$this->name} and I do {$arg}";
}
}
//in main
$Me=new Someone('Itay Moav');
$Me->doSomething('test');

Categories