【问题标题】:What is the scope of a PHP function defined within a PHP anonymous function?在 PHP 匿名函数中定义的 PHP 函数的范围是什么?
【发布时间】:2017-07-13 11:38:32
【问题描述】:

问题

如果我这样做:

$checkName = function ($value) use ($min, $max)  {
    function lengthTest($string, $min, $max)
    {
        $length = mb_strlen($string, 'UTF-8');
        return ($length >= $min) && ($length <= $max);
    }
};

1) 它是合法的 PHP 吗?还有……

2) 函数lengthTest() 是在全局命名空间中,还是仅限于$checkName Closure 对象?那么它会是一个私人成员吗?

3) lengthTest() 可以这样引用为filter_var_array() 的回调方法吗?

$filterInstructionsArray [
    'fName'   => ['filter' => FILTER_CALLBACK],
    'flags'   => FILTER_REQUIRE_SCALAR,
    'options' => [$checkName, 'lengthTest']]
];

4) lengthTest 可以像这样引用为filter_var_array() 的回调函数吗?

$filterInstructionsArray [
    'fName'   => ['filter' => FILTER_CALLBACK],
    'flags'   => FILTER_REQUIRE_SCALAR,
    'options' => 'lengthTest']
];

参考文献

PHP 手册中关于user defined functions 的内容如下:

任何有效的 PHP 代码都可能出现在函数中,甚至是其他函数 和类定义。 ... PHP 中的所有函数和类都有 全局作用域——它们可以在函数外部调用,即使它们是 在内部定义,反之亦然。

PHP 手册中关于anonymous functions 的内容如下:

匿名函数,也称为闭包,允许创建 没有指定名称的函数。它们是最有用的 回调参数的值,但它们还有许多其他用途。闭包 也可以用作变量的值; PHP 自动 将此类表达式转换为闭包内部的实例 班级。将闭包分配给变量使用与任何相同的语法 其他分配,包括结尾的分号:

感谢您抽出宝贵时间阅读、思考并回答我的问题。非常感谢。

【问题讨论】:

  • 其实发帖前应该自己做点努力。无论哪种方式,请记住,当您在函数中定义函数时,最好将内部函数包装在 if (!function_exists('xyz')) 中,因为如果再次调用外部函数 get 会出错。
  • 请给别人一个回答的机会。不要关闭这个问题。

标签: php function scope anonymous-function


【解决方案1】:

指节裂开

从技术上讲,语法是“正确的”(它不会产生致命错误),但 PHP 的语义使其在当前形式下实际上毫无意义。我们先来看几件事情,即 PHP 如何处理命名函数对变量的赋值:

php > echo shell_exec("php -v");
PHP 5.4.16 (cli) (built: Oct 30 2018 19:30:51)
Copyright (c) 1997-2013 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2013 Zend Technologies

php > function speak($arg) {echo "{$arg}\n";}
php > function give($arg) {return $arg;}
php > $speak = speak(4);
4
php > $give = give(4);
php > var_dump($speak);
NULL
php > var_dump($give);
int(4)

函数本身在赋值时执行,并将其返回值(NULL 或其他)赋值给变量。由于我们只是分配函数执行的返回值,因此尝试将此变量用作函数名是没有用的:

php > $speak(4);
php > $give(4);
php >

