PHP 匿名函数中定义的 PHP 函数的作用域是什么?

What is the scope of a PHP function defined within a PHP anonymous function?

问题

如果我这样做:

$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 的描述如下:

Any valid PHP code may appear inside a function, even other functions and class definitions. ... All functions and classes in PHP have the global scope - they can be called outside a function even if they were defined inside and vice versa.

PHP 手册对 anonymous functions 的描述如下:

Anonymous functions, also known as closures, allow the creation of functions which have no specified name. They are most useful as the value of callback parameters, but they have many other uses. Closures can also be used as the values of variables; PHP automatically converts such expressions into instances of the Closure internal class. Assigning a closure to a variable uses the same syntax as any other assignment, including the trailing semicolon:

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

咬指关节

从技术上讲,语法是“正确的”(它不会产生致命错误)但 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)

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

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

让我们将其与匿名函数的赋值进行对比 (a.k.a.a 'Closure'):

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)在 called 时捕获它们的 values(因此我们将其视为必需参数而不是静态值)。闭包中引用的语义现在不值得考虑,但如果您想进一步阅读,goat's answer to this 问题是一个很好的开始。

这里的主要收获是 Closure 对 $checkName 的赋值并没有执行 Closure 本身。相反,$checkName 成为一种“别名”,我们可以使用名称来引用此函数:

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

鉴于 PHP 对传递的函数参数的数量有多宽松,零参数执行 returns 预期结果:

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 Whosebug");
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 Whosebug");
Hello Whosebug
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 Whosebug");
value: Hello Whosebug
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 Whosebug");
value: Hello Whosebug
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 Whosebug");
value: Hello Whosebug
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 Whosebug");
value: Hello Whosebug
min: 1
max: 6
How are you
php >

那么对于你的问题来说:

  1. 是的,这是合法的,您尝试的是可能的,但在当前形式下在语义上毫无意义。

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

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

换句话说,让这些内部函数执行的唯一方法是 a) 该代码由 Closure 的代码专门执行并且 b) 您执行所述 Closure 代码。

希望这对您有所帮助。