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)
Related
I'm trying to understand PHP session handlers and more precisely the return value of SessionHandlerInterface::read.
The doc states (emphasis mine):
Returns an encoded string of the read data. If nothing was read, it must return false. Note this value is returned internally to PHP for processing.
However, the example used in the documentation of the SessionHandler class and other implementation (for instance Symfony's PdoSessionHandler) seem to return an empty string when nothing can be read (like for instance the very first time you connect to an host).
To illustrate this, I wrote a simple test (the class inherits from SessionHandler instead of implementing the whole SessionHandlerInterface for the sake of simplicity - I know this is not recommended and the example is useless: it's just here to show the problem):
<?php
define('RETURN_VALUE', false);
class SimpleSessionHandler extends SessionHandler
{
public function read($id)
{
$data = parent::read($id);
if (!$data) {
return RETURN_VALUE;
} else {
return data;
}
}
}
$handler = new SimpleSessionHandler();
session_set_save_handler($handler, true);
$res = session_start();
print_r($res);
echo("Done");
When using false as the return value, I get a PHP error (Warning: session_start(): Failed to read session data) while when I use '' (empty string) it works like a charm.
I'm missing something ?
I'm using PHP 7.4.20 under Fedora 33.
I asked the question on PHP mailing list (see the thread here).
From what I understood, read() should try to read the data if it exists or return an empty string if it doesn't; it should return false only when it can't read the data (for example if the session file cannot not be created or read, if the DB can't be reached, etc.).
In the case of a session handler based on some DB it would be something like this:
<?php
class DBSessionHandler implements SessionHandlerInterface
{
public function read($id)
{
try {
$data = $this->get_session_from_db();
if (len($data) > 0) {
// Return data
$this->serialize($data);
} else {
// Create new empty session here
try {
$this->create_empty_session_in_db();
} catch (Exception $e) {
return false;
}
return '';
}
} catch (Exception $e) {
return false;
}
}
}
I guess that most handlers would delay the insertion in write() though.
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
I'm creating functions where I want to be able to output true or false pertaining to if statements within the function are met. An example of this is like this:
function pokeme($number){
if($number > 10)
{
return true;
}
else
{
return false;
}
}
However, one problem with this approach is that if the function returns false, you wont be able to decipher which false means what, especially if there is more than one if/else statements.
My question is, how do I output a false plus a way to later identify what that false was?
Do I do an array?
return array("false", "message pertaining to whatever");
However, if that is done, you can't really do this, plus...:
if(pokeme()){ /*success*/ } else { /*there may be multiple falses for different situations... how do I distinguish what it is? */}
Note that the way the idea is demonstrated here might not be the best, but once you get te hang of it, it gets easier. Also, read end note, please.
If you want to use like this (true is expected and false is problem):
if(pokeme()){ /*success*/ } else { /* not needed */}
You can do something like this:
function pokeme($number){
//let's say you want to return true if it's >10 and -9 to -1
if($number > 10){
// do something
return true;
}
if($number < 0 && $number > -10){
return true;
}
// handling multiple problems (just 2 ^^)
if($number < -9){
throw new Exception("Invalid input. Can't use negative smaller than -9.");
}
throw new Exception('Invalid input. Expected bigger than 10.');
}
Two tests:
try{
echo "RESULT1 :".pokeme(-42).":"; // not shown (error in this line)
echo "RESULT2 :".pokeme(11).":"; // not shown
}catch(Exception $e){
echo "Error: '".$e->getMessage()."'"; // Just the message
}
echo "<br><br>";
try{
echo "RESULT3 :".pokeme(11).":<br>"; // shown
echo "RESULT4 :".pokeme(10).":"; // not shown (error in this line)
}catch(Exception $e){
echo $e; // Full error
}
You can use it like this:
try{
if(pokeme(11)){
echo "VALID INPUT<br>";
}
if(pokeme(5)){
echo "I'm not seen :\\";
}
}catch(Exception $e){
echo "Error: '".$e->getMessage()."'";
}
End note: Think of this like you are using a built-in php function that might cause an error. This way you have to handle it with a try..catch.
More about Exceptions.
For fully automated input validations you can make use of the Symfony form component, and its Validation.
You can also add very simple constraints like LessThan(10) exactly like in your example, and the component automatically writes the appropriate error message back to your page (and the invalid form e.g. dont performs DB inserting).
It exists a lot of prepared constraints to use, you can also create own ones.
Or if you want to write all by your self, i suggest you to read OOP and Exception handling before.
EDIT
If you want to "collect" errors with its messages, solving this procedural for your code example (not recommended) you can store this messages in a temporary array in the superglobal variable $_SESSION. I explicitly say superglobal variable. Dont use a global variable and inject it with the global key, this will become very complex code in long therm.
Anyway my idea using $_SESSION. This code works for me:
<?php
session_start();
// Resetting the tmp session array on every page reload. That previous error messages are resetted.
$_SESSION['errors'] = array();
function printSessionErrors()
{
// Print errors stored in the session if some exists
if (array_key_exists('errors', $_SESSION)) {
foreach ($_SESSION['errors'] as $i => $error) {
$success = true === $error['success'] ? 'true' : 'false'; // Only for print
$message = $error['message'];
echo "Success: $success. Message: $message <br>";
}
}
}
function pokeme($number)
{
$expected = 10;
$success = null;
if ($number > $expected) {
$success = true;
} else {
$_SESSION['errors'][] = array(
'success' => $success,
'message' => "Number $number is less than $expected"
);
$success = false;
}
return $success;
}
pokeme(1);
pokeme(7);
pokeme(99);
printSessionErrors();
Now depending on if it was a form POST or procedural validation you add printCleanSessionErrors() on top (after session_start()) or on bottom on the code.
I get this output:
Success: false. Message: Number 1 is less than 10
Success: false. Message: Number 7 is less than 10
You only have to add the $_SESSION['errors'][] = array .... into your other error situations.
That's one of those questions, which, being very poorly phrased, lead to completely wrong answers.
Before asking such a question, one has to realize that there are two kinds of functions:
functions that perform some action
functions that do nothing but just verify the parameter, telling whether it's right or wrong
And the approach for these two kinds must be completely different.
In case pokeme() function has to use the $number parameter somehow, but for some reason cannot do that, then an exception must be thrown. Because it's the very purpose for exceptions: to handle the exceptional behavior. In this case, the function will never returns false, so it makes no sense to return true either. So it will be either the correct result or exception will be thrown. But it is important to understand that as a rule, exceptions shouldn't be caught on the spot. Least an exception has to be used to convey some message from a function.
An exception should be used only if a function is unable to do its job:
function divide_by($number, $divisor)
{
if (!is_numeric($number) || !is_numeric($divisor)) {
throw new InvalidArgumentException("The arguments must be nubmers");
}
if ($divisor === 0) {
throw new InvalidArgumentException("Cannot divide by zero");
}
return $number / $divisor;
}
In case pokeme() function, as it shown in the example provided, has to test the $number somehow, and tell if it's all right or not, no exceptions have to be used. Because wrong value is not an exceptional case.
Here, the simplest approach to convey the message would be to reverse the function's "polarity" and make it return false in case the parameter is all right, and a true-ish value in case it's wrong. Which will allow us to return the very message we need.
function check_number($number)
{
if(!is_int($number))
{
return "The value provided is not numeric";
}
if($number < 10)
{
return "The number is less than 10";
}
return false;
}
and then it can be used like this
$error = check_number($number);
if(!$error) {
/*success*/
} else {
$errors[] = $error;
}
But this approach is rather silly. A more robust solution would be to write a class, utilizing such a handy feature as class variables
class NumberValidator
{
protected $error;
public function validate()
{
if(!is_numeric($number))
{
$this->error = "The value provided is not numeric";
return false;
}
if($number < 10)
{
$this->error = "The number is less than 10";
return false;
}
return true;
}
public function getError()
{
return $this->error;
}
}
and then it can be used like this
$validator = new NumberValidator();
if($validator->validate($number)) {
/*success*/
} else {
$errors[] = $validator->getError();
}
In my program I want update some information, so I used:
return ($result->rowCount() == 1)? true: false;
this way, if you save information without any change, false is returned and this is not the result usually we expect.
I change my function to this one
try{
$result=$db->prepare($sql);
$result->execute($arr);
return true;
}catch(Exception $e){
return false;
}
Is it the best way? Does this way guarantee that the update statement worked or not?
Assuming your $db is instance of PDO
When you run PDO::prepare you can detect sql syntax error and when you run PDO::execute you will be sure that syntax is correct. Then application is resposible for knowing if update should have updated something or not.
So here is some sample code:
function update($db, $sql)
{
$preparedSql = $db->prepare($sql);
if(!$preparedSql) {
// syntax error occured
$errorInfo = $db->errorInfo();
// handle error
return false;
}
$db->execute($preparedSql);
// note that rowCount returns 0 when none updated
return $db->rowCount();
}
In your application
$result = update($db, $sql);
if($result === false)
{
// error occured
} elseif($result === 0) {
// zero rows updated
} else {
// some rows were updated
}
Note that it's not tested and for learning purposes only, update function should accept to bind parameters for real world application
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);