I'm setting up a PHP API that will expose functionality and vend data to my users, and I'm looking for an elegant way of passing to my functions the arguments that my users are "passing" to me. I can determine what method my users are calling ($_POST['method']) and depending on the method, zero or more arguments to the method. I validate them using a metadata array. For example:
$methods = array(
'say_hello' => array('name'),
'say_goodbye' => array(),
'do_something' => array( 'foo', 'bar' )
);
I have corresponding functions for these:
function say_hello( $name ) { printf( "Hello, %s, $name ); }
function say_goodbye() { printf( "Goodbye!" ); }
function do_something( $foo, $bar ) { printf( "%d + %d = %d.", $foo, $bar, $foo+$bar ); }
When a POST comes in, I check that the method they're requesting is an array key of mine (so they're not passing in $_POST['method'] = 'exec' or anything nefarious), and I can do the actual call as:
$method = $_POST['method'];
$method(); // make the call
And knowing the method also allows me to determine what - if any - arguments should go into it:
$args = $methods[ $method ]; // an array of 0-2 items
But is there a good way for me to combine this without a big if-elseif-elseif-... ?
if( 'say_hello'==$method )
$method( $_POST['name'] );
elseif( ... )
...
Something like Python's myfunc(*args) is what I'm looking to do, that would let me somehow accomplish:
$method = $_POST['method'];
$args = $methods[ $method ];
$method( $args );
Right now, my best bet is to load the arguments in the methods:
function do_something()
{
$foo = $_POST['foo'];
$bar = $_POST['bar'];
...
Related
I have several interchangeable functions with different numbers of arguments, for example:
function doSomething1($arg1) {
…
}
function doSomething2($arg1, $arg2) {
…
}
I would like to pass a certain number of these functions, complete with arguments, to another handling function, such as:
function doTwoThings($thing1, $thing2) {
$thing1();
$thing2();
}
Obviously this syntax is not correct but I think it gets my point across. The handling function would be called something like this:
doTwoThings(‘doSomething1(‘abc’)’, ‘doSomething2(‘abc’, ‘123’));
So the question is, how is this actually done?
From my research it sounds like I may be able to "wrap" the "doSomething" function calls in an anonymous function, complete with arguments and pass those "wrapped" functions to the "doTwoThings" function, and since the anonymous function doesn't technically have arguments they could be called in the fashion shown above in the second code snippet. The PHP documentation has me confused and none of the examples I'm finding put everything together. Any help would be greatly appreciated!
you could make use of call_user_func_array() which takes a callback (eg a function or class method to run) and the arguments as an array.
http://php.net/manual/en/function.call-user-func-array.php
The func_get_args() means you can feed this funciton and arbitary number of arguments.
http://php.net/manual/en/function.func-get-args.php
domanythings(
array( 'thingonename', array('thing','one','arguments') ),
array( 'thingtwoname', array('thing','two','arguments') )
);
funciton domanythings()
{
$results = array();
foreach( func_get_args() as $thing )
{
// $thing[0] = 'thingonename';
// $thing[1] = array('thing','one','arguments')
if( is_array( $thing ) === true and isset( $thing[0] ) and is_callable( $thing[0] ) )
{
if( isset( $thing[1] ) and is_array( $thing[1] ) )
{
$results[] = call_user_func_array( $thing[0], $thing[1] );
}
else
{
$results[] = call_user_func( $thing[0] );
}
}
else
{
throw new Exception( 'Invalid thing' );
}
}
return $results;
}
This would be the same as doing
thingonename('thing','one','arguments');
thingtwoname('thing','two','arguments');
Ok so I have a function with 2 mandatory arguments and then it must have many optional arguments too.
function example($a,$b, $username, $email) {
// code
}
My data for the optional arguments comes from an array
$x = array('joeblogs', 'joe#blogs.com');
How would i be able to parse these? bearing in mind that the function may be required to parse a different set of arguments each time.
An example is with CakePHP you can specify the action arguments that are required
Something like this?
$a = 'a';
$b = 'b';
$x = array('joeblogs', 'joe#blogs.com');
$args = array_merge(array($a, $b), $x);
call_user_func_array('example', $args);
See http://php.net/manual/en/function.call-user-func-array.php
There are two approaches to optional arguments.
In the first, you specify all of the arguments like this:
function example($a, $b, $c=null, $d=null, $e=null)
Parameters $a and $b are required. The others are optional and are null if nothing is provided. This method requires that each of the optional parameters be specified in the indicated order. If you want to call the method using only $a, $b and $e you have to provide null values for $c and $d:
example($a, $b, null, null, $d);
The second method accepts an array as the third parameter. This array will be checked for keys and processed based on the keys found:
function example($a, $b, $c=array()) {
$optionalParam1 = ( !empty( $c['param1'] ) ) : $c['param1'] ? null;
$optionalParam2 = ( !empty( $c['param2'] ) ) : $c['param2'] ? null;
In this way, you can check for each key that may be provided. Null values will be provided for any key not populated.
Following shows syntax for optional parameters and default values
function example($a,$b, $username = '', $email = '') {
}
Another possibility is to pass an "optional values array"
function example($a,$b, $optional_values = array()) {
if($optional_values[0] != '') { blah blah .... }
}
This solution is a merge of your sugestion and Jez's solution.
call_user_func_array(array($controller, $action), $getVars);
Where $controller is the instance of your controller, $action is the string to the action that you want to call, and $getVars is an array of parameters.
The first parameter of the call_user_func_array function is a callback. It's possible to define a method invocation as callback.
Here is a link to the documentation of PHP's callback: http://www.php.net/manual/pt_BR/language.pseudo-types.php#language.types.callback
To pass the array parameters to a function you can use call_user_func_array:
$args = array( 'foo', 'bar', 'joeblogs', 'joe#blogs.com' );
call_user_func_array( 'example', $args );
Or simple pass any number of parameters:
example( $a, $b, $username, $email );
To retrieve the parameters inside function use func_get_args:
function example() {
$args = func_get_args();
print_r( $args );
// output:
// Array (
// [0] => foo
// [1] => bar
// [2] => joeblogs
// [3] => joe#blogs.com
// )
}
I have the following variable:
$argument = 'blue widget';
Which I pass in the following function:
widgets($argument);
The widgets function has two variables in it:
$price = '5';
$demand ='low';
My questions is how can I do the following:
$argument = 'blue widget'.$price.' a bunch of other text';
widgets($argument);
//now have function output argument with the $price variable inserted where I wanted.
I don't want to pass $price to the function
price is made available once inside the function
Is there any sound way I can do this or do I need to rethink my design?
Off the top of my head, there are two ways to do this:
Pass in two arguments
widget($initText, $finalText) {
echo $initText . $price . $finalText;
}
Use a placeholder
$placeholder = "blue widget {price} a bunch of other text";
widget($placeholder);
function widget($placeholder) {
echo str_replace('{price}',$price,$placeholder);
}
// within the function, use str_replace
Here's an example: http://codepad.org/Tme2Blu8
Use some sort of placeholder, then replace it within your function:
widgets('blue widget ##price## a bunch of other text');
function widgets($argument) {
$price = '5';
$demand = 'low';
$argument = str_replace('##price##', $price, $argument);
}
See it here in action: http://viper-7.com/zlXXkN
Create a placeholder for your variables like this:
$argument = 'blue widget :price a bunch of other text';
in your widget() function, use a dictionary array and str_replace() to get your result string:
function widgets($argument) {
$dict = array(
':price' => '20',
':demand' => 'low',
);
$argument = str_replace(array_keys($dict), array_values($dict), $argument);
}
I would encourage preg_replace_callback. By using this method, we can easily use the captured values as a lookup to determine what their replacement should be. If we come across an invalid key, perhaps the cause of a typo, we can respond to this as well.
// This will be called for every match ( $m represents the match )
function replacer ( $m ) {
// Construct our array of replacements
$data = array( "price" => 5, "demand" => "low" );
// Return the proper value, or indicate key was invalid
return isset( $data[ $m[1] ] ) ? $data[ $m[1] ] : "{invalid key}" ;
}
// Our main widget function which takes a string with placeholders
function widget ( $arguments ) {
// Performs a lookup on anything between { and }
echo preg_replace_callback( "/{(.+?)}/", 'replacer', $arguments );
}
// The price is 5 and {invalid key} demand is low.
widget( "The price is {price} and {nothing} demand is {demand}." );
Demo: http://codepad.org/9HvmQA6T
Yes, you can. Use global inside your function.
$global_var = 'a';
foo($global_var);
function foo($var){
global $global_var;
$global_var = 'some modifications'.$var;
}
Consider changing the argument and then returning it from your widget function rather than simply changing it within the function. It will be more clear to people reading your code that $argument is being modified without having to read the function as well.
$argument = widget($argument);
function widget($argument) {
// get $price;
return $argument . $price;
}
I am sorry, that sounds like a noob question. I am trying to do this, maybe my question is not clear.
I want to be able to pass something like this:
make_thumbnail( array( 'width' => 60, 'height' => 40', 'title' => 'my image' ) );
Now the above line calls the function which already produces the thumbnails I have that no problem, but I want flexibility here. I mean my function has variables ordered like this:
function make_thumbnail($title,$width,$height) {
the code..
echo ...
}
Now you get what I want to do? I want to be able to pass the variables in any order.. they do not have to come in same order title, width, height.. i want to be able to specify the order when I call the function in template as I put in very first line.
I tried to make my question as clear as I can, but really could not find anything about it.
This sort of thing?
function make_thumbnail($myarray) {
$sometitle = $myarray["title"]
$somewidth = $myarray["width"]
$someheight = $myarray["height"]
}
Why not have the array as the function argument? e.g.
function make_thumbnail($argsArray) {
echo $argsArray['width'];
}
You can create variables within your function for each parameter
function make_thumbnail($argsArray) {
$width = $argsArray['width'];
$height = $argsArray['height'];
$title = $argsArray['title'];
// ...plug the rest of your original function here
}
Then your function will behave exactly the same, except you can pass in an array.
What you're asking for is a description of the Reflection syntax of PHP:
function callWithNamedParams( $funcName, array $args = null )
{
if( is_null( $args ) ) return $funcName();
$f = new ReflectionFunction($funcName);
$input = array();
foreach( $f->getParameters() as $param )
{
array_push( $input, #$args[ $param->getName() ] );
}
return call_user_func_array( $funcName, $input );
}
Use:
function myFunc( $foo, $bar )
{
echo "foo = $foo; Bar = $bar";
}
callWithNamedParams( "myFunc", array( "bar"=>1, "foo"=>2 ) );
You should get foo = 2; Bar = 1 as an output.
you need to define your logic to take any parameter as the one you want it to be. Array is the best thing you can use. But changing the parameters changes the signatures.
you are kinda implementing polymorphism but in a wrong way..
Given the following interface:
interface ISoapInterface {
public static function registerSoapTypes( &$wsdl );
public static function registerSoapOperations( &$server );
}
And the following code:
$soapProvider = array( "FilePool", "UserList" );
foreach( $soapProvider as $provider ) {
call_user_func( array( $provider, "registerSoapTypes" ), &$server->wsdl );
call_user_func( array( $provider, "registerSoapOperations" ), &$server );
}
FilePool and UserList both implement ISoapInterface.
PHP will complain about the two calls inside the foreach stating:
Call-time pass-by-reference has been deprecated
So I looked that message up, and the documentation seems quite clear on how to resolve this. Removing the ampersand from the actual call.
So I changed my code to look like this:
$soapProvider = array( "FilePool", "UserList" );
foreach( $soapProvider as $provider ) {
call_user_func( array( $provider, "registerSoapTypes" ), $server->wsdl );
call_user_func( array( $provider, "registerSoapOperations" ), $server );
}
Now PHP complains
Parameter 1 to FilePool::registerSoapTypes expected to be reference, value given
Parameter 1 to FilePool::registerSoapOperations expected to be reference, value given
In addition to that, the functionality is now broken. So this obviously can't be the solution.
From the call_user_func:
Note that the parameters for call_user_func() are not passed by reference.
To invoke static methods you can use Class::method() syntax, supplying a variable for the Class and/or method parts:
$soapProvider = array( "FilePool", "UserList" );
foreach( $soapProvider as $provider ) {
$provider::registerSoapTypes($server->wsdl);
$provider::registerSoapOperations($server);
}
While call_user_func does not pass parameters by reference, call_user_func_array can.
$callback = array($provider, 'blahblahblah');
call_user_func_array($callback, array( &$server ));
The only real difference is that it expects an array of parameters instead of a list of parameters like call_user_func (similar to the difference between sprintf and vsprintf)...