Seeking 'coalesce' php internal function

php.internals

D. Dante Lorenso

20 years ago
All, I'm sure this has been asked somewhere, but since I see requests for features for 5.2 or 6.0, I'd like to add a "simple" item to the list which would be quite useful to me and would simplify and clean up a lot of code out there: function coalesce(...) This works much like in the SQL version of the same. In SQL, the function returns the first non-null argument from an arbitrary list. In our use, it should return the first non-empty value from the list: Example: $x = "dante"; $y = ""; $z = ""; $value = coalesce($z, $y, $x); // $value = "dante" This function would ideally be built into the language and bypass warnings about undefined variables like 'empty' does. It might be nice to have several varieties of this function: * return first parameter where empty() is FALSE * return first parameter where isset() is TRUE I don't think something like this can NOT be written in userspace because the 'isset' and 'empty' checks need to be run before arguments can be passed to a user function or warnings will start flying. A function like this simplifies code which used to look like this: if (!empty($_POST["postkey"])) { $value = $_POST["postkey"]; } elseif (!empty($_GET["getkey"])) { $value = $_POST["getkey"]; } elseif (!empty($default_value)) { $value = $default_value; } else { $value = "hard coded value"; } Into this: $value = coalesce($_POST["postkey"], $_GET["getkey"], $default_value, "hard coded value"); Can this be built and included? Dante

D. Dante Lorenso

20 years ago
D. Dante Lorenso wrote:
> I don't think something like this can NOT be written in userspace > because the 'isset' and 'empty' checks need to be run before arguments > can be passed to a user function or warnings will start flying. A > function like this simplifies code which used to look like this: > > if (!empty($_POST["postkey"])) { > $value = $_POST["postkey"]; > } > elseif (!empty($_GET["getkey"])) { > $value = $_POST["getkey"]; > } > elseif (!empty($default_value)) { > $value = $default_value; > } > else { > $value = "hard coded value"; > } > > Into this: > > $value = coalesce($_POST["postkey"], $_GET["getkey"], > $default_value, "hard coded value");
I mean "I DON'T think like this CAN be written in userspace". And, yes, there is a bug in that code up above (around $_GET testing), but the 'coalesce' function removes the test/set with identical values so removes those kinds of careless bugs. Dante

Steph

20 years ago
D. Please read the mailing list archives. And don't mention it again ;) - Steph ----- Original Message ----- From: "D. Dante Lorenso" <dante@vocalspace.com> To: <dante@lorenso.com> Cc: <internals@lists.php.net> Sent: Wednesday, May 03, 2006 8:05 AM Subject: Re: [PHP-DEV] Seeking 'coalesce' php internal function

Johannes Schlueter

20 years ago
Hi, please search the archives for "ifsetor". johannes On Wednesday 03 May 2006 07:56, D. Dante Lorenso wrote:

D. Dante Lorenso

20 years ago
Johannes Schlueter wrote:
> please search the archives for "ifsetor". >
I have completed this search and find: http://marc.theaimsgroup.com/?r=1&w=2&q=b&l=php-dev&s=coalesce http://marc.theaimsgroup.com/?l=php-dev&w=2&r=1&s=ifsetor&q=b I am using PHP 5.1.2 currently and thought using pass-by-reference was frowned upon. Assuming a mistaken assumption I move forward and see the following code which was presented to the list: <?php function ifsetor(&$var, $value) { return (isset($var)) ? $var : $value; } print ifsetor($x, "dante"); ?> This works as I'd like in my dev environment, however, this does not solve the problem of allowing variable number of parameters (variables and values). So, I've whipped up this 10 variable version: <?php function ifsetor(&$var1, &$var2, &$var3, &$var4, &$var5, &$var6, &$var7, &$var8, &$var9, &$var10) { if (isset($var1)) return $var1; if (isset($var2)) return $var2; if (isset($var3)) return $var3; if (isset($var4)) return $var4; if (isset($var5)) return $var5; if (isset($var6)) return $var6; if (isset($var7)) return $var7; if (isset($var8)) return $var8; if (isset($var9)) return $var9; if (isset($var10)) return $var10; return false; } print ifsetor($x["notset"]->notset, $y["notset"]->notset, $z->notset, "dante"); ?> PHP Fatal error: Only variables can be passed by reference in .../ifsetor.php on line 15 PHP Warning: Missing argument 5 for ifsetor(), called in .../ifsetor.php on line 15 and defined in .../ifsetor.php on line 2 PHP Warning: Missing argument 6 for ifsetor(), called in .../ifsetor.php on line 15 and defined in .../ifsetor.php on line 2 PHP Warning: Missing argument 7 for ifsetor(), called in .../ifsetor.php on line 15 and defined in .../ifsetor.php on line 2 PHP Warning: Missing argument 8 for ifsetor(), called in .../ifsetor.php on line 15 and defined in .../ifsetor.php on line 2 PHP Warning: Missing argument 9 for ifsetor(), called in .../ifsetor.php on line 15 and defined in .../ifsetor.php on line 2 PHP Warning: Missing argument 10 for ifsetor(), called in .../ifsetor.php on line 15 and defined in .../ifsetor.php on line 2 I can work around passing values by reference by calling the function like this: print ifsetor($x["notset"]->notset, $y["notset"]->notset, $z->notset, $temp = "dante"); But you have to admit that's getting ugly and still, there are undefined parameters declared ... so then I tried it by assigning default values: <?php function ifsetor(&$var1=null, &$var2=null, &$var3=null, &$var4=null, &$var5=null, &$var6=null, &$var7=null, &$var8=null, &$var9=null, &$var10=null) { if (isset($var1)) return $var1; if (isset($var2)) return $var2; if (isset($var3)) return $var3; if (isset($var4)) return $var4; if (isset($var5)) return $var5; if (isset($var6)) return $var6; if (isset($var7)) return $var7; if (isset($var8)) return $var8; if (isset($var9)) return $var9; if (isset($var10)) return $var10; return false; } print ifsetor($x["notset"]->notset, $y["notset"]->notset, $z->notset, $temp = "dante")."\n"; print_r($x); ?> Now, $x, $y, and $z ARE set ... dante Array ( [notset] => stdClass Object ( [notset] => ) ) This is a nasty side-effect and can not be accepted. I did not want to set these variables, just check their existance. I am willing to go away and concede that this should be done in userspace IF you can show me that it is in fact possible. The 2 argument ifsetor is trivial in userspace, yes, but the variable case is more useful in a variety of situations and provides the most benefit to simplify code. I did not find the answer in the archives. Can you point me in the right direction? I need to be able to do all of the following: * variable number of parameters * test 'isset' or 'empty' * testing variables and non-variable values (pass-by-reference won't work on values) * not have side-effect of defining values which are not already set * not trigger notices or warnings * returns value of first proper match Dante