让我们将其与匿名函数的赋值(也称为“闭包”)进行对比:

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php {   echo "value: {$value}\n";
php {   echo "min: {$min}\n";
php {   echo "max: {$max}\n";
php { };
php > var_dump($checkName);
object(Closure)#1 (2) {
  ["static"]=>
  array(2) {
    ["min"]=>
    int(1)
    ["max"]=>
    int(6)
  }
  ["parameter"]=>
  array(1) {
    ["$value"]=>
    string(10) "<required>"
  }
}

与其他一些语言不同,闭包在 PHP 中由一个实际的对象表示。 'use' 子句中的变量是在闭包创建时导入的;当闭包被调用时,函数参数(即$value)的被捕获(因此我们将其视为必需参数而不是静态值)。闭包中引用的语义现在不值得考虑,但如果您想进一步阅读,goatthis 问题的回答是一个很好的开始。

这里的主要内容是闭包对 $checkName 的赋值并没有执行闭包本身。相反,$checkName 变成了一种“别名”,我们可以用它来按名称引用这个函数:

php > $checkName("hello stackoverflow");
value: hello stackoverflow
min: 1
max: 6
php >

鉴于 PHP 对传递的函数参数的数量有多么松散,零参数执行会返回预期结果:

php > $checkName();
value:
min: 1
max: 6
php >

现在让我们再深入一点,在函数中定义一个函数:

php > function myOuterFunc($arg) {
php {   function myInnerFunc($arg){
php {     echo "{$arg}\n";
php {   }
php { }
php > $myVal = myOuterFunc("Hello stackoverflow");
php > var_dump($myVal);
NULL
php >

现在这个结果应该是有意义的。除非显式调用,否则函数不会执行;仅仅因为我们调用 myOuterFunc 并不意味着我们执行其中定义的任何函数代码。这并不是说我们不能:

php > function myOuterFunc($arg) {
php {   function myInnerFunc($arg){
php {     echo "{$arg}\n";
php {   }
php {   myInnerFunc($arg);
php { }
php > $myVal = myOuterFunc("Hello stackoverflow");
Hello stackoverflow
php > var_dump($myVal);
NULL
php >

这让我们回到本质上是您的问题:闭包内的命名函数怎么样?鉴于我们现在对函数执行的发现,我们可以生成一系列非常可预测的示例:

$min = 1; $max = 6;
$checkName = function ($value) use ($min, $max) {
  function question(){echo "How are you\n";}
  echo "value: {$value}\n";
  echo "min: {$min}\n";
  echo "max: {$max}\n";
};
php > $checkName("Hello stackoverflow");
value: Hello stackoverflow
min: 1
max: 6
php >

正如预期的那样,闭包内的命名函数的代码没有被执行,因为我们没有显式调用它。

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php {   function question(){echo "How are you\n";}
php {   echo "value: {$value}\n";
php {   echo "min: {$min}\n";
php {   echo "max: {$max}\n";
php {   question();
php { };
php > $checkName("Hello stackoverflow");
value: Hello stackoverflow
min: 1
max: 6
How are you
php >

显式调用内部函数就可以了,只要我们在调用它之前定义该函数:

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php {   echo "value: {$value}\n";
php {   echo "min: {$min}\n";
php {   echo "max: {$max}\n";
php {   question();
php {   function question(){echo "How are you\n";}
php { };
php > $checkName("Hello stackoverflow");
value: Hello stackoverflow
min: 1
max: 6
php >

php > $min = 1; $max = 6;
php > $checkName = function ($value) use ($min, $max) {
php {   echo "value: {$value}\n";
php {   echo "min: {$min}\n";
php {   echo "max: {$max}\n";
php {   function question(){echo "How are you\n";}
php {   question();
php { };
php > $checkName("Hello stackoverflow");
value: Hello stackoverflow
min: 1
max: 6
How are you
php >

那么就你的问题而言:

  1. 是的,它是合法的,您尝试的内容是可能的,但在当前形式下没有语义意义。

  2. 闭包内的任何命名函数绝对不驻留在全局命名空间中,它们在其定义闭包的范围内。 FWIW,术语“成员”通常是指类变量(通常在 PHP 中称为“属性”)。虽然闭包是一个对象,可以让您复制在类中找到的实例变量的功能,但不应以任何方式将它们与类混淆或解释。

3/4) 不是您尝试使用它的方式,不。闭包之外的任何东西都没有任何内部功能的概念;如果在调用您的闭包代码时,所述代码使用内部函数执行操作,那么它们将看到曙光。但是没有直接的方法来引用这些内部函数,就好像它们是在闭包范围之外定义的一样。

换句话说,执行这些内部函数的唯一方法是:a)该代码由闭包代码专门执行,b)您执行该闭包代码。

希望这会有所帮助。

【讨论】:

  • 我前段时间问过这个问题。这更像是一个思想实验,而不是我真正想做的事情。为你的勤奋喝彩。没有多少人有兴趣或精力去制定一个像样的解决方案。
  • 这是一个很好的问题,让我也思考了问题,所以依次感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-20
  • 2014-11-07
  • 1970-01-01
  • 2019-03-31
  • 2013-02-09
相关资源
最近更新 更多