Equivalent of python's **kwargs in php [duplicate] - php

In C#, there is a new feature coming with 4.0 called Named Arguments and get along well with Optional Parameters.
private static void writeSomething(int a = 1, int b = 2){
// do something here;
}
static void Main()
{
writeSomething(b:3); // works pretty well
}
I was using this option to get some settings value from users.
In PHP, I cannot find anything similar except for the optional parameters but I am accepting doing $.fn.extend (jQuery) kind of function :
function settings($options)
{
$defaults = array("name"=>"something","lastname"=>"else");
$settings = array_merge($defaults,$options);
}
settigs(array("lastname"=>"John");
I am wondering what kind of solutions you are using or you would use for the same situation.

As you found out, named arguments don't exist in PHP.
But one possible solution would be to use one array as unique parameter -- as array items can be named :
my_function(array(
'my_param' => 10,
'other_param' => 'hello, world!',
));
And, in your function, you'd read data from that unique array parameter :
function my_function(array $params) {
// test if $params['my_param'] is set ; and use it if it is
// test if $params['other_param'] is set ; and use it if it is
// test if $params['yet_another_param'] is set ; and use it if it is
// ...
}
Still, there is one major inconvenient with this idea : looking at your function's definition, people will have no idea what parameters it expects / they can pass.
They will have to go read the documentation each time they want to call your function -- which is not something one loves to do, is it ?
Additionnal note : IDEs won't be able to provide hints either ; and phpdoc will be broken too...

You can get around that by having an array such as $array= array('arg1'=>'value1');
And then let the function accept the array such as function dostuff($stuff);
Then, you can check arguments using if(isset($stuff['arg1')){//do something.} inside the function itself
It's just a work-around but maybe it could help

You can fake C++-style optional arguments (i.e. all optional arguments are at the end) by checking for set variables:
function foo($a, $b)
{
$x = isset($a) ? $a : 3;
$y = isset($b) ? $b : 4;
print("X = $x, Y = $y\n");
}
#foo(8);
#foo();
It'll trigger a warning, which I'm suppressing with #. Not the most elegant solution, but syntactically close to what you wanted.
Edit. That was a silly idea. Use variadic arguments instead:
// faking foo($x = 3, $y = 3)
function foo()
{
$args = func_get_args();
$x = isset($args[0]) ? $args[0] : 3;
$y = isset($args[1]) ? $args[1] : 3;
print("X = $x, Y = $y\n");
}
foo(12,14);
foo(8);
foo();

Related

How to avoid a really long list of function parameters in PHP?

My problem is that I have lots of functions with VERY long lists of function parameters such as this one:
function select_items($con,$type,$id_item,$item_timestamp,$item_source_url,$item_type,$item_status,$item_blogged_status,$item_viewcount,$item_language,$item_difficulty,$item_sharecount,$item_pincount,$item_commentcount,$item_mainpage,$item_image_width,$item_image_height,$item_image_color,$item_modtime,$order,$start,$limit,$keyword,$language,$id_author,$id_sub_category,$id_category,$id_tag,$id_user){ ... }
As you can see its super long and (of course) very hard to maintain. Sometimes I need all of the variables to construct a super complex sql query, but sometimes I just use 1 or 2 of them. Is there a way to avoid this colossal list of parameters? For example with some strict / special naming convention ?
So basically I need something like this:
$strictly_the_same_param_name="It's working!";
echo hello($strictly_the_same_param_name);
function hello() //<- no, or flexible list of variables
{
return $strictly_the_same_param_name; // but still able to recognize the incoming value
}
// outputs: It's working!
I thought about using $_GLOBALs / global or $_SESSIONs to solve this problem but it doesn't seems really professional to me. Or is it?
For a first step, as you said, sometimes you need to call the function with only 2 args, you can set default values to your arguments in the declaration of your function. This will allow you to call your function with only 2 args out of 25.
For example:
function foo($mandatory_arg1, $optional_arg = null, $opt_arg2 = "blog_post") {
// do something
}
In a second step, you can use, and especially for that case, arrays, it will be way more simple:
function foo(Array $params) {
// then here test your keys / values
}
In a third step, you can also use Variable-length argument lists (search in the page "..."):
function sum(...$numbers) {
$acc = 0;
foreach ($numbers as $n) {
$acc += $n;
}
return $acc;
}
But ultimately, I think you should use objects to handle such things ;)
You can try use ... token:
$strictly_the_same_param_name= ["It's working!"];
echo hello($strictly_the_same_param_name);
function hello(...$args) //<- no, or flexible list of variables
{
if ( is_array( $args ) {
$key = array_search( 'What you need', $args );
if ( $key !== false ) {
return $args[$key];
}
}
return 'Default value or something else';
}

PHP Function to cascade through a flexible number of full/empty values

I'm trying to write a function that will take any number of arguments, and will return the first argument, in order, that is valid (in my case !empty).
I've been able to pretty much get it working how I want, except I'm getting some notices because of undefined variables. See below for examples:
function cascade()
{
if (func_num_args() < 1) return false;
foreach (func_get_args() as &$arg) {
if (!empty($arg)) return $arg;
}
return false;
}
You can see that I've tried to declare that each $arg of the foreach is passed by reference, but that doesn't seem to have done the trick.
To elaborate on how I plan to use this function, see below:
$a = 'a';
$c = 'c';
echo cascade($z, $b, $a, $c);
Since $z and $b are undefined, the first non-empty variable in the list is $a so the output is a as expected. However, you then get the undefined variable notices, which I wish to get rid of.
I realise I can just say echo #cascade($z, $b, $a, $c); which would suppress the errors, but I want to know if there is a way around this issue, so that the reference can be passed somehow. Any thoughts?
Edit:
To further highlight what I'm trying to acheive, see the following function that DOES work without throwing errors, even when passed an undefined variable:
// returns default value if input variable is not set
function ifset(&$var, $default = false) {
return isset($var) ? $var : $default;
}
With this function, if param 1 is not set, then the default value in param 2 is returned. Either way, no error is thrown.
What I am trying to achieve is the same result, but with ANY number of arguments, as this function is limited to 1, unless I nest them (gets messy).
A real life example:
This is WHY I want this function and how I would use it in a real life scenario:
<input type="text" name="customerName" value="<?= cascade($order->fullName, $currentUser->fullName, 'Anonymous') ?>">
So if we have an order in the making, and there is a name available from that, we use that, if that info hasn't been saved yet, we use the logged in user's name as the default value, if no one is logged in, we use 'Anonymous'.
Not exactly what I would do in real life, but perhaps it highlights example usage?
For those who are suggesting defining the variables to mitigate the errors, the who point of this function is to work though a chain of values, giving priority to the ones that come first, then moving to the next if that is 'empty' and so on, until eventually a FALSE default value is returned if all are empty.
The Notice you're seeing is triggered at the function call, not inside the function itself. Therefore, there is nothing you can do inside the function to solve the problem. However, there are a number of ways of solving this problem depending on how your variables are prepared before the function call.
Solution #1: Define the variables before the function call.
for instance:
$z = ''; OR $z = null;
or any falsy value like : null, "", 0, "0", 0.0, [], ..., your function will still work as expected and you won't see the notice.
Solution #2: Test for validity before the function call.
if( !isset($z) ){ $z = ''; }
echo cascade($z);
Solution #3: Test for validity as part of the function call.
This is the same thing as solution #2 but a bit more elegant. Use the Ternary function to pass the variable value or an empty string depending on whether or not the variable is set.
echo cascade(
isset($z)?$z:'',
isset($b)?$b:'',
isset($a)?$a:'',
isset($c)?$c:''
);
Solution #4: If using PHP 7 or above, you can use the new Null Coalescing Operator. This is the same thing as solution #3, but more elegant still.
echo cascade($z ?? '', $b ?? '', $a ?? '', $c ?? '');
If you want a clean alternative to your function, you can try this:
function cascade()
{
$args = func_get_args();
while (!($arg = array_shift($args)));
return $arg ? $arg : false;
}
BIG THANKYOU to JBH for Editing significantly this answer.
The undefined variable notice is not from the code in function but it is from the function call cascade($z, $b, $a, $c)
You are passing undefined variables ($z and $b) as arguments to the function. Since they are not defined any where in the code, you are getting notice.
To get rid of the the notices, define the variables before passing them as arguments.
$a = 'a';
$c = 'c';
$z = '';
$b = '';
echo cascade($z, $b, $a, $c);
OR
echo cascade('', '', $a, $c);
Don't know if this is "good enough".
Create an array of the variables and use array filter to remove the null values.
Use array values to reset the keys.
Now $arr[0] is the first non empty item.
https://3v4l.org/PlmrS
$a = 'a';
$c = 'c';
$arr =array_values(array_filter([$z, $b, $a, $c]));
Var_dump($arr);
Thank you for all your kind answers, but unfortunately none of them solved my problem. Perhaps I didn't explain it well enough, or I over-complicated it with too much information confusing the issue, but anyway I've been able to find the answer.
The following function does exactly what I wanted. It uses the variadic syntax (php 5.6 and over) which allows a variable number of arguments, all of which are passed by reference.
function cascade(&...$args)
{
if (count($args) < 1) return false;
foreach ($args as &$arg) {
if (!empty($arg)) return $arg;
}
return false;
}

register_shutdown_function converting references to values?

I'm trying to use a shutdown function that uses a value changed dynamically after registration. My understanding was that passing the variable by reference would allow changes in later portions of the code to affect the shutdown call. The code below demonstrates that this is not the case:
<?php
$x = 0;
function shutdown(&$x) {echo $x;}
register_shutdown_function('shutdown',$x);
$x = 1;
exit();
?>
Am I misunderstanding how this should be done? Is it expected behavior for the register function to convert the reference pass to a value pass?
PHP version 5.5.9
One of possible solutions is to use anonymous functions (it's a closure in this case with $x captured as a reference):
$x = 0;
$shutdown = function() use(&$x) { echo $x; };
register_shutdown_function($shutdown);
$x = 1;
exit();
Demo: http://ideone.com/L2cYJp
UPD: Explanation why your solution doesn't work:
If you have a look on register_shutdown_function signature:
void register_shutdown_function ( callable $callback [, mixed $parameter [, mixed $... ]] )
you will see that parameters are passed by values. That's it - the value of $x in register_shutdown_function('shutdown',$x); line is passed by value and equals to the value it was on the moment of call.
If doesn't matter if you defined your function that accepts a reference since it's too late to accept a reference because you have already lost reference (indirectly, by using a function that does not take it into account).

How to check if variable was passed by reference in PHP

Straightforward:
I want to write some code which tells if a variable was passed by reference or not.
For example:
<?php
function isReference($variable) {
//return TRUE if &$something was passed
//return FALSE if $something was passed
}
$text = 'Anything';
$a = isReference(&$text); //return TRUE
$b = isReference($test); //return FALSE
?>
For those who are curious - why do I need it?
Firstly I do not like to leave problems unsolved.
Secondly, I am currently enhancing by skills by writing an extension to mysqli, which would prepare statements similar to how PDO does. If anybody knows the difference between bindParam and bindValue in PDO, they know that it's a workaround of this question. I can just make two functions, but I wonder if it's possible with one.
Thanks in advance.
Here's a way to do it without using the debug_zval_dump function:
function isReference($variable) {
$variable = array($variable);
$arg = func_get_arg(0);
$isRef = isset($arg[0]) && $arg === array($variable[0]);
$variable = $variable[0];
return $isRef;
}
Note: there is only a single case when this will not work:
$text = array(&$text); // although i don't see why such a structure could be used
isReference($text); // will wrongly return false
Obviously you can bypass this limitation by using a random unique key (instead of 0).
You can use debug_zval_dump to dump a string representation of an internal zend value to output:
function isRef(&$val) {
ob_start();
debug_zval_dump(&$val);
$ret = ob_get_clean();
preg_match('~refcount\((\d+)\)~', $ret, $matches);
return $matches[1] - 4;
}
$var1 = 'Var1';
$var2 = &$var1; // passed by ref
if(isRef($var2)) echo 'Passed by ref';
But be aware of PHP - 5.4.
Simply checking for the default value seems to work fine in my tests. Obviously it wont work if $t is already set to 'x' but you could change the default value to something totally unlikely to workaround this:
function test(&$t='x'){
if($t!='x') $t = 2;
}
test(); echo $t; //outputs: null
test($t); echo $t; //outputs: 2

PHP Command Line Arguments and Options

I am writing a small command line application in php.
What is the correct way to handle command line arguments and options?
There seems to be the argv array, $_SERVER['argv'], and getopt but its confusing when to use each?
Also with regards to options i.e. "argument --option" what is the best way to get these?
Arguments, made easy
One day I decided to defeat this monster once and for all. I forged a secret weapon - a function that acts as a storage, a parser and a query function for arguments.
// You can initialize it with a multiline string:
arg("
-a --alpha bool Some explanation about this option
-b --beta bool Beta has some notes too
-n --number int Some number you need for the script
- --douglas int There is no short form of this
-o --others str A string of other things
");
// ... and now you have your arguments nicely wrapped up:
print arg("alpha"); // returns the value of -a or --alpha
print arg("a"); // same thing
print arg(); // returns the whole parsed array
print arg(1); // returns the first unnamed argument
print arg(2); // returns the second unnamed argument
print arg("douglas",42); // value of "douglas", or a reasonable default
Explanation
All you need to do is write the argument list as a multiline string. Four columns, looks like a help, but arg() parses your lines and finds out the arguments automatically.
Separate columns by two or more spaces - just like you would anyway.
Once parsed, each item will be represented by an array of fields, named char, word, type and help, respectively. If there's no short (char) or long (word) version for a parameter, just use a dash. Not for both, obviously.
Types are what they seem: bool means there's no value after the parameter; it's false if missing, true if present. The int and str types mean there must be a value, and int makes sure it's an integer. Optional parameters are not supported. Values can be separated by space or equal sign (i.e. "-a=4" or "-a 4")
After this first call, you have all your arguments neatly organized in a structure (dump it, you'll see) and you can query their values by name or number.
Function arg() has a second parameter for defaults so you'll never have to worry about missing values.
The arg() function itself
function arg($x="",$default=null) {
static $arginfo = [];
/* helper */ $contains = function($h,$n) {return (false!==strpos($h,$n));};
/* helper */ $valuesOf = function($s) {return explode(",",$s);};
// called with a multiline string --> parse arguments
if($contains($x,"\n")) {
// parse multiline text input
$args = $GLOBALS["argv"] ?: [];
$rows = preg_split('/\s*\n\s*/',trim($x));
$data = $valuesOf("char,word,type,help");
foreach($rows as $row) {
list($char,$word,$type,$help) = preg_split('/\s\s+/',$row);
$char = trim($char,"-");
$word = trim($word,"-");
$key = $word ?: $char ?: ""; if($key==="") continue;
$arginfo[$key] = compact($data);
$arginfo[$key]["value"] = null;
}
$nr = 0;
while($args) {
$x = array_shift($args); if($x[0]<>"-") {$arginfo[$nr++]["value"]=$x;continue;}
$x = ltrim($x,"-");
$v = null; if($contains($x,"=")) list($x,$v) = explode("=",$x,2);
$k = "";foreach($arginfo as $k=>$arg) if(($arg["char"]==$x)||($arg["word"]==$x)) break;
$t = $arginfo[$k]["type"];
switch($t) {
case "bool" : $v = true; break;
case "str" : if(is_null($v)) $v = array_shift($args); break;
case "int" : if(is_null($v)) $v = array_shift($args); $v = intval($v); break;
}
$arginfo[$k]["value"] = $v;
}
return $arginfo;
}
// called with a question --> read argument value
if($x==="") return $arginfo;
if(isset($arginfo[$x]["value"])) return $arginfo[$x]["value"];
return $default;
}
I hope this helps a lot of lost souls out there, like I was. May this little function shed a light upon the beauty of not having to write a help AND a parser and keeping them in sync... Also, once parsed, this approach is lightning fast since it caches the variables so you can call it as many times as you want. It acts like a superglobal.
Also available on my GitHub Gist.
You can retrieve the "raw" arguments using $argv.
See also: http://www.php.net/manual/de/reserved.variables.argv.php
Example: php file.php a b c
$argv will contain "file.php", "a", "b" and "c".
Use getopts to get the parameters "parsed", PHP will do the dirty job for you. So it's probably the best way to go in your case as you want to pass the parameters with --options.
Have a close look at http://www.php.net/manual/de/function.getopt.php
It describes the function well.

Categories