Eric Coleman

20 years ago
http://us3.php.net/func_get_args http://us3.php.net/array Enjoy. Eric Coleman Eric Coleman http://aplosmedia.com home: 412 399 1024 cell: 412 779 5176 On May 3, 2006, at 3:13 AM, D. Dante Lorenso wrote:

D. Dante Lorenso

20 years ago
Eric, This reply is too basic and is not the answer. The problem is more complex then you have grasped. The only way to remove the notice and warning errors is by using pass-by-reference in the userspace function. Pass-by-reference can not be done for literal values and will only work on variables. All notices and warnings have been thrown long before func_get_args can be run. Besides, func_get_args will return an array and then isset() and empty() no longer apply to the original variables. Dante Eric Coleman wrote:

Rick Widmer

20 years ago
D. Dante Lorenso wrote:
> Eric, > > This reply is too basic and is not the answer. The problem is more > complex then you have grasped.
function ifsetor() { $args = func_get_args(); $count = count( $args ); for( $i=0; $i<$count; $i++ ) { if isset( $args[ $i ] )) { return $args[ $i ]; } return false; }

D. Dante Lorenso

20 years ago
Rick Widmer wrote:
> D. Dante Lorenso wrote: >> Eric, >> This reply is too basic and is not the answer. The problem is more >> complex then you have grasped. > function ifsetor() { > $args = func_get_args(); > $count = count( $args ); > > for( $i=0; $i<$count; $i++ ) { > if isset( $args[ $i ] )) { > return $args[ $i ]; > } > > return false; > } >
No, that doesn't address the problem. See this: ---------- 8< -------------------- 8< -------------------- 8< ---------- <pre> <?php function ifsetor() { $args = func_get_args(); $count = count($args); for ($i=0; $i<$count; $i++) { if (isset($args[$i])) { return $args[$i]; } } return false; } print ifsetor($x, $y, $z, "dante")."\n"; ?> </pre> PHP Notice: Undefined variable: x in .../ifsetor.php on line 13 PHP Notice: Undefined variable: y in .../ifsetor.php on line 13 PHP Notice: Undefined variable: z in .../ifsetor.php on line 13 ---------- 8< -------------------- 8< -------------------- 8< ---------- See my post regarding a specification for 'filled()' as a function. An important feature is that warnings about unset variables should not occur. Your suggestion does not qualify. This problem can not be solved in userspace. Dante

Rick Widmer

20 years ago
D. Dante Lorenso wrote:
> No, that doesn't address the problem. See this:
print @ifsetor($x, $y, $z, "dante")."\n";

Jochem Maas

20 years ago
Rick Widmer wrote:
> D. Dante Lorenso wrote: > >> No, that doesn't address the problem. See this: > > > print @ifsetor($x, $y, $z, "dante")."\n";
point 1: regardless of how people think there should be an ifsetor() in the engine - the devs don't so there wont be one. point 2: anything trying to implement this functionality in userspace is a hack. (to stregethen that stance: Rasmus reiterated the point a few days ago [can't remember which mailinglist] that people should be trying to write *error free* code - ergo the error suppression is not a proper solution) Dante, live with the hack. :-)

