这可能微不足道,但销毁不存在的变量不会报错。
(PHP 4, PHP 5, PHP 7, PHP 8)
unset — 销毁指定的变量
unset() 销毁指定的变量。
unset() 在函数内部的行为可能因尝试销毁的变量类型而异。
如果在函数内部 unset() 一个全局变量,则只销毁局部变量。调用环境中的变量将保持与调用 unset() 之前相同的值。
<?php
function destroy_foo()
{
global $foo;
unset($foo);
}
$foo = 'bar';
destroy_foo();
echo $foo;
?>
上面的例子将输出
bar
要在函数内部 unset() 一个全局变量,请使用 $GLOBALS 数组。
<?php
function foo()
{
unset($GLOBALS['bar']);
}
$bar = "something";
foo();
?>
如果一个通过引用传递的变量在函数内部被 unset(),则只销毁局部变量。调用环境中的变量将保持与调用 unset() 之前相同的值。
<?php
function foo(&$bar)
{
unset($bar);
$bar = "blah";
}
$bar = 'something';
echo "$bar\n";
foo($bar);
echo "$bar\n";
?>
上面的例子将输出
something something
如果在函数内部 unset() 一个静态变量,unset() 只会在函数剩余上下文中销毁该变量。后续调用将恢复变量的先前值。
<?php
function foo()
{
static $bar;
$bar++;
echo "Before unset: $bar, ";
unset($bar);
$bar = 23;
echo "after unset: $bar\n";
}
foo();
foo();
foo();
?>
上面的例子将输出
Before unset: 1, after unset: 23 Before unset: 2, after unset: 23 Before unset: 3, after unset: 23
var
要销毁的变量。
vars
更多变量。
不返回任何值。
示例 #1 unset() 示例
<?php
// 销毁单个变量
unset($foo);
// 销毁数组中的单个元素
unset($bar['quux']);
// 销毁多个变量
unset($foo1, $foo2, $foo3);
?>
示例 #2 使用 (unset)
类型转换
(unset)
类型转换常与 unset() 函数混淆。(unset)
类型转换仅作为 NULL
类型转换,为了完整性。它不会改变它正在转换的变量。自 PHP 7.2.0 起,(unset) 类型转换已弃用,自 8.0.0 起已移除。
<?php
$name = 'Felipe';
var_dump((unset) $name);
var_dump($name);
?>
上面的例子将输出
NULL string(6) "Felipe"
注意:
可以销毁在当前上下文中可见的对象属性。
注意:
无法在对象方法内销毁
$this
。
注意:
当对不可访问的对象属性使用 unset() 时,如果已声明,则将调用 __unset() 重载方法。
你不需要在销毁变量之前检查变量是否已设置。
<?php
unset($a);
?>
是无害的。
<?php
if(isset($a)) {
unset($a);
}
?>
是毫无意义的复杂化。
这并不适用于具有 __isset() 方法(这些方法会明显改变对象状态)或 __unset() 方法(这些方法不会正确检查其参数或具有额外的副作用)的对象的属性。
后一种情况意味着 __unset 不应该做超出其名称所描述的事情,并且还负责检查(可能使用 __isset())它被要求做的事情是否有意义。
前一种情况纯粹是不良设计。
如果你尝试销毁一个对象,请注意引用。
只有当所有引用都被取消设置后,对象才会释放其资源并触发其`__destruct`方法。
即使它们*在*对象内部……唉!
<?php
class A {
function __destruct() {
echo "cYa later!!\n";
}
}
$a = new A();
$a -> a = $a;
#unset($a); # 只需取消注释,你就会看到
echo "No Message ... hm, what now?\n";
unset($a -> a);
unset($a);
echo "Finally that thing is gone\n";
?>
当然,对象在脚本结束时会完全销毁。
由于`unset()`是一个语言结构,它不能传递除变量以外的任何内容。它的唯一目的是“取消设置”这个变量,即从当前作用域中移除它并销毁其关联的数据。这对于引用变量尤其如此,其中销毁的不是实际值,而是对该值的引用。这就是为什么不能将`unset()`包装在用户定义函数中的原因:如果参数按值传递,则会取消设置数据的副本;如果参数按引用传递,则只会取消设置函数作用域内的引用变量。对此没有解决方法,因为无法将“作用域”传递给PHP中的函数。这样的函数只能对存在于公共或全局作用域中的变量起作用(比较`unset($_GLOBALS[variable])`)。
我不知道PHP是如何内部处理垃圾回收的,但我猜想这种行为会导致巨大的内存泄漏:如果一个值变量超出作用域,而第二个变量仍然持有对内存中值的引用,那么取消设置该引用仍然会将该值保留在内存中,但可能会取消设置对该内存数据的最后一个引用,因此:占据的内存变得无用,因为你无法再引用它了。
一个如何从来自MySQL请求的数组结果中取消设置数组元素的示例。在这个示例中,它检查文件是否存在,如果不存在则从数组中移除该行。
<?php
$db->set_query("select * from documents where document_in_user = 0"); //1
$documents = $db->result_to_array($db->get_result()); //1
foreach ($documents as $key => $row) { //2
$file = "uploads/".rawurldecode($row['document_name']);
if ( file_exists ( $file ) == FALSE ) {
unset($documents[$key]); //3
}
}
$documents = array_values($documents); // 重新索引数组 (4)
?>
变量
MySQL表 = documents,
数组 = $documents
数组键(索引)= $key
数组行(某种记录)= $row
解释
1.
它从表(MySQL)中获取数组
2.
foreach 遍历数组 $documents
3.
如果记录不存在则取消设置
4.
`array_values($documents)` 重新索引 $documents 数组,否则当你的进程开始期望一个从键 ($key) 0(零)开始的数组时,你可能会遇到麻烦。
这是另一种使 `unset` 与函数中的会话变量一起工作的方法
<?php
function unsetSessionVariable ($sessionVariableName) {
unset($GLOBALS[_SESSION][$sessionVariableName]);
}
?>
希望它对其他人也有用……
F.
只有在 `register_globals` 为 'ON' 时才有效。
unset( $_SESSION['variable'] );
以上代码在 `register_globals` 打开时无效(只能在函数外部工作)。
$variable = $_SESSION['variable'];
unset( $_SESSION['variable'], $variable );
以上代码在 `register_globals` 打开且在函数内部时有效
为了阐明 hugo dot dworak at gmail dot com 关于取消设置尚未设置的内容的说明
取消设置数组中不存在的键不会抛出错误。
<?
$array = array();
unset($array[2]);
// 这不会抛出错误
unset($array[$undefinedVar]);
// 由于未定义的变量而抛出错误,而不是由于不存在的键。
?>
补充 bond at noellebond dot com 的说法,如果你想从数组末尾移除一个索引,如果你使用 `unset`,下一个索引值仍然是它本来应该的值。
例如,你拥有
<?php
$x = array(1, 2);
for ($i = 0; $i < 5; $i++)
{
unset($x[(count($x)-1)]); // 从数组中移除最后一个设置的键
$x[] = $i;
}
?>
你期望
Array([0] => 1, [1] => 4)
因为你想移除最后一个设置的键……
但你实际上得到
Array ( [0] => 1 [4] => 2 [5] => 3 [6] => 4 )
这是因为即使移除了最后一个键,自动索引仍然保留其先前值。
只有当你从末尾移除一个值时,这看起来才不正确。我猜不同的人会想要不同的方式。
解决方法是使用 `array_pop()` 代替 `unset()`,因为 `array_pop()` 会刷新数组的自动索引。
<?php
$x = array(1, 2);
for ($i = 0; $i < 5; $i++)
{
array_pop($x); // 从数组中移除最后一项
$x[] = $i;
}
?>
这将返回预期的 x 值:Array([0] => 1, [1] => 4);
希望这对某些需要这个的人有所帮助,我确实需要。
尽管进行了大量搜索,但我尚未找到关于如何手动释放PHP中变量(而不是对象)资源的解释。我还看到了许多关于`unset()`与将变量设置为null的优缺点的评论。因此,以下是比较对大量变量进行`unset()`与将其设置为null(关于内存使用和处理时间)的一些基准测试结果
10个变量
Unset
内存使用:296
耗时:1.0013580322266E-5
Null set
内存使用:1736
耗时:5.9604644775391E-6
50个变量
Unset
内存使用:296
耗时:3.6001205444336E-5
Null set
内存使用:8328
耗时:3.2901763916016E-5
100个变量
Unset
内存使用:296
耗时:5.6982040405273E-5
Null set
内存使用:15928
耗时:5.8174133300781E-5
1000个变量
Unset
内存使用:296
耗时:0.00041294097900391
Null set
内存使用:168096
耗时:0.00067591667175293
10000个变量
Unset
内存使用:296
耗时:0.0042569637298584
Null set
内存使用:1650848
耗时:0.0076930522918701
100000个变量
Unset
内存使用:296
耗时:0.042603969573975
Null set
内存使用:16249080
耗时:0.087724924087524
300000个变量
Unset
内存使用:296
耗时:0.13177299499512
Null set
内存使用:49796320
耗时:0.28617882728577
也许我的null set测试代码有缺陷,但尽管存在这种可能性,也很容易看出`unset()`对处理时间的的影响最小,并且没有明显的内存使用影响(除非`memory_get_usage()`返回的值有缺陷)。如果你真的关心在<50个变量上节省的约4微秒,那随你便。否则,使用`unset()`来最大限度地减少脚本对系统的影响。
注意:在Fedora 14上通过RPM安装的PHP 5.3.8上测试
至少在PHP 5.0.4中,可以从按引用传递给函数的数组中取消设置函数内部的数组元素。
但是,正如手册所暗示的那样,不能通过引用传递来取消设置整个数组。
<?php
function remove_variable (&$variable) // 传递变量引用
{
unset($variable);
}
function remove_element (&$array, $key) // 传递数组引用
{
unset($array[$key]);
}
$scalar = 'Hello, there';
echo '$scalar 的值为: ';
print_r ($scalar); echo '<br />';
// $scalar 的值为: Hello, there
remove_variable($scalar); // 尝试取消设置变量
echo '$scalar 的值为: ';
print_r ($scalar); echo '<br />';
// $scalar 的值为: Hello, there
$array = array('one' => 1, 'two' => 2, 'three' => 3);
echo '$array 的值为: ';
print_r ($array); echo '<br />';
// $array 的值为: Array ( [one] => 1 [two] => 2 [three] => 3 )
remove_variable($array); // 尝试取消设置数组
echo '$array 的值为: ';
print_r ($array); echo '<br />';
// $array 的值为: Array ( [one] => 1 [two] => 2 [three] => 3 )
remove_element($array, 'two'); // 成功地从数组中删除一个元素
echo '$array 的值为: ';
print_r ($array); echo '<br />';
// $array 的值为: Array ( [one] => 1 [three] => 3 )
?>
请注意,如果您尝试取消设置不存在的数组索引以及其父级也不存在,PHP 4 将生成警告。
示例
<?php
$foo = array();
unset($foo['Bar']['Baz']);
?>
结果:“Notice: Undefined index: Bar”
在 PHP 5 中,不会引发错误,这在我看来是正确的行为。
请注意,在上述示例中使用 unset($foo['Bar']) 在任何版本中都不会生成警告。
(在 4.4.9 和 5.2.4 上测试)
我在其他说明中找不到此澄清,但它似乎很重要,因此冒着重复的风险……
虽然全局变量(如整个数组)不会在函数内部取消设置,但数组的元素会被取消设置。所以
<?
function myfunction(){
Global $test;
unset($test[1]);
print_r($test);
unset($test)
}
$test = array(1,2,3);
myfunction();
print_r($test);
?>
产生
Array ( [0] => 1 [2] => 3 ) # 来自 myfunction 内部
Array ( [0] => 1 [2] => 3 ) # 从 myfunction 返回
这是我对略显枯燥的 unset 方法的变体。它在其中加入了一些 80 年代史泰龙动作片的元素。享受!
<?php
/**
* 函数 rambo(第一滴血)
*
* 完全彻底地摧毁一切,返回受害者的死亡人数
*
* @param 无所谓,这是兰博宝贝
* @return Integer 死亡人数(但少于 500 就没什么好说的了)
*/
function rambo() {
// 获取受害者并启动死亡人数状态
$victims = func_get_args();
$body_count = 0;
// 杀死那些该死的恶棍
foreach($victims as $victim) {
if($death_and_suffering = @unset($victim)) {
$body_count++;
}
}
// 兰博这次任务杀死了多少人?
return($body_count);
}
?>
只是为了确认,使用 UNSET 可以销毁整个数组。我在任何地方都找不到对此的参考,所以我决定写这个。
使用 unset 和使用 $myarray=array(); 来取消设置之间的区别在于,很明显,数组只会覆盖,并且仍然存在。
<?php
$myarray=array("Hello","World");
echo $myarray[0].$myarray[1];
unset($myarray);
//$myarray=array();
echo $myarray[0].$myarray[1];
echo $myarray;
?>
使用 unset 的输出是
<?
HelloWorld
Notice: Undefined offset: 0 in C:\webpages\dainsider\myarray.php on line 10
Notice: Undefined offset: 1 in C:\webpages\dainsider\myarray.php on line 10
使用 $myarray=array(); 的输出是
?>
<?
HelloWorld
Notice: Undefined offset: 0 in C:\webpages\dainsider\myarray.php on line 10
Notice: Undefined offset: 1 in C:\webpages\dainsider\myarray.php on line 10
Array
?>
dh at argosign dot de -
由于使用了 $GLOBALS 数组,因此可以在函数内部取消设置全局变量。
<?php
$x = 10;
function test() {
// 不需要执行 ' global $x; '
unset ($GLOBALS['x']);
echo 'x: ' . $GLOBALS['x'] . '<br />';
}
test();
echo "x: $x<br />";
// 结果将是
/*
x:
x:
*/
?>
在静态变量方面,文档并非完全清楚。它说
如果在函数内部取消设置静态变量,则 unset() 将销毁该变量及其所有引用。
<?php
function foo()
{
static $a;
$a++;
echo "$a\n";
unset($a);
}
foo();
foo();
foo();
?>
上述示例将输出
1
2
3
它确实如此!但是变量并没有被删除,这就是为什么值持续增加的原因,否则输出将是
1
1
1
引用在函数内部被销毁,此处理方式与全局变量相同,区别在于静态变量是局部变量。
请小心使用 unset 和静态值,因为输出可能与您预期的不同。似乎不可能销毁静态变量。您只能销毁当前正在执行函数中的引用,后续的静态语句将恢复引用。
如果文档这样说会更好
“如果在函数内部取消设置静态变量,则 unset() 将销毁对该变量的所有引用。”
示例:(在 PHP 4.3.7 上测试)
<?php
function foo()
{
static $a;
$a++;
echo "$a\n";
unset($a);
echo "$a\n";
static $a;
echo "$a\n";
}
foo();
foo();
foo();
?>
将输出
1
1
2
2
3
3
关于数组的 unset
如果您取消设置最后一个数组成员
$ar[0]==2
$ar[1]==7
$ar[2]==9
unset ($ar[2])
在通过 $ar[]=7 添加新成员后,
您将获得
$ar[0]==2
$ar[1]==7
$ar[3]==7,
因此,unset 对内部数组计数器没有任何影响!!!
此外,我意识到,当对象被销毁时,它会关心销毁对象空间可见性中的变量,但不会关心局部可见性中的变量,请注意发现的模式。
<?php
class release_test{
private $buffer;
private $other_object;
public function __construct(){
$this->other_object=new other_object_class();
}
public function __destruct(){
//注意,为了释放资源,你必须始终释放类对象
unset($this->other_object);
}
public allocate_mem_A(){
$this->buffer=file("/tmp/bigfile");
}
public allocate_mem_B(){
$buffer=file("/tmp/bigfile");
}
public allocate_mem_C(){
$buffer=file("/tmp/bigfile");
unset($buffer);
}
public allocate_mem_D(){
$this->other_buffer=file("/tmp/bigfile");
}
}
//这不会导致资源问题
$A=new release_test();
$A->allocate_mem_A();
$A->__destruct();
unset($A);
//这会导致资源问题
$B=new release_test();
$B->allocate_mem_B();
$B->__destruct();
unset($B);
//这不会导致资源问题
$C=new release_test();
$C->allocate_mem_C();
$C->__destruct();
unset($C);
//这不会导致资源问题
$D=new release_test();
$D->allocate_mem_D();
$D->__destruct();
unset($D);
?>
你可能需要释放所有已定义的变量,这里是一种方法
<?php
function unset_all_vars($a)
{ foreach($a as $key => $val)
{ unset($GLOBALS[$key]); }
return serialize($a); }
unset_all_vars(get_defined_vars());
?>
你也可以将“内存”的序列化变量保存起来,或许可以将其存储在临时文件中。如果你处理文本文件和/或文件上传并且拥有非常大的变量时,这非常有用。
致意
警告!
当从数组中释放时,如果你释放所有元素,数组始终会被设置
$tab=array('A'=>1,'B'=>2);
unset($tab['A']);
unset($tab['B']);
echo isset($tab)." ".count($tab);
输出:1 0
关于之前这些笔记中关于什么原因导致 unset() 在释放不存在的变量时触发通知的一些混淆……
释放不存在的变量,例如
<?php
unset($undefinedVariable);
?>
不会触发“未定义变量”的通知。但是
<?php
unset($undefinedArray[$undefinedKey]);
?>
会触发两个通知,因为这段代码用于释放数组的元素;$undefinedArray 和 $undefinedKey 本身并没有被释放,它们仅仅被用来定位应该被释放的内容。毕竟,如果它们确实存在,你仍然期望它们之后都还在。你不会希望你的整个数组仅仅因为你释放了它的一个元素而消失!
与其使用 unset 函数注销你的会话或其他数组值,你也可以使用这个小功能,只需一行代码即可完成此任务。
假设,如果你想注销你的会话存储值。
你可以使用
$_SESSION = array();
这个语法比逐个释放每个值节省了大量时间。
有时你需要在循环(if、while、foreach 等)中为数组索引赋值,但你希望将起始索引键设置为大于零的某个数字(例如 5)。一种实现方法是
<?php
$values = array(5, 10, 15, 100); //我们希望添加到新数组的值数组
$myArray = array(4=>0); //将起始键设置为 4 并赋值(例如 0)
unset($myArray[4]); //删除此索引键,但保留进一步的枚举
foreach($values as $value){
$myArray[] = $value; //为我们的数组赋值
}
print_r($myArray);
/* 输出:
Array ( [5] => 5 [6] => 10 [7] => 15 [8] => 100 )
*/
?>
两种在数组中释放值的方法
<?php
# 按键移除:
function array_remove_key ()
{
$args = func_get_args();
return array_diff_key($args[0],array_flip(array_slice($args,1)));
}
# 按值移除:
function array_remove_value ()
{
$args = func_get_args();
return array_diff($args[0],array_slice($args,1));
}
$fruit_inventory = array(
'apples' => 52,
'bananas' => 78,
'peaches' => 'out of season',
'pears' => 'out of season',
'oranges' => 'no longer sold',
'carrots' => 15,
'beets' => 15,
);
echo "<pre>原始数组:\n",
print_r($fruit_inventory,TRUE),
'</pre>';
# 例如,甜菜和胡萝卜不是水果……
$fruit_inventory = array_remove_key($fruit_inventory,
"beets",
"carrots");
echo "<pre>移除键后的数组:\n",
print_r($fruit_inventory,TRUE),
'</pre>';
# 我们也移除“季节性缺货”和“不再销售”的水果……
$fruit_inventory = array_remove_value($fruit_inventory,
"out of season",
"no longer sold");
echo "<pre>移除值后的数组:\n",
print_r($fruit_inventory,TRUE),
'</pre>';
?>
如果键是字符串,则不能释放数组的数字键。请参见此示例
// 创建一个包含 3 种不同键类型的简单数组
$test[1] = array(
10 => array('apples'),
"20" => array('bananas'),
'30' => array('peaches')
);
$test[2] = (array) json_decode(json_encode($test[1]));
$test[3] = (array) (object) $test[1];
// 从 stdClass 对象中创建数组
$testClass = new stdClass();
$testClass->{10} = array('apples');
$testClass->{"20"} = array('bananas');
$test[4] = (array) $testClass[6];
echo "<pre>";
foreach($test as $testNum => $arr) {
echo "\nTest: " . $testNum . " \n";
var_dump($arr);
foreach($arr as $key => $fruit) {
echo "key: " . $key . "\n";
echo "key exists: ";
var_dump(array_key_exists(strval($key), $arr));
echo "key 的类型是: " . gettype($key) . "\n";
unset($arr[$key]);
}
var_dump($arr);
echo "\n" . str_repeat("-", 80);
}
echo "</pre>";
以下是输出
Test: 1
array(3) {
[10]=>
array(1) {
[0]=>
string(6) "apples"
}
[20]=>
array(1) {
[0]=>
string(7) "bananas"
}
[30]=>
array(1) {
[0]=>
string(7) "peaches"
}
}
key: 10
key exists: bool(true)
key 的类型是:integer
key: 20
key exists: bool(true)
key 的类型是:integer
key: 30
key exists: bool(true)
key 的类型是:integer
array(0) {
}
--------------------------------------------------------------
Test: 2
array(3) {
["10"]=>
array(1) {
[0]=>
string(6) "apples"
}
["20"]=>
array(1) {
[0]=>
string(7) "bananas"
}
["30"]=>
array(1) {
[0]=>
string(7) "peaches"
}
}
key: 10
key exists: bool(false)
键的类型是:字符串
key: 20
key exists: bool(false)
键的类型是:字符串
key: 30
key exists: bool(false)
键的类型是:字符串
array(3) {
["10"]=>
array(1) {
[0]=>
string(6) "apples"
}
["20"]=>
array(1) {
[0]=>
string(7) "bananas"
}
["30"]=>
array(1) {
[0]=>
string(7) "peaches"
}
}
--------------------------------------------------------------
测试:3
array(3) {
[10]=>
array(1) {
[0]=>
string(6) "apples"
}
[20]=>
array(1) {
[0]=>
string(7) "bananas"
}
[30]=>
array(1) {
[0]=>
string(7) "peaches"
}
}
key: 10
key exists: bool(true)
key 的类型是:integer
key: 20
key exists: bool(true)
key 的类型是:integer
key: 30
key exists: bool(true)
key 的类型是:integer
array(0) {
}
--------------------------------------------------------------
测试:4
数组(2) {
["10"]=>
array(1) {
[0]=>
string(6) "apples"
}
["20"]=>
array(1) {
[0]=>
string(7) "bananas"
}
}
key: 10
key exists: bool(false)
键的类型是:字符串
key: 20
key exists: bool(false)
键的类型是:字符串
数组(2) {
["10"]=>
array(1) {
[0]=>
string(6) "apples"
}
["20"]=>
array(1) {
[0]=>
string(7) "bananas"
}
}
--------------------------------------------------------------
通过重建数组来修复问题
$oldArray = $array();
$array = array();
foreach($oldArray as $key => $item) {
$array[intval($key)] = $item;
}
在 PHP 5.1.6 上观察到,在方法内部使用 <?php unset($this); ?> 将会移除该方法中对 $this 的引用。就 unset() 而言,$this 不被认为是“特殊的”。
您可以 unset 超全局变量,例如 $GLOBALS、$_GET 等,但这会导致异常行为(自 PHP 5.3.3 起)。
1) 超全局变量的 unset 操作是全局的,即在函数内部进行 unset 操作会全局生效。
2) 可以重新创建已 unset 的超全局变量(重新创建的变量是超全局变量),但原始功能(在 $GLOBALS、$_SESSION 中…)已丢失。
<?php
function foo(){
unset($GLOBALS);
}
function bar(){
var_dump($GLOBALS);
}
foo();
bar(); //会引发 E_NOTICE ($GLOBALS 未定义)
$GLOBALS=3;
bar(); //显示 int(3)
?>
<?php
$list = ['a', 'b', 'c'];
next($list);
unset($list[1]);
echo current($list); // 结果:"c"