为什么 echo 很特殊

image.png(19.79 KB)

Parse error: syntax error, unexpected 'echo' (T_ECHO) in /www/app1/public/test.php on line 20


若把最后一行改成 vardump,print_f  ||  function() 等 函数形式就行  echo echo()等不可以
回复列表(52|隐藏机器人聊天)
  • @Ta / 2022-08-22 / /

    echo是一个没有返回值的语言结构,就算加括号也不能改变这一点。

    因为没有返回值,所以无法用于a || b表达式。这和函数不同,函数总是有返回值,函数不加return的情况下只是返回null,不像echo完全没有返回值。因为完全没有返回值,所以任何尝试捕获其返回值的表达式都会语法错误。

    要让a || echo 'xxx'成立,echo 'xxx'必须有返回值,因为该表达式的本义是求aecho 'xxx'的逻辑或值,而||的右侧不可求值,所以语法错误。

    想要用于a || b表达式,可以改用print(有返回值的语言结构)或printf(普通函数)。

    https://php.net/echo
    https://php.net/print
    https://php.net/printf

    $a['k1'] || print('ok');
    

    从上面可以看出,PHP中的a || b表达式并不是if (!a) b的良好替代品,因为这么用完全依赖语句的“副作用”,是当a为假时,需要对b求值,所以才执行了b的代码。而如果b不可求值,代码就会语法错误。

  • @Ta / 2022-08-22 / /

    估计是和if之类同级别的某种语法吧,而不是函数。在语法分析里会有单独的分支。

    感觉早期编程语言容易出现特殊语法。比如:

    Visual Basic

    Mid(Str, 2, 3) = "替换"                                    ' 将 Str 第 2 ~ 4 个字符替换为“替换”
    Name "C:\旧路径\旧文件.txt" As "C:\新文件.txt"              ' 移动文件
    Open "C:\文件.bin" For Binary Access Read Lock Read As #1  ' 二进制模式独占打开文件
    Print Spc(3); "hello"; Tab(15); ; ; ; "world"              ' 空 3 格,输出 hello,再定位至 15 列,输出 world
    PSet (15, 20), &HFF                                        ' 在 x = 15, y = 20 处画个红色的点
    Line (15, 20)-(30, 45), vbBlue                             ' 画条蓝色的线
    Line (15, 20)-Step(10, 10), vbGreen, B                     ' 在 x = 15, y = 20 处,画个绿色的,10 x 10 的框框
    Line (15, 20)-Step(10, 10), vbGreen, BF                    ' 在 x = 15, y = 20 处,画个绿色的,10 x 10 的实心框框
    

    SQL

    SELECT CAST(... AS ...),
           DATE_ADD(..., INTERVAL ... DAY),
           EXTRACT(DAY_MINUTE FROM ...),
      FROM JSON_TABLE(
             ...,
             "$[*]"
             COLUMNS(
               rowid FOR ORDINALITY,
               a VARCHAR(10) PATH "$.a" DEFAULT '123' ON EMPTY DEFAULT '456' ON ERROR,
               b JSON PATH "$.b" DEFAULT '{"key": 789}' ON EMPTY,
               c INT EXISTS PATH "$.c"
             )
           ) AS tt;
    
  • @Ta / 2022-08-22 / /

    echo具有返回值从而可以用于a || b表达式并不难(print就可以),但是会影响性能。所以PHP选择保持echo无返回值(于是不能用于求值表达式,比如a || ba && b),并且添加了单独的print语言结构用于这类场景。

    print语言结构之所以“总是返回1”,就是因为它的应用场景要求它必须有一个返回值,而且最好是真值。

    如果print返回0,以下代码不能按预期运行:

    do() && print('ok') || print('fail');
    

    https://php.net/print

  • @Ta / 2022-08-22 / /

    @老虎会游泳,我上次生成2亿行数字,用的好像是echo吧,感觉也不是很快啊

    PythonC正常的print/printf速度也不慢

    感觉没必要做成特殊语法结构

  • @Ta / 2022-08-22 / /

    @无名啊,不,你用的是printf。如果你用echo,可能会快很多。

    <?php
    mt_srand(strtotime('2022-08-11 20:00:00'));
    
    foreach ([STDOUT, STDERR] as $fp)
                for ($uid = 1; $uid <= 10000000; ++$uid)
                                for ($i = mt_rand(15, 25); $i > 0; --$i)
                                                    fprintf($fp, "%d,%d\n", $uid, mt_rand(0, 255));
    
  • @Ta / 2022-08-22 / /

    @老虎会游泳,我试试用echo

  • @Ta / 2022-08-22 / /

    @老虎会游泳,也不知道Visual Basic怎么做的词法分析,有些关键词可做变量名,有些不可
    无标题.png(137.36 KB)

  • @Ta / 2022-08-22 / /

    @无名啊,测试结果很有趣,echo比printf慢,多参数echo的性能取决于参数个数。

    Screenshot_20220822_153907.jpg(407.95 KB)

    单参数echo也比print慢。

    Screenshot_20220822_154318.jpg(284.58 KB)

    还有,终端是有瓶颈的,如果不重定向到文件,一分钟也完不成。

  • @Ta / 2022-08-22 / /

    @老虎会游泳

    测试源码

    <?php
    mt_srand(strtotime('2022-08-11 20:00:00'));
    
    for ($uid = 1; $uid <= 10000000; ++$uid)
        for ($i = mt_rand(15, 25); $i > 0; --$i)
            ...
    

    echo $uid . ',' . mt_rand(0, 255) . "\n";

    $ time php main.php > /dev/null
    
    real    8m41.194s
    user    2m50.391s
    sys     5m50.391s
    

    printf($uid . ',' . mt_rand(0, 255) . "\n");

    $ time php main.php > /dev/null
    
    real    9m39.083s
    user    3m15.938s
    sys     6m23.078s
    

    PHP版本

    $ php --version
    PHP 8.1.5 (cli) (built: May 16 2022 17:15:25) (NTS)
    Copyright (c) The PHP Group
    Zend Engine v4.1.5, Copyright (c) Zend Technologies
        with Zend OPcache v8.1.5, Copyright (c), by Zend Technologies
        with Xdebug v3.1.2, Copyright (c) 2002-2021, by Derick Rethans
    
  • @Ta / 2022-08-22 / /

    @老虎会游泳,上面有点乱,重新写成个shell,减少数据量来测试:

    结论

    1. 单参数echo和有格式化参数的printf几乎一样快
    2. printf直接输出稍慢一点点儿 (扫描参数字符串导致的慢?)
    3. 多参数echo非常慢,4倍耗时左右

    测试结果

    <echo $uid . ',' . mt_rand(0, 255) . "\n">
    2000232 18919812
    
    real    0m5.687s
    user    0m1.984s
    sys     0m9.078s
    
    <echo $uid , ',' , mt_rand(0, 255) , "\n">
    2000232 18919812
    
    real    0m22.305s
    user    0m8.547s
    sys     0m35.875s
    
    <printf($uid . ',' . mt_rand(0, 255) . "\n")>
    2000232 18919812
    
    real    0m5.817s
    user    0m2.484s
    sys     0m8.563s
    
    <printf("%d,%d\n", $uid, mt_rand(0, 255))>
    2000232 18919812
    
    real    0m5.694s
    user    0m2.172s
    sys     0m9.000s
    

    bash源码

    while read -r code; do
    
        echo $'\n'"<$code>"
        time php <<-EOF | wc -lc
            <?php
            mt_srand(strtotime('2022-08-11 20:00:00'));
    
            for (\$uid = 1; \$uid <= 100000; ++\$uid)
                for (\$i = mt_rand(15, 25); \$i > 0; --\$i)
                    $code;
    EOF
    
    done <<-'EOF'
        echo $uid . ',' . mt_rand(0, 255) . "\n"
        echo $uid , ',' , mt_rand(0, 255) , "\n"
        printf($uid . ',' . mt_rand(0, 255) . "\n")
        printf("%d,%d\n", $uid, mt_rand(0, 255))
    EOF
    
  • @Ta / 2022-08-22 / /

    @无名啊,我这边printf("%d,%d\n", $uid, mt_rand(0, 255))是最快的。搞不懂。

    PHP 8.1.6 (cli) (built: Jun 7 2022 04:09:29) (NTS)

  • @Ta / 2022-08-22 / /

    @老虎会游泳,结论差不多,反正echo存在的必要性看来是很低了

  • @Ta / 2022-08-22 / /

    @无名啊,目前只测了CLI,在web中的情况可能不同。

  • @Ta / 2022-08-22 / /

    @老虎会游泳,你这啥手机啊,比我笔电快一倍不止,搞得笔电一点尊严都没有,好歹也是个 (低压) 8 代 i5 啊

  • @Ta / 2022-08-22 / /

    @无名啊,没什么不同,还是printf最快

    Screenshot_20220822_162523_com.termux.jpg(588.56 KB)

  • @Ta / 2022-08-22 / /

    @无名啊,你的终端有瓶颈吧,你是不是在使用Windows?Windows控制台宿主(conhost.exe)的终端行处理很慢。

    Screenshot_20220822_162759_com.termux.jpg(443.20 KB)

  • @Ta / 2022-08-22 / /

    @老虎会游泳,我是wsl1

  • @Ta / 2022-08-22 / /

    @无名啊,那你试试WSL2。

  • @Ta / 2022-08-22 / /

    @老虎会游泳,手机试了试,确实速度快多了
    Screenshot_2022-08-22-16-34-17-151_com.termux.jpg(388.95 KB)

添加新回复
回复需要登录