D. Dante Lorenso

20 years ago
Jochem Maas wrote:
> point 1: regardless of how people think there should be an ifsetor() > in the engine - the devs don't so there wont be one.
Arrrrvvhggghh! Don't you just want to scream!
> point 2: anything trying to implement this functionality in userspace > is a hack. (to stregethen that stance: Rasmus reiterated the point a few > days ago [can't remember which mailinglist] that people should be > trying to > write *error free* code - ergo the error suppression is not a proper > solution)
Ok. You CAN NOT implement 'ifsetor/coalesce/filled' in userspace. And the devs don't want it to exist ... so, WTF?
> Dante, live with the hack. :-)
Translate to: Dante, you don't need PHP to be better, adequate is good enough. <soap box> Come on devs, I'm pleading with you. Say it isn't so. You force me to write so much extra code unless this functionality exists in the core. Explain to me how every possible thing you might want to do to an array gets it's own function in the language, but something as trivial as this gets refused so strongly! I mean really: array_change_key_case, array_chunk, array_combine, array_count_values, array_diff_assoc, array_diff_key, array_diff_uassoc, ... http://www.php.net/manual/en/ref.array.php You can't tell me that most if not all of these functions could not have been implemented in userspace? I might use 10% of all the array functions, but I'd use 'coalesce' and 'filled' daily. </soap box> Tell me at least this much. Can these two functions be implemented as an extension? From what you know about PHP internals, does what WE want have to be a language construct? If I can write coalesce() and filled() as extensions, I'll submit a new extension/patch. Maybe it could join PECL and with popularity make it's way begrudgingly into the core? Dante

D. Dante Lorenso

20 years ago
Rick Widmer wrote:
> D. Dante Lorenso wrote: >> No, that doesn't address the problem. See this: > print @ifsetor($x, $y, $z, "dante")."\n";
Usage of the shutup operator, @, in this context is a shameful coding habit and hides too much: print @ifsetor($x, fakefunction($y), $z, "dante")."\n"; Does not show any warnings or errors, but REALLY hides this: PHP Notice: Undefined variable: x in .../ifsetor.php on line 13 PHP Fatal error: Call to undefined function fakefunction() in .../ifsetor.php on line 13 Ignoring the notice on $x is fine, but that fatal error shouldn't be ignored. Dante

D. Dante Lorenso

20 years ago
Dear Internals, I'd like a white bikeshed, but until you can decide on a color, how about we just build the bikeshed already. Please consider the following specification: ========== 8< ==================== 8< ==================== 8< ========== filled (PHP 5.2.0) filled - Find first non-empty value in a variable list where empty() evaluates as FALSE. Description ------------------------------ mixed filled ( mixed varname [, mixed ...] ) filled() takes a variable number of parameters. For each of these, *filled()* looks for a variable where !empty( varname ) evaluates as TRUE. It returns the first non-empty value or NULL if all values are empty(). Note: filled() only checks variables as anything else will result in a parse error. In other words, the following will not work: filled(trim($name)). filled() is the opposite of empty(), and no warning is generated when any one of the variables is not set. Return Values ------------------------------ Returns first non-empty() value. If all values are empty(), returns NULL. Example ------------------------------ <?php echo filled($x["somekey"], $_GET["getkey"], $default, "example"); echo filled("yes"); // prints yes filled(false); // returns NULL echo filled($x["apple"], $y, "banana"); // prints banana $y = "cat"; echo filled($x["apple"], $y, "banana"); // prints cat $x["apple"] = "pear"; echo filled($x["apple"], $y, "banana"); // prints pear unset($x["apple"]); echo filled($x["apple"], $y, "banana"); // prints cat ?> See also empty() ========== 8< ==================== 8< ==================== 8< ========== Dante

D. Dante Lorenso

20 years ago
The default value for 'filled()' if all values test TRUE for empty() should be FALSE, not NULL. This is more consistent with the return values of empty(). Since filled() returns the first non-empty() value in the list of parameters provided, it works well when cast into a boolean context because the non-empty() value should evaluate as TRUE. Example: filled("testing") // returns testing if (filled("testing")) {} // true filled(87) // returns 87 (boolean) filled(87) // true $x["apple"] = "42"; filled($x["apple"]) // returns 42 filled($x["pear"]) // returns false if (filled($x["apple"]) !== false) {} // true if (filled($x["pear"]) !== false) {} // false Just wanted to chime in with an update in case 'filled()' can actually be considered. I'd like everyone to consider this one function to be evaluated on it's merits alone and apart from any other functions which also might be wanted. filled() is the opposite of empty() and is not dealing with tests for 'isset()'. Another function should be proposed to solve that problem if it would still be needed after filled() is implemented. Do I have any power to call a vote on this? Dante D. Dante Lorenso wrote: