A function or method can be called dynamically using call_user_func_array. If the call itself fails, FALSE is returned. Also, call_user_func_array returns the return values from the function or method that is called.
So when the called function or method returns FALSE as well (for example, see SO example), that value would be recognised as a false positive.
How can one reliably check if the function or method call was executed succesfully by call_user_func_array?
EDIT: People tend to point out the existence of is_callable. But this is not about checking if a method exists before calling it, thus avoiding possible errors. Actually before doing call_user_func_array the function call and it's arguments and argument types are already verified using Reflection to avoid a Massive Assign attack.
The documentation mentions the FALSE return value, but I fail to see how it can be used to check if the call was succesful.
You can explicitly check whether an error occurred during the last call:
error_clear_last(); // since PHP 7, before that you'll need to store and
// compare the error state before and after the call
$result = call_user_func_array($foo, $bar);
if ($result === false && error_get_last()) {
echo 'Failed to call ', $foo;
}
The above is a generic check for any error, perhaps you want to inspect the last error in more detail. It'll look something like:
Array
(
[type] => 2
[message] => call_user_func_array() expects parameter 1 to be a valid callback, function 'foo' not found or invalid function name
[file] => /in/M8PrG
[line] => 3
)
You might want to check that the message matches something like 'call_user_func_array() expects parameter 1 to be a valid callback' and/or that the line it refers to is the line above. Note that especially checking the message may break between PHP versions.
The alternative is to check before whether your supposed callback is_callable.
I'd transform the boolean callable into one that is void but that throws an exception on error.
That way, you could catch the exception and you would know if false was returned by the call_user_func_array that only its call failed:
<?php
$booleanCallable = function (... $args): bool {
foreach ($args as $arg) {
echo "$arg \n";
};
return false;
};
$transformBooleanCallableToVoidThrowingException = function (callable $c): callable {
return function (... $args) use ($c): void {
if (false === $c(... $args)) {
throw new \RuntimeException("the call to the callable failed");
}
};
};
try {
$callable = $transformBooleanCallableToVoidThrowingException($booleanCallable);
$response = call_user_func_array($callable, [1, 2, 3]);
if (false === $response) {
throw new \RuntimeException("call_user_func_array failed");
}
} catch (\Exception $e) {
echo $e->getMessage();
}
This will output the provided arguments and an error message:
1
2
3
the call to the callable failed
Related
What is the best way to return errors from a PHP function, when the function has executed normally?
Example
public function login($user, $pw){
if(!$this->verifyUser($user))
// return error about invalid user
}
else if (!$this->verifyPw($pw)){
// return error about invalid pw
}
else {
// return OK
}
}
Caller - Return response as JSON for web UI
public function doLogin($user,$pw){
$res = $this->login($user, $pw);
return json_encode($res);
}
On one hand I could understand returning results as an array, but I feel like this does not make sense for such low level functions. Perhaps they should return error codes and then caller must lookup the error code string?
Assuming you are in an object, you basically have three major options:
store errors in something like $this->errors array and return false
have some kind of error-collector as a dependency for object, where you
call $this->collector->addError('blah blah'); and return false
throw an exception
For the first two approaches, you will have to check the return value, and based on that, pull the list of errors. But both of those options have the benefit of being able to collect multiple errors.
The exception approach is a bit lighter on coupling, but you can only get one error.
As for what to actually return, I would recommend going with error code + description string. But that string would not be returned by your class. Instead your error should be registered using some "placeholder", that later is translated:
$this->errors[] = [
'code' => 52,
'msg' => 'authentication.login.invalid-password',
];
When you pull the errors from your object, it would be basically a list of entries like this, And then you just run them through your translation service.
In a case of exception, that same information would reside in $e->getCode() and $e->getMessage(), when your object throws InvalidPassword exception.
For an API response the answer from tereško would be along the correct lines.
For a DOM response you can do the following:
I have used a response code only in the past for something so simple:
http://php.net/manual/en/function.http-response-code.php with code 401
public function login($user, $pw) {
header_remove(); # Clear all previous headers.
if( !$this->verifyUser($user) || !$this->verifyPw($pw) ){
http_response_code(401);
exit;
}
http_response_code(200);
exit;
}
jQuery:
$.ajax({
.......
statusCode: {
200: function() {
window.location.href = '/';
},
401: function() {
alert( "Login Failed" );
}
}
});
I am trying to mod a script written for MySQL to mysqli and have been successful up to this error:
Warning: get_resource_type() expects parameter 1 to be resource, object given (etc)
Here is the code:
function free_result($queryid=-1)
{
if($queryid != -1)
{
$this->queryid = $queryid;
}
if((get_resource_type($this->queryid)==='mysql result') && is_resource($this->queryid))
return #mysql_free_result($this->queryid);
else
{
return false;
}
}
I can't for the life of me figure out how to change this to update it to mysqli.
Unlike the old MySQL functions, MySQLi methods don't return resources. They either return an object or some other string/integer/boolean/null type. The error message is crystal clear. The function get_resource_type() expects a resource, as an argument, and you're giving it an object.
Be sure to read about Types in PHP for clarification on the differences between resource types and object types.
My guess here is that you expect $this->queryid to be a MySQLi_Result object, and that's what you're trying to check for. So rather than get_resource_type() use the instanceof operator instead, to check the object's type.
function free_result($queryid = -1) {
if($queryid != -1) {
$this->queryid = $queryid;
}
if($this->queryid instanceof MySQLi_Result)
return mysqli_free_result($this->queryid);
} else {
return false;
}
}
Alternatively, you can just Type Hint the function's prototype to only accept objects of type MySQLi_Result to simplify your code and make it easier to test.
function free_result(MySQLi_Result $queryid = null) {
if($queryid) {
$this->queryid = $queryid;
}
if($this->queryid instanceof MySQLi_Result) {
return mysqli_free_result($this->queryid);
} else {
return false;
}
}
Also, please stop using the error silence operator #. No good can ever come of that.
Make sure you're calling mysqli_free_result() and not the old mysql_free_result() function, also, as mentioned in the comments.
You need to understand that MySQli returns an object instead of a resource. So you should test for that
If($query instanceof mysqli_result)
How could I perform the equivalent to an open try:this except:pass in Python?
I have the following code:
$banana = true;
unserialize($banana);
And it returns the error:
Warning: array_keys() expects parameter 1 to be array, boolean given
Which is expected, as I fed it a boolean.
This is just an example; I am using unserialize(), but I'm not purposely feeding it booleans. I need it to work this way.
Since unserialize doesn't throw an exception, there is nothing to catch.
One of the workarounds is using the silence operator (#) and check whether the
outcome of the unserialize method equals false.
$foo = #unserialize($bar);
if ($foo === false && $bar !== 'b:0;') {
// Unserialize went wrong
}
set_error_handler(function ($errno, $errstr) {
throw new Exception($errstr, 0);
}, E_NOTICE);
try {
$result = unserialize($banana); // correct
} catch (Exception $e) {
$result = array(); // default value
}
restore_error_handler();
print_r($result);
I throw an exception like this:
public function findRole($role)
{
if(!is_string($role)){
throw new \InvalidArgumentException(
sprintf('Role should be a string, %s given.', gettype($role))
);
//...
}
I have seen some exceptions like this and would like to do the same:
error: json_decode() expects parameter 1 to be string, array given.
Any chance I can automatically throw an exception like this so that the exception automatically outputs the name of the function and the invalid argument number for me?
Those errors you want are printed automatically by PHP and probably handled nicely with a set_error_handler function. There's no way you can simulate the same behavior yourself (possibly without nonsense hacks). Therefore you are forced to go with your exception way.
There's an exception that you should be aware of: type hinting; that can only be used with arrays, classes, objects and callables (functions):
public function acceptArray(array $array);
public function acceptObject(object $o);
public function acceptClass(MyClass $o);
public function acceptCallback(callable $f);
These functions if called with any other type of variable will complain almost like the specific error you posted.
The hacks I was talking about earlier might include redefining every type yourself:
class Int {...}
class String {...}
class Float {...}
class Bool {...}
and then use it like that:
$bool = new Bool(true);
acceptString($bool); // public function acceptString(String $s);
will trigger an error. But that's just not how PHP was supposed to work. Therefore I still suggest you to go with your initial idea.
Not automatically, but you could make kind of a generic template, for example like that:
if(!is_string($role)) {
throw create_invalid_argument_exception(__METHOD__, 1, 'string', $role);
}
function create_invalid-argument_exception($method, $argNo, $expectedType, $actualValue) {
return new \InvalidArgumentException(
sprintf(
'%s expects parameter %d to be %s, %s given.',
$method, $argNo, $expectedType, gettype($actualValue)
)
);
}
For catching exceptions, you must use the construction:
try{
/** you code here */
}catch(Exception $e){
/** convert $e to json and output */
}
And wrap you main function by it
I have method, in this method may be happened fatal error, for catching this error I make this
class a {
function shutDownFunction() {
$error = error_get_last();
if ($error['type'] == 1) {
echo "this is fatal error";
}
}
function terribleFunction () {
register_shutdown_function(array($this,'shutdownFunction'));
// here is code, wich may causes fatal error
}
}
Okay, this understand, but I need pass argument from terribleFunction to shutDownFunction. How to make this?
First you need to specify that shutDownFunction should accept a parameter.
function shutDownFunction($var)
Then you can call register_shutdown_function as so
register_shutdown_function(array($this, 'shutdownFunction'), $myVar);
Documentation is here and there are examples in the comments.
You should only use one register_shutdown_function and not use it just inside a method to catch errors in that method. For error catching use a try, catch, finally block
See:
http://php.net/manual/en/language.exceptions.php
You can check register_shutdown_function documentation. There is a second optional argument parameter which is passed to your shutDownFunction(). This function you can define like this:
function shutDownFunction($args) {
//do whatever you want with $args
}