How to check if a function is callable with parameters? - php

I'm calling a function with call_user_func_array :
call_user_func_array(array($this, 'myFunction'), array('param1', 'param2', 'param3'));
Everything is ok unless I don't know how many parameters the function needs.
If the function needs 4 parameters it sends me an error, I'd like to test if I can call the function (with an array of parameters).
is_callable() doesn't allow parameters check.
Edit : If the call fails I need to call another function, that's why I need a check.
Thanks!

You could use reflection to get the number of parameters:
$refl = new ReflectionMethod(get_class($this), 'myFunction');
$numParams = $refl->getNumberOfParameters();
or
$numParams = $refl->getNumberOfRequiredParameters();
See here for some more information

One way getting around this is to call the function always with a lot of arguments. PHP is designed in such a way that you can pass as many extraneous arguments as you want, and the excess ones are just ignored by the function definition.
See manual entry for func_get_args() to see an illustration about this.
Edit: As user crescentfresh pointed out, this doesn't work with built-in functions, only user defined functions. If you try to pass too many (or few) arguments into a built-in function, you'll get the following warning:
Warning: Wrong parameter count for strpos() in Command line code on line [...]

Related

How to write a function with an optional argument and variable length arguments?

I want to implement a method inside a class like this:
class Query {
public function orderBy($dir="asc", ...$fields){}
}
Where $dir is a default argument and $field is a variable-length argument using (...) token.
The problem is that variable-length argument must be declared at the end, and when I do this, the developer can’t skip the first argument to its default. How can I fix this?
Note that I don’t want to use func_get_args() because I want the developer to have some type hints.
This is simply not doable. PHP does not support this, and it doesn't make a lot of sense that it would. The only way this could work is by having named arguments, as in some other languages, making argument order irrelevant.
Furthermore, your requirment of using a variadic argument there is artificial, and not very useful. Just use an array and an optional order argument.
E.g.:
function orderBy(array $columns, string $order = 'asc') {
// do your thing
}
Neater, simpler to understand to the method users, and complies with the language syntax.
If you want it to look "similar" to a variadic function, just call the method using a syntax like this:
orderBy(["column1", "column5"]);
orderBy(["column2"], 'desc');
What I wanted to do was:
function tableHead($id=NULL, $class=NULL, ...$columnsHeads)
the "logical" function call when the first two arguments were not wanted to me was:
tableHead( , , "Dogs","Cars","Sausages");
Sadly PHP does not let you use a , as an empty default value. I get round this by sticking a NULL, NULL, as the first 2 arguments. Not ideal but will do for now!
(I am sure I have used empty , in some language 20 years ago to send an empty value - anyone know? - Or is it just wishful thinking?)
My SOLUTION (haha) is:
tableHead(NULL, NULL, "dogs","cars", "sausages");
Not ideal but it works. In normal usage the function will be called with id and class specified by variables. Personally I do not see anything unholy about combining variadic and optional arguments ... but you do have to be a bit careful!

Does the following call to call_user_func_array() work?

I have a couple of libraries that use code similar to the following one.
$args = array_merge(array(&$target, $context), $args);
$result = call_user_func_array($callback, $args);
The code is different in both the cases, but the code I shown is what essentially is done. The $callback function uses the following signature:
function callback(&$target, $context);
Both the libraries document that, and third-party code (call it plug-in, or extension) adopts that function signature, which means none of the extensions defines the callback as, e.g., function my_extension_loader_callback($target, $context).
What confuses me are the following sentence in the documentation for call_user_func_array().
Before PHP 5.4, referenced variables in param_arr are passed to the function by reference, regardless of whether the function expects the respective parameter to be passed by reference. This form of call-time pass by reference does not emit a deprecation notice, but it is nonetheless deprecated, and has been removed in PHP 5.4. Furthermore, this does not apply to internal functions, for which the function signature is honored. Passing by value when the function expects a parameter by reference results in a warning and having call_user_func() return FALSE.
In particular, the highlighted sentence seems to suggest that is not done for functions define in PHP code.
Does using call_user_func_array() in this way work in PHP 5.4?
When using call_user_func_array, passing by value when a function expects a reference is considered an error, in newer versions of PHP.
This was valid PHP code before PHP 5.3.3:
//first param is pass by reference:
my_function(&$strName){
}
//passing by value, not by reference, is now incorrect if passing by reference is expected:
call_user_func_array("my_function", array($strSomething));
//correct usage
call_user_func_array("my_function", array(&$strSomething));
The above pass by value is no longer possible without a warning (my project is also set to throw exceptions on any kind of error (notice, warning, etc).) so I had to fix this.
Solution
I've hit this problem and this is how I solved it (I have a small RPC server, so there is no such thing as referenced values after deserializing params):
//generic utility function for this kind of situations
function &array_make_references(&$arrSomething)
{
$arrAllValuesReferencesToOriginalValues=array();
foreach($arrSomething as $mxKey=>&$mxValue)
$arrAllValuesReferencesToOriginalValues[$mxKey]=&$mxValue;
return $arrAllValuesReferencesToOriginalValues;
}
Although $strSomething is not passed by reference, array_make_references will make it a reference to itself:
call_user_func_array("my_function", array_make_references(array($strSomething)));
I think the PHP guys were thinking of helping people catch incorrectly called functions (a well concealed pitfall), which happens often when going through call_user_func_array.
If call_user_func_array() returns false you have a problem, otherwise everything should be fine.
Parameters aren't passed by reference by default anymore, but you do it explicitly.
The only trouble could be that your reference gets lost during array_merge(), haven't tested that.
I've found this same problem when upgrading to PHP5.4 when there were several sites using call_user_func_array with arguments passed by reference.
The workaround I've made is very simple and consists on replacing the call_user_func_array itself with the full function call using eval(). It's not the most elegant solution but it fits the purpose for me :)
Here's the old code:
call_user_func_array($target, &$arguments);
Which I replace with:
$my_arguments = '';
for ($i=0; $i<count($arguments); $i++) {
if ($i > 0) { $my_arguments.= ", "; }
$my_arguments.= "\$arguments[$i]";
}
$evalthis = " $target ( $my_arguments );";
eval($evalthis);
Hope this helps!

