P粉7864325792023-07-24 11:19:34
虽然在函数作用域内定义的变量无法从外部访问,但这并不意味着在函数完成后就无法使用它们的值。PHP有一个广为人知的静态关键字,在面向对象的PHP中被广泛用于定义静态方法和属性,但需要记住静态关键字也可以在函数内部用于定义静态变量。
静态变量与在函数作用域内定义的普通变量不同,它在程序执行离开该作用域时不会丢失其值。让我们考虑以下使用静态变量的示例:
function countSheep($num) { static $counter = 0; $counter += $num; echo "$counter sheep jumped over fence"; } countSheep(1); countSheep(2); countSheep(3);
结果:
1 sheep jumped over fence 3 sheep jumped over fence 6 sheep jumped over fence
如果我们没有使用静态关键字来定义$counter变量,那么每次输出的值都将与传递给函数的$num参数相同。使用静态关键字可以构建这个简单的计数器,而无需额外的解决方案。
静态变量仅存在于局部函数作用域中。它无法在定义它的函数之外访问。因此,您可以确保它的值在下一次调用该函数之前保持不变。
静态变量只能定义为标量或标量表达式(自PHP 5.6起)。将其他值分配给它必然会导致至少在撰写本文时发生错误。然而,您可以在代码的下一行中这样做:
function countSheep($num) { static $counter = 0; $counter += sqrt($num);//imagine we need to take root of our sheep each time echo "$counter sheep jumped over fence"; }
结果:
2 sheep jumped over fence 5 sheep jumped over fence 9 sheep jumped over fence
静态函数在同一类的对象方法之间是一种“共享”的机制。通过查看以下示例,可以很容易理解:
class SomeClass { public function foo() { static $x = 0; echo ++$x; } } $object1 = new SomeClass; $object2 = new SomeClass; $object1->foo(); // 1 $object2->foo(); // 2 oops, $object2 uses the same static $x as $object1 $object1->foo(); // 3 now $object1 increments $x $object2->foo(); // 4 and now his twin brother
这仅适用于同一类的对象。如果对象来自不同的类(即使它们相互扩展),静态变量的行为将如预期。
另一种在函数调用之间保持值的方式是使用闭包。闭包在PHP 5.3中引入。简单来说,它们允许您将某个变量集合的访问限制在函数作用域内的另一个匿名函数中,该匿名函数将是访问这些变量的唯一方式。在闭包中,变量可以模拟(或多或少成功)面向对象编程中的“类常量”(如果它们通过值传递给闭包)或“私有属性”(如果通过引用传递)的概念。
实际上,后者允许使用闭包来替代静态变量。使用哪种方式完全取决于开发者的决定,但值得一提的是,静态变量在处理递归时非常有用,并且值得开发者注意。
P粉5746952152023-07-24 09:30:10
变量有一个有限的"作用域",或者说"可以访问它们的位置"。仅仅因为你在应用程序的某个地方写了一次 $foo = 'bar'; 并不意味着你可以在应用程序的其他任何地方引用 $foo。变量 $foo 在特定的作用域内有效,只有在相同作用域内的代码才能访问该变量。
非常简单:PHP具有函数作用域。这是PHP中唯一存在的作用域分隔符。函数内部的变量只能在该函数内部使用。函数外部的变量可以在函数外的任何地方使用,但不能在任何函数内部使用。这意味着PHP中有一个特殊的作用域:全局作用域。在任何函数外部声明的变量都在全局作用域内。
<?php $foo = 'bar'; function myFunc() { $baz = 42; }
$foo
is in the global scope, $baz
is in a local scope inside myFunc
. Only code inside myFunc
has access to $baz
. Only code outside myFunc
has access to $foo
. Neither has access to the other:
<?php $foo = 'bar'; function myFunc() { $baz = 42; echo $foo; // doesn't work echo $baz; // works } echo $foo; // works echo $baz; // doesn't work
文件边界不会分隔作用域。
a.php
<?php $foo = 'bar';
b.php
<?php include 'a.php'; echo $foo; // works!
适用于包含的代码的规则与适用于任何其他代码的规则相同:只有函数可以分隔作用域。就作用域而言,您可以将包含文件视为复制和粘贴代码。
c.php
<?php function myFunc() { include 'a.php'; echo $foo; // works } myFunc(); echo $foo; // doesn't work!
在上面的示例中,a.php 被包含在 myFunc 内部,a.php 中的任何变量只具有局部函数作用域。它们在 a.php 中看起来处于全局作用域,并不意味着它们实际上就是全局作用域,这实际上取决于该代码被包含/执行的上下文。
每个新的函数声明引入一个新的作用域,就是这么简单。
function foo() { $foo = 'bar'; $bar = function () { // no access to $foo $baz = 'baz'; }; // no access to $baz }
$foo = 'foo'; class Bar { public function baz() { // no access to $foo $baz = 'baz'; } } // 无法访问 $baz。
处理作用域问题可能看起来很烦人,但有限的变量作用域对于编写复杂的应用程序至关重要!如果你在应用程序中声明的每个变量都可以从任何地方访问,你将无法跟踪变量的变化。你只能给变量取有限的合理名称,可能希望在多个地方使用变量"$name"。如果你的应用程序中只能有一个唯一的变量名,你将不得不使用非常复杂的命名方案,以确保变量的唯一性,并确保你不会从错误的代码片段更改错误的变量。
如下:
function foo() { echo $bar; }
如果没有作用域,上述函数会做什么?$bar是从哪里来的?它有什么状态?它是否被初始化?你是否每次都需要检查?这是不可维护的。这就引出了...
function foo($bar) { echo $bar; return 42; }
变量$bar明确作为函数参数传入该作用域。仅通过查看这个函数,就可以清楚地知道它所使用的值的来源。然后,它明确返回一个值。调用者可以确信函数将使用哪些变量以及返回值的来源:
$baz = 'baz'; $blarg = foo($baz);
$foo = 'bar'; $baz = function () use ($foo) { echo $foo; }; $baz();
匿名函数明确地从其周围的作用域中包含了$foo变量。请注意,这与全局作用域不同。
global
如前所述,全局作用域是特殊的,函数可以明确地从全局作用域中导入变量:
$foo = 'bar'; function baz() { global $foo; echo $foo; $foo = 'baz'; }
这个函数使用并修改全局变量$foo。不要这样做!(除非你真的真的真的真的知道自己在做什么,即使是这样:也不要这样做!)
调用这个函数的调用者只能看到这个:
baz(); // outputs "bar" unset($foo); baz(); // no output, WTF?! baz(); // outputs "baz", WTF?!?!!
没有任何迹象表明这个函数有任何副作用,但实际上它有。当一些函数不断修改和依赖于一些全局状态时,这很容易变成一个混乱的局面。你希望函数是无状态的,只对其输入进行操作并返回定义好的输出,而不管你调用它多少次。
尽可能避免在任何情况下使用全局作用域;特别是不应该将变量从全局作用域“提取”到局部作用域中。