如何在PHP中在块外不可见的块内声明局部变量


How to declare local variable inside a block which is invisible outside the block in PHP?

我是PHP的新手,已经在Perl/C/Scheme方面有了一些经验,我发现我不知道如何在块内定义变量,例如if/for/while,并使其在块外不可见。

我必须将块中的代码放入函数中才能使其成为本地代码吗?

例如,我的很多错误都是由以下代码引起的:

<?php
    for($id = 0; $id<10; $id++)
    {
        $a = $id;
    }
    if(1)
    {
        $b = 3;
    }
    echo $a;//9
    echo $id;//10;
    echo $b;//3
?>

然而,我perl,这样的代码将是安全的:

#!/usr/bin/perl
use 5.014;
use strict;
use warnings;
for(my $id = 0; $id < 10; $id++)
{
    my $a = $id;
}
if(1)
{
    my $b = 3;
}
#say $a; #error
#say $b; #error
#say $id; #error

在C中,这样的代码会导致错误(使用-std=gnu99编译)

#include<stdio.h>
int main()
{
    for(int i=0; i<10; i++)
    {
        int a = i;
    }
    if(1)
    {
        int b = 3;
    }
    //printf("%d'n",i);//err
    //printf("%d'n",a);//err
    //printf("%d'n",b);//err
    return 0;
}

那么,由于PHP缺少块范围,我该如何避免错误呢?

几种可能的方法,但它们几乎都可以归结为:将代码分解为函数。不管怎样,一切都回到了那个问题上。不管PHP缺少块作用域,对于可维护和可重用的代码,无论如何都应该这样做。

  1. 使用方法尽可能小且自包含的类
  2. 使用尽可能小且独立的功能
  3. 切勿使用global
  4. 不要编写长的过程代码,不管它是否在函数中。如果一个作用域比一个代码屏幕长,那么它很可能太长,并且可能会被分解
  5. 使用合理的描述性变量名,在每个作用域的顶部初始化它们,不要在整个作用域中将它们用于不同的目的
  6. 更多地考虑映射操作、迭代器和回调,例如:

    // Look ma, no $i or other superfluous iterator variables!
    $foo = array_map(function ($bar) { return /* something */; }, $baz);
    $foo = array_reduce($bar, function ($foo, $baz) { return /* something */; });
    $files = new RecursiveIteratorIterator(
        new RecursiveCallbackFilterIterator(
            new RecursiveDirectoryIterator(__DIR__),
            function ($file) { return /* something */; }
        )
    );
    foreach ($files as $file) {
        /* A lot of code that would have gone here is in the
           RecursiveCallbackFilterIterator callback now. */
    }
    

一般来说,你现在看到的最明智的PHP代码广泛使用依赖注入的OOP,并且由许多小类和许多小方法组成。这最大限度地提高了代码的可重用性、灵活性并减少了问题。

作为一种丑陋的破解方法,您可以通过模拟IIFE的常见Javascript实践来人为地引入作用域:

$foo = call_user_func(function () {
    /* your variables here */
    return $result;
});

这可能适用于一次性脚本,这些脚本本质上只是长的和过程性的,但您仍然希望按范围进行隔离。不过,我一般不会真的推荐这种做法。

在每个块的末尾使用unset($var);

这在PHP中很常见,我个人有很多与这个问题有关的错误。

一种方法是在块的末尾取消set()变量。所以在你的代码中做一些类似的事情

<?php
    for($id = 0; $id<10; $id++)
    {
    $a = $id;
    }
    if(1)
    {
        $b = 3;
        unset($b);
    }
    echo $a;//9
    echo $id;//10;
    echo $b;//unset notice
?>

或在使用变量之前为其指定null。