使用php变量$_(美元符号后跟下划线)

17

如果在foreach($array as $key => $value)循环中,不需要使用$value变量,那么使用$_作为虚拟变量是否真的可行呢?除了PHP语法格式外,我没有找到任何有用的信息来证明这一点。

对于不需要在循环内使用值的情况,foreach循环有一个特殊的情况。 在这种情况下,会使用虚拟变量$_(下划线):

foreach ($GLOBALS['TCA'] as $table => $_) { // 使用$table做某些操作 }

出于性能原因,这样做比调用array_keys()并循环其结果要快。


3
测试所需的时间比询问少。 - user557846
你尝试过 foreach(array_keys($GLOBALS['TCA']) as $table) 吗?虽然只使用未使用的值变量可能更节省资源。 - Powerlord
我实际上有一个 array($key => $arrayforlateruse, ... , ...)$arrayforlateruse 是现在不需要的变量。现在我知道用 $_ 替换它没有影响。 - BalticMusicFan
让我希望PHP有一个惯例,用于像这种情况下使用$_,其中必须提供一个变量,但不会被使用 - 类似于Go,但在Go中,未使用的变量会导致错误; PHP不必那么严格,但使用$_可以向语法检查器和IDE发出信号,表明变量是有意不使用的,并且它们可以为此变量禁用“未使用的变量”警告。 - rob74
4个回答

22
"_" 是一个有效的变量名字符,因此您可以像使用任何其他变量一样使用它,并且没有特殊意义;这不是 Perl。
<?php
    $_ = "Hello";
    $__ = "World";
    $___ = "foo";

    print "{$_}, {$__}, {$___}\n";
?>

预期输出将是"Hello, World, foo"。此外,

foreach ( [ 'a' => 'Alpha', 'b' => 'Beta', 'c' => 'Gamma' ] as $letter => $_ ) {
    print $letter;
}
print $_;

将输出"abcGamma",显示$_变量在foreach后仍然保持定义;它不是某种奇怪的“本地”变量。

至于性能,我不认为这会有太大的影响,但这取决于你。相反,我建议不要使用全局变量,以避免污染全局范围。

随机测试和牢骚

注:我认为需要最新版本的PHP

欢迎纠正/增加/建议改进

define('INNER_LOOP', 10000);
define('OUTER_LOOP', 10);

$TCA    = [
    'customers' => '',
    'relations' => '',
    'invoices'  => '',
    'books'     => '',
    'parts'     => '',
    'records'   => '',
    'calories'  => '',
    'bounties'  => '',
    'cats'      => '',
    'cowabunga' => '',
    'amenities' => '',
];

$tests  = [
    "foreach access to global" => function() {
        global $TCA;
        for ($i = 0; $i < INNER_LOOP; $i++) {
            foreach ($TCA as $table => $_) {
                $t = $table . 'x';
            }
        }
    },
    "foreach access to GLOBALS" => function() {
        for ($i = 0; $i < INNER_LOOP; $i++) {
            foreach ($GLOBALS['TCA'] AS $table => $_) {
                $t = $table . 'x';
            }
        }
    },
    "passing parameter" => function($TCA) {
        for ($i = 0; $i < INNER_LOOP; $i++) {
            foreach ($TCA AS $table => $_) {
                $t = $table . 'x';
            }
        }
    },
    "passing parameter and array_keys" => function($TCA) {
        $keys = array_keys($TCA);
        for ($i = 0; $i < INNER_LOOP; $i++) {
            foreach ($keys AS $table) {
                $t = $table . 'x';
            }
        }
    },
    "walking passed parameter w/lambda" => function($TCA) {
        for ($i = 0; $i < INNER_LOOP; $i++) {
            array_map(
                function($table) {
                    $t = $table . 'x';
                },
                array_keys($TCA)
            );
        }
    },
    "walking passed parameter w/ anon func" => function($TCA) {
        $handler = function($table) {
                    $t = $table . 'x';
                };
        $keys = array_keys($TCA);
        for ($i = 0; $i < INNER_LOOP; $i++) {
            array_map($handler, $keys);
        }
    },


];

function timeFunc($function, $obj) {
  $time   = microtime(true);
  for ($i = 0; $i < OUTER_LOOP; $i++) {
    $function($obj);
  }
  return (microtime(true) - $time);
}

foreach ($tests as $name => $test) {
    print "$name: " . timeFunc($test, $TCA) . "\n";
    flush();
}

这是我的结果,经过格式化和排序:
- passing parameter and array_keys:      0.04573917388916
- foreach access to global:              0.067629098892212
- passing parameter:                     0.08098292350769
- foreach access to GLOBALS:             0.082289934158325
- walking passed parameter w/ anon func: 1.6233508586884
- walking passed parameter w/lambda:     1.6796138286591

需要注意的是:最快和最慢之间相差大约40倍。但是,在超过十万次调用的情况下,速度差距只有1.63秒,这意味着较快版本和较慢版本之间单次调用的时间差为16.3微秒。
因此,如果其中一种版本表现出能够节省你每年五分钟头痛、解决错误或客户支持的优点,那么选择那个版本很可能是值得投资的。
另一方面,如果你真的需要多次调用(例如数十亿次),使得那些微不足道的微秒时间总和值得处理,那么你最好花些时间将代码段移植到一种本质上更快或可以实现大规模并行化的语言中——也许是C语言,或者Erlang;或者重新思考架构(例如守护进程来节省开销,使用存储过程将麻烦转移到RDBMS中,缓存结果等)。
PHP 7.2的更新:
以下是在较新的64位机器上运行PHP 7.2.19的结果:
passing parameter and array_keys        0.57718586921692
foreach access to global                0.65028595924377
passing parameter                       0.65098810195923
foreach access to GLOBALS               0.69678092002869
walking passed parameter w/ anon func   0.84391593933105
walking passed parameter w/lambda       1.0423438549042

请注意,最快和最慢之间的差距现在不到2倍;因此,“选择最清晰、最易理解的代码”的论点现在甚至更加强有力。

2
下面的测试表明,在这种情况下使用$_作为变量名似乎与使用任何其他变量名没有区别。该值仍存储在变量中。
$tmp = array(1=>"one", 2=>"two", 3=>"three", 4=>"four", 5=>"five");
foreach ($tmp as $num=>$_) {
        echo "num is $num; dummy is $_<br>";
}

2
基础知识
变量在 PHP 中由一个美元符号后跟变量名称表示。变量名区分大小写。
变量名称遵循 PHP 中其他标签的相同规则。有效的变量名称以字母或下划线开头,后面跟任意数量的字母、数字或下划线。
因此,$_ 只是一个任意的变量。使用 $_$value 没有什么区别,除了 $_ 只是一种传统的方式来表示在循环内实际上没有使用的值。
请注意,这个
$foo = array('a' => 1, 'b' => 2, 'c' => 3);
foreach ($foo as $key => $_) echo $_;

输出
123

2

正如其他人所说,$_ 是一个有效的变量名。尽管 $_ 在 Typo3 编码规范中被定义了,但实际上并没有使用它,而是需要一个值变量来处理 foreach 循环。为什么要循环值而不是键呢?这看起来有些 hackish。你可以把键当做值来获取。我会直接使用:

foreach (array_keys($GLOBALS['TCA']) as $table) {
    // Do something with $table
}

1
为什么要使用foreach,如果你不需要值呢?...但是你仍然在使用foreach - Powerlord
1
是的,我正在获取这个value的值。 - AbraCadaver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接