====== PHP RFC: unset(): return bool if the variable has existed ======
* Version: 0.61
* Date: 2013-03-06
* Author: Bob Weinand, bobwei9@hotmail.com
* Status: Inactive
* First Published at: http://wiki.php.net/rfc/unset_bool
* Implementation: https://github.com/bwoebi/php-src/commit/787d71eed0c5e0140048b8fbacf799029b322661 (tests may follow later)
===== Introduction =====
The purpose of this RFC is to make unset return something meaningful. People also shouldn't have to wonder why they can't use unset if it isn't as a standalone expression.
This removes also an inconsistency: the function's return value is //void//, states the docs. It is until now the only function (language construct, function-like) (apart from echo, which already has an alternative: print) which has no return value.
===== Proposal =====
Change unset()'s behaviour to:
* return true if deletion was successful
* return false if deletion failed (e.g. there was nothing to delete or the deleting function has failed)
The feature could be useful in if's like:
public function deleteKey ($key) {
if (unset($this->array[$key])) {
// do some cleanup
}
}
This is the way we do it today:
public function deleteKey ($key) {
if (isset($this->array[$key])) {
unset($this->array[$key]);
// do some cleanup
}
}
But even now we can't be sure if the variable was unset, because the _////_unset() magic method may not have deleted it. With the patch _////_unset() can return a value (which is internally casted to bool) and inform the caller of unset() if the deletion was successful.
The main advantage is that you can do constructs like this: https://git.php.net/?p=php-src.git;a=blob;f=Zend/zend_object_handlers.c#l818 I am just asking myself: when you use it in the Engine, why don't you offer the possibility to PHP?
===== Execution example =====
$var = 1;
var_dump(unset($var)); // bool(true)
var_dump(unset($var)); // bool(false)
$var = 1;
var_dump(unset($var, $undefined)); // evaluates to unset($var) && unset($undefined) == bool(false) (like isset() works)
$dim[1] = "set";
var_dump(unset($dim[2])); // bool(false)
var_dump(unset($dim[1])); // bool(true)
class object {
private $property;
public function __unset($var) {
if ($var == "prima")
return false;
if ($var == "secunda")
return unset($this->property);
if ($var == "tertia")
return "some garbage";
}
}
$class = new object;
$class->prop = 0;
var_dump(unset($class->prop)); // bool(true)
var_dump(unset($class->prima)); // bool(false)
var_dump(unset($class->secunda)); // bool(true)
var_dump(unset($class->tertia)); // bool(true)
var_dump(unset($class->quarta)); // bool(false) (a function execution without return returns NULL, which is, casted to boolean, false)
===== Use case =====
class User {
private $data = [];
// here a __set and a __get function for $data
public function __unset ($key) {
switch ($key) {
case "id":
return false;
default:
return unset($this->data[$key]);
}
}
class UserManager {
private $Users = [];
public function __construct($data) {
// some code to initialize the $this->Users array
}
public function reset($userId, $key) {
if (!unset($this->Users[$userId]->$key)) {
throw new Exception("You can't reset the `$key` of '{$this->Users[$userId]->name}'");
}
}
}
$manager = new UserManager($data);
$manager->reset($id, "id"); // now you have a meaningful debugging output through classes as current behaviour would be to do nothing - silently.
This genre of code could be also in Frameworks to indicate to a coder that the variable can be accessed, but not deleted.
===== Implementation Details ===
Every deleting function called by the VM_HANDLERs ZEND_UNSET_* now return //int//: SUCCESS or FAILURE.
All the functions which didn't were previously //void// functions, so only adding a return value shouldn't affect any existing code (only exception: see one section below).
The zend_do_unset function was adapted to zend_do_isset_or_is_empty in behaviour. As it's mostly reusing existing code, it shouldn't be error-prone.
The rest was adding loads of "result = SUCCESS/FAILURE;" into the various void functions.
===== Backward Incompatible Changes =====
There may break some extensions which have their own unset_property and/or unset_dimension class-handler.
This can easily be fixed by changing the return value from //void// to //int// and returning SUCCESS (or FAILURE).
===== Proposed PHP Version(s) =====
Should be implemented in the next 5.x (e.g. 5.5 if it's delayed due to ZO+ or 5.6).
===== Microbenchmark =====
time ./sapi/cli/php -r 'while($i++ < 1e7) { $a = true; unset($a); }'
Unpatched: average of 5x executed:
real 0m4.935s
user 0m4.925s
sys 0m0.008s
Patched: average of 5x executed:
real 0m4.945s
user 0m4.938s
sys 0m0.005s
This is an increase of 0.15%. This is 1 nanosecond per execution more than previously.
===== References =====
- https://github.com/php/php-src/pull/282
===== Changelog =====
* Version 0.5: Initial RFC
* Version 0.51: Added example for multiple unset()'s in one
* Version 0.52: echo may be also considered as a function...
* Version 0.53: Added microbenchmark
* Version 0.6: Added example real world use case
* Version 0.61: Little clarification about failures