How can I get a list of arguments _expected_ by a function?

I need to be able to interrogate a function/method and get a list of the arguments which that function expects, from outside the function, before the function is called.
So not argument values, but a list of expected argument 'slots' per say.
Possible?
Take a look at PHP Reflection - specifically the ReflectionParameter class...
http://nz.php.net/manual/en/language.oop5.reflection.php#language.oop5.reflection.reflectionparameter

How can I get the list of available arguments for a PHP function?

Is there a way to get the available arguments for a PHP function?
Example: I want to call function1($arg1, $arg2=null). How can I find out, before calling the function, the number of arguments this function takes, and, if it's possible, what arguments?
As you can see, I am dynamically calling functions.
ReflectionFunction
You can use the following functions from inside the function that is being called to determine how many arguments were passed and to get their values. I'm not sure how you would check what arguments a function expects.
func_num_args()
func_get_arg()
func_get_args
Normally, via ReflectionFunction. However, PHP functions can use variable arguments and in that case it's impossible to tell.

Recommended replacement for deprecated call_user_method?

Since PHP's call_user_method() and call_user_method_array() are marked deprecated I'm wondering what alternative is recommended?
One way would be to use call_user_func(), because by giving an array with an object and a method name as the first argument does the same like the deprecated functions. Since this function is not marked deprecated I assume the reason isn't the non-OOP-stylish usage of them?
The other way I can think of is using the Reflection API, which might be the most comfortable and future-oriented alternative. Nevertheless it's more code and I could image that it's slower than using the functions mentioned above.
What I'm interested in:
Is there a completely new technique for calling an object's methods by name?
Which is the fastest/best/official replacement?
What's the reason for deprecation?
As you said call_user_func can easily duplicate the behavior of this function. What's the problem?
The call_user_method page even lists it as the alternative:
<?php
call_user_func(array($obj, $method_name), $parameter /* , ... */);
call_user_func(array(&$obj, $method_name), $parameter /* , ... */); // PHP 4
?>
As far as to why this was deprecated, this posting explains it:
This is
because the call_user_method() and call_user_method_array() functions
can easily be duplicated by:
old way:
call_user_method($func, $obj, "method", "args", "go", "here");
new way:
call_user_func(array(&$obj, "method"), "method", "args", "go", "here");
Personally, I'd probably go with the variable variables suggestion posted by Chad.
You could do it using variable variables, this looks the cleanest to me. Instead of:
call_user_func(array($obj, $method_name), $parameter);
You do:
$obj->{$method_name}($parameter);
Do something like that :
I use something like that in my __construct() method.
$params = array('a','b','c'); // PUT YOUR PARAMS IN $params DYNAMICALLY
call_user_func_array(array($this, $this->_request), array($params));
1st argument : Obect instance,
2nd argument : method to call,
3rd argument : params
Before you can test if method or Class too, with :
method_exists()
class_exists()
Cheers
if you get following error:
Warning: call_user_func() expects parameter 1 to be a valid callback, second array member is not a valid method in C:\www\file.php on line X
and code like this:
call_user_func(array($this, $method));
use (string) declaration for $method name
call_user_func(array($this, (string)$method));

Categories