PHP解析/语法错误;以及如何解决它们

每个人都会遇到语法错误。即使是有经验的程序员也会犯错别字。对于新人来说,这只是学习过程的一部分。然而,通常很容易解释错误消息,例如:

PHP解析错误:语法错误,在第20行index.php意外的{

意想不到的符号并不总是真正的罪魁祸首。但是行号给出了从哪里开始寻找的粗略想法。

始终查看代码上下文。语法错误通常隐藏在以前的代码行中提到的中。将您的代码与手册中的语法示例进行比较。

虽然不是每个案例都匹配另一个。然而有一些解决语法错误的一般步骤。 这些参考文献总结了常见的陷阱:

密切相关的参考资料:

还有:

虽然Stack Overflow也欢迎新手程序员,但它主要针对专业编程问题。

  • 回答每个人的编码错误和狭窄的拼写错误被认为大多是离题的。
  • 因此,在发布语法修复请求之前,请花时间关注基本步骤
  • 如果你仍然需要,请展示你自己的解决方案,尝试修复,以及你对看起来或可能是错误的思考过程。


最后,请考虑如果语法错误不是通过编辑代码库引发的,而是在外部供应商软件包安装或升级之后,它可能是由于PHP版本不兼容,因此请根据您的平台设置检查供应商的要求。

810683 次浏览

语法错误有哪些?

PHP属于c风格当务之急编程语言。它有严格的语法规则,当遇到错位的符号或标识符时无法恢复。它无法猜测你的编码意图。

函数定义语法摘要

最重要的提示

有一些基本的预防措施,你可以随时采取:

  • 使用正确的代码缩进,或采用任何崇高的编码风格。 可读性可以防止不规则行为。

  • 使用IDE或PHP编辑器语法高亮。 这也有助于括号/括号平衡。

    需要分号

  • 阅读手册中的的语言参考和示例。 两次,有点精通。

如何解释解析器错误

典型的语法错误消息如下:

解析错误:语法错误,意外的T_STRING,期待';'file.php线217

其中列出了语法错误的可能位置。参见提到的文件名行号

绰号(例如T_STRING)解释了解析器/标记器最终无法处理的符号。然而,这不一定是语法错误的原因。

研究以前的代码行也很重要。通常语法错误只是之前发生的意外。错误行号正是解析器最终放弃处理这一切的地方。

解决语法错误

有许多方法可以缩小和修复语法错误。

  • 打开提到的源文件。查看提到的代码行

    • 对于失控的字符串和错位的运算符,这通常是您找到罪魁祸首的地方。

    • 从左到右阅读这一行,想象每个符号的作用。

  • 您还需要更经常地查看前面的行

    • 特别是,在前一行结束/语句中缺少;分号。(至少从风格角度来看。)

    • 如果{代码块}关闭或嵌套不正确,您可能需要进一步调查源代码。使用适当的代码缩进来简化它。

  • 看看语法着色

    • 字符串、变量和常量都应该有不同的颜色。

    • 运算符+-*/.也应该是不同的。否则他们可能在错误的上下文中。

    • 如果你看到字符串着色扩展太远或太短,那么你发现了一个未转义或丢失的闭包"'字符串标记。

    • 两个颜色相同的标点符号相邻也可能意味着麻烦。通常,如果运算符后面不是++--或括号,则运算符是孤独的。两个字符串/标识符直接在一起在大多数上下文中是不正确的。

  • 怀特斯佩斯是你的朋友。 遵循任何编码风格。

  • 暂时打破长线。

    • 你可以自由地在运算符或常量和字符串之间添加换行符。然后解析器将具体化行号以分析错误。你可以隔离缺失或错位的语法符号,而不是查看非常冗长的代码。

    • 将复杂的if语句拆分为不同的或嵌套的if条件。

    • 使用临时变量来简化代码,而不是冗长的数学公式或逻辑链。(更具可读性=更少的错误。)

    • 添加换行符:

      1. 您可以轻松识别为正确的代码,
      2. 你不确定的部分,
      3. 以及解析器抱怨的行。

      分区长代码块真的有助于定位语法错误的来源。

  • 发表评论违规代码。

    • 如果您无法隔离问题源,请开始注释掉(从而暂时删除)代码块。

    • 一旦你摆脱了解析错误,你就找到了问题的根源。仔细看看那里。

    • 有时你想暂时删除完整的函数/方法块。(在不匹配的花括号和错误缩进的代码的情况下。)

    • 当您无法解决语法问题时,请尝试从零开始重写注释掉的部分。

  • 作为新手,请避免一些令人困惑的语法结构。

    • 三元? :条件运算符可以压缩代码,确实很有用。但它并不是在所有情况下都有助于易读性。

    • PHP的替代语法(if:/elseif:/endif;)对于模板很常见,但可以说比普通的{代码}块更不容易遵循。

  • 最常见的错误是:

    • 缺少用于终止语句/行的分号;

    • "'的字符串引号和其中未转义的引号不匹配。

    • 忘记的运算符,特别是用于字符串.连接。

    • 不平衡的(括号)。在报告行中计数它们。它们的数量相等吗?

  • 不要忘记,解决一个语法问题可以发现下一个语法问题。

    • 如果你解决了一个问题,但在下面的一些代码中出现了其他问题,那么你基本上是在正确的道路上。

    • 如果在编辑一个新的语法错误后出现在同一行中,那么您尝试的更改可能是失败的。(但并非总是如此。)

  • 如果无法修复,请恢复以前工作代码的备份。

    • 采用源代码版本控制系统。您始终可以查看损坏和最后一个工作版本的diff。这可能会启发语法问题是什么。
  • 不可见的杂散Unicode字符:在某些情况下,您需要在源代码上使用使用十六进制编辑器或不同的编辑器/查看器。仅通过查看您的代码无法发现某些问题。

  • 注意哪些换行符类型保存在文件中。

    • PHP只是尊重\n换行符,而不是\r回车。

    • 这偶尔会成为MacOS用户的问题(即使在OS X上配置错误的编辑器也是如此)。

    • 通常只有在使用单行//#注释时才会出现问题。当换行符被忽略时,多行/*...*/注释很少会干扰解析器。

  • 如果您的语法错误不会通过网络传输: 碰巧您的机器上有语法错误。但是在线发布相同的文件不再显示它。这只能意味着两件事之一:

    • 您正在查看错误的文件!

    • 或者您的代码包含不可见的流浪Unicode(见上文)。 您可以轻松找到:只需将代码从Web表单复制回文本编辑器即可。

  • 检查您的php版本。并非所有语法结构在每台服务器上都可用。

    • php -v用于命令行解释器

    • <?php phpinfo();用于通过Web服务器调用的那个。


    这些不一定是相同的。特别是在使用框架时,您会将它们匹配起来。

  • 不要使用PHP的保留关键字作为函数/方法、类或常量的标识符。

  • 试错是你最后的手段。

如果所有其他方法都失败了,您可以随时谷歌您的错误消息。语法符号不那么容易搜索(Stack Overflow本身由符号猎犬索引)。因此,在找到相关内容之前,可能需要再浏览几页。

更多指南:

死亡的白色屏幕

如果您的网站只是空白,那么通常是语法错误造成的。 启用它们的显示:

  • error_reporting = E_ALL
  • display_errors = 1

一般在php.ini中,或通过.htaccessmod_php, 甚至.user.ini与FastCGI设置。

在损坏的脚本中启用它为时已晚,因为PHP甚至无法解释/运行第一行。一个快速的解决方法是制作一个包装脚本,比如test.php

<?php
error_reporting(E_ALL);
ini_set("display_errors", 1);
include("./broken-script.php");

然后通过访问此包装脚本调用失败的代码。

它还有助于启用PHP的error_log并在脚本与HTTP 500响应崩溃时查看您的网络服务器的error.log

意想不到的T_VARIABLE

“意外的T_VARIABLE”意味着有一个文字$variable名称,它不适合当前的表达式/语句结构。

有意抽象/不精确运算符+$变量图

  1. 缺少分号

    它最常在前一行中表示缺少的分号。语句后面的变量赋值是一个很好的指示符:

            ⇓
    func1()
    $var = 1 + 2;     # parse error in line +2
    
  2. 字符串连接

    经常发生的事故是串连接与被遗忘的.操作符:

                                    ⇓
    print "Here comes the value: "  $value;
    

    顺便说一句,只要有助于易读性,您应该更喜欢串插值(双引号中的基本变量)。这避免了这些语法问题。

    字符串插值是脚本语言的核心功能。使用它并不羞耻。忽略任何关于变量.连接为更快的微优化建议。它不是。

  3. 缺少表达式运算符

    当然,同样的问题也可能出现在其他表达式中,例如算术运算:

                ⇓
    print 4 + 7 $var;
    

    如果变量应该被添加、减去或比较等,PHP不能在此处

  4. 列表

    语法列表也是如此,例如数组填充,其中解析器还指示预期的逗号,,例如:

                                           ⇓
    $var = array("1" => $val, $val2, $val3 $val4);
    

    或函数参数列表:

                                     ⇓
    function myfunc($param1, $param2 $param3, $param4)
    

    listglobal语句中,或者在for循环中缺少;分号时,您也会看到这种情况。

  5. 类声明

    此解析器错误也发生在类声明。您只能分配静态常量,而不能分配表达式。因此解析器抱怨变量作为分配的数据:

     class xyz {      ⇓
    var $value = $_GET["input"];
    

    不匹配的}结束花括号特别会导致这里。如果方法过早终止(使用适当的缩进!),那么杂散变量通常会错放到类声明主体中。

  6. 标识符后的变量

    你也永远不能直接拥有一个变量跟随一个标识符

                  ⇓
    $this->myFunc$VAR();
    

    顺便说一句,这是一个常见的示例,其目的可能是使用变量。在这种情况下,例如使用$this->{"myFunc$VAR"}();进行变量属性查找。

    请记住,使用变量变量应该是例外。新手经常试图过于随意地使用它们,即使数组会更简单、更合适。

  7. 语言构造后缺少括号

    匆忙打字可能会导致忘记开括号或闭括号 对于ifforforeach语句:

            ⇓
    foreach $array as $key) {
    

    解决方案:在语句和变量之间添加缺少的开头(

                           ⇓
    if ($var = pdo_query($sql) {
    $result = …
    

    花括号{不会打开代码块,而不会首先使用)右括号关闭if表达式。

  8. 其他不期望条件

         ⇓
    else ($var >= 0)
    

    解决方案:从else中删除条件或使用elseif

  9. 需要括号来结束

         ⇓
    function() use $var {}
    

    解决方案:在$var周围添加括号。

  10. 不可见的空格

    正如参考答案中提到的“隐形杂散Unicode”(例如不间断空间),您可能还会看到此错误用于毫无戒心的代码,例如:

    <?php
    ⇐
    $var = new PDO(...);
    

    它在文件开头和复制粘贴代码中相当普遍。如果您的代码在视觉上看起来不包含语法问题,请使用六进制编辑器检查。

另见

意想不到的T_STRING

T_STRING有点用词不当。它不是指引用的"string"。它意味着遇到了原始标识符。这可以从bare单词到剩余的CONSTANT或函数名称、被遗忘的未引用字符串或任何纯文本。

  1. 错误引用的字符串

    然而,此语法错误最常见的是错误引用的字符串值。任何未转义和杂散的"'引号都将形成无效表达式:

                   ⇓                  ⇓
    echo "<a href="http://example.com">click here</a>";
    

    语法突出显示会使此类错误变得非常明显。重要的是要记住使用反斜杠来转义\"双引号或\'单引号-这取决于哪个用作字符串外壳

    • 为方便起见,当输出带有双引号的纯超文本标记语言时,您应该更喜欢外部单引号。
    • 如果要插入变量,请使用双引号字符串,但要注意转义文字"双引号。
    • 对于较长的输出,首选多个echo/print行而不是转义输入和输出。更好的是考虑HEREDOC部分。


    另一个例子是在PHP生成的超文本标记语言代码中使用PHP条目:

    $text = '<div>some text with <?php echo 'some php entry' ?></div>'
    

    如果$text很大,有很多行,并且开发人员看不到整个PHP变量值并专注于忘记其来源的代码段,就会发生这种情况。例子是这里

    请参阅PHP中单引号和双引号字符串有什么区别?

  2. 未闭合字符串

    如果您错过闭幕式",那么语法错误通常会在稍后出现。未终止的字符串通常会消耗一点代码,直到下一个预期的字符串值:

                                                           ⇓
    echo "Some text", $a_variable, "and some runaway string ;
    success("finished");
    ⇯
    

    解析器可能会抗议的不仅仅是文字上的#EYZ。另一个常见的变体是Unexpected '>'用于未引用的文字超文本标记语言。

  3. 非编程字符串引号

    如果您从博客或网站复制和粘贴代码,有时会以无效代码告终。印刷引号不是 PHP期望:

    $text = ’Something something..’ + ”these ain't quotes”;
    

    排版/智能引号是Unicode符号。PHP将它们视为相邻的字母数字文本的一部分。例如”these被解释为常量标识符。但随后的任何文本文字都会被解析器视为单字/T_STRING。

  4. 缺少的分号;再次

    如果您在前面的行中有一个未终止的表达式,那么以下任何语句或语言构造都会被视为原始标识符:

           ⇓
    func1()
    function2();
    

    PHP只是不知道你是否打算一个接一个地运行两个函数,或者你是否打算将它们的结果相乘、相加、比较它们,或者只运行一个||或另一个。

  5. PHP脚本中的短开放标签和<?xml标头

    这是相当罕见的。但是如果启用了short_open_tags,那么您就无法开始PHP脚本使用XML声明

          ⇓
    <?xml version="1.0"?>
    

    PHP将看到<?并将其收回。它不会理解流浪的xml的含义。它会被解释为常量。但是version将被视为另一个文字/常量。由于解析器无法在没有表达式运算符的情况下理解两个后续文字/值,这将是解析器故障。

  6. 不可见的Unicode字符

    语法错误的最可怕的原因是Unicode符号,例如不间断空间。PHP允许Unicode字符作为标识符名称。如果您收到T_STRING解析器对完全不可疑的代码的投诉,例如:

    <?php
    print 123;
    

    您需要开发另一个文本编辑器。甚至是一个六进制编辑器。这里看起来像普通空格和换行符,可能包含不可见的常量。基于Java的IDE有时会忘记内部损坏的UTF-8 BOM、零宽度空格、段落分隔符等。尝试重新编辑所有内容,删除空格并重新添加正常空格。

    您可以通过在每一行开始处添加冗余的;语句分隔符来缩小范围:

    <?php
    ;print 123;
    

    这里额外的;分号将前面的不可见字符转换为未定义的常量引用(表达式作为语句)。这反过来使PHP产生有用的通知。

  7. 变量名前面缺少“$”符号

    PHP中的变量由美元符号表示,后跟变量的名称。

    美元符号($)是一个印记,它将标识符标记为变量的名称。如果没有这个符号,标识符可以是语言关键字恒定

    当PHP代码是从另一种语言编写的代码“翻译”(C、Java、JavaScript等)时,这是一个常见的错误。在这种情况下,变量类型的声明(当原始代码是用使用类型化变量的语言编写时)也可能会偷偷溜出并产生此错误。

  8. 转义引号

    如果您在字符串中使用\,它有特殊的含义。这称为“转义字符”,通常会告诉解析器按字面意思获取下一个字符。

    示例:echo 'Jim said \'Hello\'';将打印Jim said 'hello'

    如果您转义字符串的结束引号,结束引号将按字面意思而不是预期,即作为字符串的一部分的可打印引号而不是关闭字符串。这通常会在您打开下一个字符串或脚本末尾显示为解析错误。

    在Windows中指定路径时非常常见的错误:"C:\xampp\htdocs\"错误。您需要"C:\\xampp\\htdocs\\"

  9. 类型化属性

    您需要PHP≥7.4才能使用属性类型,例如:

    public stdClass $obj;
    

意外T_CONSTANT_ENCAPSED_STRING
意外T_ENCAPSED_AND_WHITESPACE

笨拙的名称T_CONSTANT_ENCAPSED_STRINGT_ENCAPSED_AND_WHITESPACE指的是引用的"string"文字

它们在不同的上下文中使用,但语法问题非常相似。T_ENCAPSED…警告出现在双引号字符串上下文中,而T_CONSTANT…字符串通常在普通PHP表达式或语句中误入歧途。

  1. 变量插值不正确

    不正确的PHP变量插值最常出现:

                              ⇓     ⇓
    echo "Here comes a $wrong['array'] access";
    

    在PHP上下文中引用数组键是必须的。但在双引号字符串(或HEREDOC)中,这是一个错误。解析器抱怨包含单引号的'string',因为它通常需要那里的文字标识符/键。

    更准确地说,使用PHP2风格的双引号内的简单语法进行数组引用是有效的:

    echo "This is only $valid[here] ...";
    

    然而,嵌套数组或更深层的对象引用需要复卷曲字符串表达式语法:

    echo "Use {$array['as_usual']} with curly syntax.";
    

    如果不确定,这通常使用起来更安全。它通常甚至被认为更具可读性。更好的IDE实际上为此使用不同的语法着色。

  2. 缺少连接

    如果字符串跟随表达式,但缺少连接或其他运算符,那么您将看到PHP抱怨字符串文字:

                           ⇓
    print "Hello " . WORLD  " !";
    

    虽然对你和我来说很明显,PHP只是不能字符串应该附加在那里。

  3. 混淆字符串引号外壳

    混淆字符串分隔符时也会出现相同的语法错误。由单个'或双引号"开头的字符串也以相同的引号结尾。

                    ⇓
    print "<a href="' . $link . '">click here</a>";
    ⌞⎽⎽⎽⎽⎽⎽⎽⎽⌟⌞⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⌟⌞⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⎽⌟
    

    该示例以双引号开始。但是双引号也用于超文本标记语言属性。然而,其中预期的连接运算符被解释为单引号中第二个字符串的一部分。

    提示:设置您的编辑器/IDE为单引号和双引号字符串使用略微不同的着色。(它还有助于应用程序逻辑,例如,对于文本输出更喜欢双引号字符串,而单引号字符串仅用于类似常量的值。)

    这是一个很好的例子,您不应该首先打破双引号。相反,只需使用正确的\"逃逸作为超文本标记语言属性的引号:

    print "<a href=\"{$link}\">click here</a>";
    

    虽然这也可能导致语法混乱,但所有更好的IDE/编辑器都通过以不同的方式为转义引号着色来提供帮助。

  4. 缺少开场白

    相当于被遗忘的开场#EYZ0/'报价是解析器错误的配方:

                   ⇓
    make_url(login', 'open');
    

    在这里,', '将成为bareword之后的字符串文字,而login显然是一个字符串参数。

  5. 数组列表

    如果您在数组创建块中错过了,逗号,解析器将看到两个连续的字符串:

    array(               ⇓
    "key" => "value"
    "next" => "....",
    );
    

    请注意,最后一行可能总是包含一个额外的逗号,但忽略中间的逗号是不可原谅的。如果没有语法突出显示,这很难发现。

  6. 函数参数列表

    同样的事情进行函数调用

                             ⇓
    myfunc(123, "text", "and"  "more")
    
  7. 失控字符串

    一个常见的变体是完全被遗忘的字符串终止符:

                                    ⇓
    mysql_evil("SELECT * FROM stuffs);
    print "'ok'";
    ⇑
    

    这里PHP抱怨两个字符串文字直接跟随在一起。但真正的原因当然是未关闭的前一个字符串。

  8. HEREDOC缩进

    php7.3之前,heldoc字符串结束分隔符不能以空格为前缀:

    print <<< HTML
    <link..>
    HTML;
    ⇑
    

    解决方案:升级PHP或找到更好的主机。

另见

意外(

开括号通常跟随语言结构,例如if/foreach/for/array/list或开始算术表达式。在"strings"、之前的()、单独的$之后以及在一些典型的声明上下文中,它们在语法上是不正确的。

  1. 函数声明参数

    此错误较少出现尝试使用表达式作为默认函数参数。即使在PHP7中也不支持:

    function header_fallback($value, $expires = time() + 90000) {
    

    函数声明中的参数只能是文字值或常量表达式。与函数调用不同,您可以自由使用whatever(1+something()*2)等。

  2. 类属性默认值

    类成员声明也是如此,其中只允许字面量/常量值,不允许表达式:

    class xyz {                   ⇓
    var $default = get_config("xyz_default");
    

    把这些东西放在构造函数中。 请参阅为什么PHP属性不允许函数?

    再次注意PHP 7只允许var $xy = 1 + 2 +3;常量表达式。

  3. PHP中的JavaScript语法

    使用JavaScript或jQuery语法在PHP中不起作用,原因很明显:

    <?php      ⇓
    print $(document).text();
    

    发生这种情况时,它通常表示前面的字符串未终止;和文字<script>部分泄漏到PHP代码上下文中。

  4. isset(()),空,键,下一个,当前

    isset()empty()都是语言内置的,而不是函数。它们需要直接访问变量。如果你无意中添加了一对括号太多,那么你会创建一个表达式:

              ⇓
    if (isset(($_GET["id"]))) {
    

    这同样适用于任何需要隐式变量名访问的语言构造。这些内置是语言语法的一部分,因此不允许装饰性的额外括号。

    需要变量引用但获取表达式结果的用户级函数会导致运行时错误。


意外)

  1. 缺少函数参数

    您不能有流浪的函数调用中的最后一个逗号。PHP期望那里有一个值,因此抱怨提前关闭)括号。

                  ⇓
    callfunc(1, 2, );
    

    尾部逗号只允许在array()list()构造中使用。

  2. 未完成的表达式

    如果你忘记了算术表达式中的某些内容,那么解析器就会放弃。因为它应该如何解释:

                   ⇓
    $var = 2 * (1 + );
    

    如果你甚至忘记了结尾),那么你会收到一个关于意外分号的投诉。

  3. Foreach为constant

    对于控制语句中被遗忘的变量$前缀,您将看到:

                       ↓    ⇓
    foreach ($array as wrong) {
    

    这里的PHP有时会告诉你它需要一个::来代替。因为一个类::$变量可以满足预期的$变量表达式。


意外{

花括号{}包含代码块。关于它们的语法错误通常表明一些不正确的嵌套。

  1. if中不匹配的子表达式

    最常见的是,如果解析器抱怨开头的卷曲{出现得太早,则会导致不平衡#EYZ0和)。一个简单的例子:

                                  ⇓
    if (($x == $y) && (2 == true) {
    

    计算括号或使用有助于此的IDE。也不要编写没有空格的代码。可读性很重要。

  2. {和}在表达式上下文中

    你不能在表达式中使用花括号。如果你混淆括号和花括号,它将不符合语言语法:

               ⇓
    $var = 5 * {7 + $x};
    

    标识符构造有一些例外,例如局部范围变量${references}

  3. 变量或卷曲var表达式

    这是非常罕见的。但您也可能会收到{}解析器对复杂变量表达式的投诉:

                          ⇓
    print "Hello {$world[2{]} !";
    

    虽然在这种情况下出现意外的}的可能性更高。


意外}

当收到“意外的}”错误时,您大多过早地关闭了代码块。

  1. 代码块中的最后一条语句

    它可以发生在任何未终止的表达式上。

    如果函数/代码块中的最后一行缺少尾随的;分号:

    function whatever() {
    doStuff()
    }            ⇧
    

    在这里,解析器无法判断您是否仍然想将+ 25;添加到函数结果或其他内容中。

  2. 无效的块嵌套/忘记{

    当代码块}过早关闭时,或者您甚至忘记了打开{时,您有时会看到此解析器错误:

    function doStuff() {
    if (true)    ⇦
    print "yes";
    }
    }   ⇧
    

    在上面的代码片段中,if没有开头的{花括号。因此,下面的闭包}变得多余。因此,用于该函数的下一个闭包}不能与原始开头{花括号相关联。

    如果没有适当的代码缩进,这种错误更难找到。使用IDE和括号匹配。


意外{,期待(

需要条件/声明标头代码块的语言结构将触发此错误。

  1. 参数列表

    例如没有参数列表的错误声明函数不允许:

                     ⇓
    function whatever {
    }
    
  2. Control statement conditions

    And you can't likewise have an if without condition.

      ⇓
    if {
    }
    

    很明显,这没有意义。通常的嫌疑人也是如此,for/foreachwhile/do等。

    如果你有这个特殊的错误,你绝对应该查找一些手动示例。

意外T_IF
意外T_FOREACH
意外T_FOR
意外T_WHILE
意外T_DO
意想不到的T_ECHO

ifforeachforwhilelistglobalreturndoprintecho等控件构造只能用作语句。它们通常单独驻留在一行中。

  1. 分号;你在哪里?

    如果解析器抱怨控制语句,则在上一行中普遍存在漏了一个分号

                 ⇓
    $x = myfunc()
    if (true) {
    

    解决方案:查看前一行;添加分号。

  2. 类声明

    发生这种情况的另一个位置是在类声明。在类部分,您只能列出属性初始化和方法部分。那里不能驻留任何代码。

    class xyz {
    if (true) {}
    foreach ($var) {}
    

    这种语法错误通常会出现在嵌套不正确的{}中。特别是当函数代码块过早关闭时。

  3. 表达式上下文中的语句

    大多数语言结构都可以只能用作陈述。它们不应该放在其他表达式中:

                       ⇓
    $var = array(1, 2, foreach($else as $_), 5, 6);
    

    同样,你不能在字符串、数学表达式或其他地方使用if

                   ⇓
    print "Oh, " . if (true) { "you!" } . " won't work";
    // Use a ternary condition here instead, when versed enough.
    

    为了在表达式中嵌入if类条件,您通常需要使用?:三元计算

    这同样适用于forwhileglobalecho和较小的扩展list

              ⇓
    echo 123, echo 567, "huh?";
    

    print()是一种内置语言,可以在表达式上下文中使用。(但很少有意义。)

  4. 保留关键字作为标识符

    您也不能对用户定义的函数或类名使用doif和其他语言结构。(也许在PHP 7中。但即使这样也不建议。)

  5. 您的控制块后有一个分号而不是冒号(:)或大括号({)

    控件结构通常用花括号包装(但冒号可以在替代语法中使用)来表示它们的作用域。如果您不小心使用了分号,则会过早关闭该块,从而导致结束语句抛出错误。

    foreach ($errors as $error); <-- should be : or {

我认为这个话题完全被过度讨论/过度复杂了。使用IDE是完全避免任何语法错误的方法。我甚至会说,没有IDE的工作有点不专业。为什么?因为现代的IDE会在你输入的每个字符后检查你的语法。当你编码时,整行变成红色,并且一个巨大的警告通知会显示你语法错误的确切类型和确切位置,那么绝对没有必要搜索其他解决方案。

使用语法检查IDE意味着:

您将(有效地)再也不会遇到语法错误,仅仅是因为您在键入时看到它们。

具有语法检查的优秀IDE(所有这些都可用于Linux,Windows和Mac):

  1. NetBeans[免费]
  2. PHPStorm[199美元]
  3. Eclipse withPHP插件[免费]
  4. 崇高[80美元](主要是一个文本编辑器,但可以使用插件进行扩展,例如PHP语法分析器

意外$end

当PHP谈到“意外的$end”时,这意味着你的代码结束了,而解析器期待更多代码。(从字面上理解,这个消息有点误导。它不是关于一个名为“$end”的变量,有时是新手假设的。它指的是“文件末尾”,EOF。)

原因:不平衡{}用于代码块/和函数或类声明。

这是几乎总是关于缺少}花括号来关闭前面的代码块。它的意思是解析器期望找到一个关闭的},但实际上到达了文件的末尾。

  • 同样,使用适当的缩进来避免此类问题。

  • 使用带有括号匹配的IDE,找出}的错误所在。 大多数IDE和文本编辑器都有键盘快捷键:

    • NetBeans、PhpStorm、Komodo:Ctrl[Ctrl]
    • Eclipse, Aptana:CtrlShiftP
    • Atom, Sublime:Ctrlm-Zend StudioCtrlM
    • Geany, Notepad++:CtrlB-Joe:Ctrlg-Emacs:C-M-n-Vim:%

大多数IDE还突出匹配大括号、括号和括号。 这使得检查它们的相关性变得非常容易:

IDE中的括号匹配

未终止表达式

未终止的表达式或语句也可能出现Unexpected $end语法/解析器错误:

  • $var = func(1, ?>EOF

所以,首先看看脚本的结尾。在任何PHP脚本中,尾随;对于最后一条语句通常是多余的。但是你有一个应该。正是因为它缩小了此类语法问题。特别是在你发现自己在脚本末尾添加了更多语句之后。

缩进的HEREDOC标记

另一个常见的情况出现在HEREDOC或NOWDOC字符串中。前导空格、制表符等的终止标记将被忽略:

print <<< END
Content...
Content....
END;
# ↑ terminator isn't exactly at the line start

因此,解析器假定HEREDOC字符串将持续到文件末尾(因此“意外的$end”)。几乎所有IDE和语法突出显示编辑器都会使这一点变得明显或警告它。

转义引号

如果您在字符串中使用\,它有特殊的含义。这称为“转义字符”,通常会告诉解析器按字面意思获取下一个字符。

示例:echo 'Jim said \'Hello\'';将打印Jim said 'hello'

如果您转义字符串的结束引号,结束引号将按字面意思而不是预期,即作为字符串的一部分的可打印引号而不是关闭字符串。这通常会在您打开下一个字符串或脚本末尾显示为解析错误。

在Windows中指定路径时非常常见的错误:"C:\xampp\htdocs\"是错误的。您需要"C:\\xampp\\htdocs\\"。或者,PHP通常会将Unix样式的路径(例如"C:/xampp/htdocs/")转换为Windows的正确路径。

替代语法

在模板中使用语句/代码块的替代语法时,您很少会看到此语法错误。例如,使用if:else:以及缺少的endif;

另见:

意外[

如今,意外的[数组括号在过时的PHP版本上很常见。短数组语法自PHP>=5.4起可用。较旧的安装仅支持array()

$php53 = array(1, 2, 3);
$php54 = [1, 2, 3];
⇑

数组函数结果取消引用同样不适用于旧的PHP版本:

$result = get_whatever()["key"];
⇑

参考-这个错误在PHP中是什么意思?-“语法错误,意外\[展示了最常见和最实用的解决方法。

不过,您最好只是升级您的PHP安装。对于共享虚拟主机计划,首先研究是否可以使用SetHandler php56-fcgi来启用更新的运行时。

另见:

顺便说一句,还有预处理器和PHP 5.4语法下转换器,如果你真的很喜欢旧的+较慢的PHP版本。

意外[语法错误的其他原因

如果不是PHP版本不匹配,那么它通常是一个简单的错字或新手语法错误:

  • 您不能使用类中的数组属性声明/表达式,即使在PHP 7中也不能。

    protected $var["x"] = "Nope";
    ⇑
    
  • Confusing [ with opening curly braces { or parentheses ( is a common oversight.

    foreach [$a as $b)
    ⇑
    

    甚至:

    function foobar[$a, $b, $c] {
    ⇑
    
  • Or trying to dereference constants (before PHP 5.6) as arrays:

    $var = const[123];
    ⇑
    

    至少PHP将const解释为常量名称。

    如果您打算访问数组变量(这是这里的典型原因),那么添加前导$符号-因此它成为$varname

  • 您正在尝试对关联数组的成员使用global关键字。这是无效的语法:

    global $var['key'];
    


Unexpected ] closing square bracket

This is somewhat rarer, but there are also syntax accidents with the terminating array ] bracket.

  • Again mismatches with ) parentheses or } curly braces are common:

    function foobar($a, $b, $c] {
    ⇑
    
  • Or trying to end an array where there isn't one:

    $var = 2];
    

    这通常发生在多线嵌套数组声明中。

    $array = [1,[2,3],4,[5,6[7,[8],[9,10]],11],12]],15];
    ⇑
    

    如果是这样,请使用IDE进行括号匹配以查找任何过早的]数组闭包。至少使用更多的行间距和换行符来缩小范围。

意外T_IS_EQUAL
意外T_IS_GREATER_OR_EQUAL
意外T_IS_IDENTICAL
意外T_IS_NOT_EQUAL
意外T_IS_NOT_IDENTICAL
意外T_IS_SMALLER_OR_EQUAL
意外<
意外>

比较运算符,如==>====!=<>!==<=<>大多应该只在表达式中使用,例如if表达式。如果解析器抱怨它们,那么通常意味着错误的解析或它们周围的>=0>=1括号不匹配。

  1. 父母分组

    特别是对于具有多个比较的if语句,您必须注意正确计数开括号和闭括号

                            ⇓
    if (($foo < 7) && $bar) > 5 || $baz < 9) { ... }
    ↑
    

    这里的if条件已经被)终止

    一旦你的比较变得足够复杂,它通常有助于将其拆分为多个嵌套的if构造。

  2. isset()比较

    一个常见的新手是Pitfal,他试图将isset()empty()与比较结合起来:

                            ⇓
    if (empty($_POST["var"] == 1)) {
    

    甚至:

                        ⇓
    if (isset($variable !== "value")) {
    

    这对PHP来说没有意义,因为issetempty是只接受变量名的语言构造。比较结果也没有意义,因为输出只是/已经是布尔值。

  3. >=大于等于与=>数组运算符混淆

    这两个运算符看起来有些相似,所以它们有时会混淆:

             ⇓
    if ($var => 5) { ... }
    

    您只需记住此比较运算符称为“大于平等”即可正确使用。

    请参阅:PHP中的if语句结构

  4. 没什么可比较的

    如果两个比较属于相同的变量名,您也不能组合它们:

                     ⇓
    if ($xyz > 5 and < 100)
    

    PHP无法推断您打算再次比较初始变量。表达式通常根据运算符优先级配对,因此当看到<时,原始变量只剩下一个布尔结果。

    请参阅:意想不到的T_IS_SMALLER_OR_EQUAL

  5. 比较链

    您不能与具有一行运算符的变量进行比较:

                      ⇓
    $reult = (5 < $x < 10);
    

    这必须分为两个比较,每个都针对$x

    这实际上更多的是黑名单表达式的情况(由于等价的运算符结合性)。它在一些C风格语言中语法上是有效的,但PHP也不会将其解释为预期的比较链。

  6. 意外>
    意外<

    大于>或小于<的运算符没有自定义的T_XXX标记器名称。虽然它们可能像其他运算符一样放错位置,但你更经常看到解析器抱怨它们引用错误的字符串和捣碎的超文本标记语言:

                            ⇓
    print "<a href='z">Hello</a>";
    ↑
    

    这相当于将字符串"<a href='z"与文字常量Hello进行比较,然后将另一个<进行比较。或者至少PHP是这样看待的。实际原因和语法错误是字符串"过早终止。

    也不可能嵌套PHP开始标记:

    <?php echo <?php my_func(); ?>
    ↑
    

See also:

意外T_IF
意外T_ELSEIF
意外T_ELSE
意想不到的T_ENDIF

条件控制块ifelseifelse遵循简单的结构。当你遇到语法错误时,很可能只是无效的块嵌套→缺少{花括号}-或者太多。

在此处输入图片描述

  1. 由于缩进不正确,缺少{}

    不匹配的代码大括号对于格式不太好的代码很常见,例如:

    if((!($opt["uniQartz5.8"]!=$this->check58)) or (empty($_POST['poree']))) {if
    ($true) {echo"halp";} elseif((!$z)or%b){excSmthng(False,5.8)}elseif (False){
    

    如果你的代码看起来像这样,请重新开始!否则你或其他任何人都无法修复它。在互联网上展示这个以寻求帮助是没有意义的。

    如果您可以直观地遵循if/else条件句及其{代码块}的嵌套结构和关系,您将只能修复它。使用您的IDE查看它们是否全部配对。

    if (true) {
    if (false) {
    …
    }
    elseif ($whatever) {
    if ($something2) {
    …
    }
    else {
    …
    }
    }
    else {
    …
    }
    if (false) {    //   a second `if` tree
    …
    }
    else {
    …
    }
    }
    elseif (false) {
    …
    }
    

    任何双}}不仅会关闭一个分支,还会关闭一个以前的条件结构。因此坚持一种编码风格;不要在嵌套的if/else树中混合和匹配。

    除了这里的一致性之外,避免冗长的条件也很有帮助。使用临时变量或函数来避免不可读的if表达式。

  2. IF不能在表达式中使用

    一个令人惊讶的频繁的新手错误是试图在表达式中使用if语句,例如print语句:

                       ⇓
    echo "<a href='" . if ($link == "example.org") { echo …
    

    这当然是无效的。

    您可以使用一个三元条件,但要注意易读性影响。

    echo "<a href='" . ($link ? "http://yes" : "http://no") . "</a>";
    

    否则打破这样的输出结构:使用多个#EYZ和echos
    更好的是,使用临时变量,并将条件放在:

    if ($link) { $href = "yes"; } else { $href = "no"; }
    echo "<a href='$href'>Link</a>";
    

    为这种情况定义函数或方法通常也有意义。

    控制块不返回“结果”

    现在这种情况不太常见,但一些程序员甚至试图将if视为可以返回结果

    $var = if ($x == $y) { "true" };
    

    这在结构上与在字符串连接/表达式中使用if相同。

    • 但是控制结构(if/foreach/同时)没有“结果”
    • 文字字符串“true”也只是一个空语句。

    您必须使用赋值在代码块

    if ($x == $y) { $var = "true"; }
    

    或者,求助于?:三元比较。

    如果在如果

    无法嵌套if在一个条件:

                        ⇓
    if ($x == true and (if $y != false)) { ... }
    

    这显然是多余的,因为and(或or)已经允许链接比较。

  3. 忘记;分号

    再说一次:每个控制块都需要是一条语句。如果上一个代码段没有以分号结尾,那么这是一个保证的语法错误:

                    ⇓
    $var = 1 + 2 + 3
    if (true) { … }
    

    顺便说一句,{…}代码块中的最后一行也需要分号。

  4. 分号太早

    现在指责特定的编码风格可能是错误的,因为这个陷阱太容易被忽视了:

                ⇓
    if ($x == 5);
    {
    $y = 7;
    }
    else           ←
    {
    $x = -1;
    }
    

    这比你想象的要频繁得多。

    • 当您用#EYZ1终止if ()表达式时,它将执行一个空语句。;变成了它自己的一个空{}
    • 因此,{…}块与if分离,并将始终运行。
    • 因此else不再与开放的if构造有关系, 这就是为什么这会导致意外的T_ELSE语法错误。

    这也解释了这个语法错误的一个同样微妙的变化:

    if ($x) { x_is_true(); }; else { something_else(); };
    

    其中代码块{…}后的;终止整个if 构造,在语法上切断else分支。

  5. 不使用代码块

    if/elseif/else分支的代码块中,语法上允许省略花括号{}。遗憾的是,这是一种对于不熟悉的编码人员非常常见的语法风格。(在错误的假设下,这输入或阅读更快)。

    然而,这很可能会使语法出错。迟早会有额外的语句进入if/else分支:

    if (true)
    $x = 5;
    elseif (false)
    $x = 6;
    $y = 7;     ←
    else
    $z = 0;
    

    但是要实际使用代码块,您确实有要编写{}它们!

    即使是经验丰富的程序员也会避免这种无约束的语法,或者至少 将其理解为规则的例外。

  6. 其他/其他顺序错误

    当然,要提醒自己的一件事是条件订单

    if ($a) { … }
    else { … }
    elseif ($b) { … }
    ↑
    

    你可以拥有尽可能多的elseif,但是else必须最后走。就是这样。

  7. 类声明

    作为上述,您不能在类声明中包含控制语句:

    class xyz {
    if (true) {
    function ($var) {}
    }
    

    你要么忘了一个函数定义,要么在这种情况下过早关闭一个}

  8. 意外T_ELSEIF/T_ELSE

    混合PHP和超文本标记语言时,if/elseif的结束}必须与下一个elseif/else在同一个PHP块<?php ?>中。这将生成错误,因为if的结束}需要成为elseif的一部分:

    <?php if ($x) { ?>
    html
    <?php } ?>
    <?php elseif ($y) { ?>
    html
    <?php } ?>
    

    正确的格式<?php } elseif

    <?php if ($x) { ?>
    html
    <?php } elseif ($y) { ?>
    html
    <?php } ?>
    

    这或多或少是不正确缩进的变体-大概通常基于错误的编码意图。
    您不能使用在之间混搭其他语句ifelseif/else结构令牌:

    if (true) {
    }
    echo "in between";    ←
    elseif (false) {
    }
    ?> text <?php      ←
    else {
    }
    

    两者都只能出现在{…}代码块中,而不能出现在控制结构令牌之间。

    • 无论如何,这都没有意义。当PHP在ifelse分支之间跳转时,并不存在某种“未定义”状态。
    • 你必须决定打印语句属于哪里/或者它们是否需要在两个分支中重复。

    您也不能在不同的控制结构之间如果/否则

    foreach ($array as $i) {
    if ($i) { … }
    }
    else { … }
    

    ifelse之间没有句法关系foreach词法范围结束于},因此if结构没有继续的意义。

  9. T_ENDIF

    如果一个意外的T_ENDIF被抱怨,你可以使用另一种语法样式if:elseif:else:endif;。你真的应该三思而后行。

    • 一个常见的陷阱是将类似于;分号的:冒号混淆为

    • 由于缩进在模板文件中更难跟踪,因此使用替代语法时更难-您的endif;与任何if:不匹配是合理的。

    • 使用} endif;翻了一番if终止符。

    而“意外的$end”通常是被遗忘的闭幕式}花括号的价格。

  10. 赋值vs.比较

    所以,这不是语法错误,但在这种情况下值得一提:

           ⇓
    if ($x = true) { }
    else { do_false(); }
    

    这不是#EYZ0/#EYZ1比较,但=赋值。这是相当微妙的,很容易导致一些用户无助地编辑整个条件块。首先要注意意想不到的分配-当你遇到逻辑错误/错误时。

意想不到的T_LNUMBER

标记T_LNUMBER指的是一个“长”/数字。

  1. 无效的变量名

    在PHP和大多数其他编程语言中,变量不能以数字开头。第一个字符必须是字母或下划线。

    $1   // Bad
    $_1  // Good
    

    *

    • 通常出现用于在PHP上下文中使用preg_replace占位符"$1"

      #                         ↓            ⇓  ↓
      preg_replace("/#(\w+)/e",  strtopupper($1) )
      

      回调应该被引用的地方。(现在/e正则表达式标志已被弃用。但它有时仍然在preg_replace_callback函数中被误用。)

    • 相同的标识符约束适用于对象属性,顺便说一句。

             ↓
      $json->0->value
      
    • While the tokenizer/parser does not allow a literal $1 as variable name, one could use ${1} or ${"1"}. Which is a syntactic workaround for non-standard identifiers. (It's best to think of it as a local scope lookup. But generally: prefer plain arrays for such cases!)

    • Amusingly, but very much not recommended, PHPs parser allows Unicode-identifiers; such that $➊ would be valid. (Unlike a literal 1).

  2. Stray array entry

    An unexpected long can also occur for array declarations - when missing , commas:

    #            ↓ ↓
    $xy = array(1 2 3);
    

    或者同样的函数调用和声明,以及其他构造:

    • func(1, 2 3);
    • function xy($z 2);
    • for ($i=2 3<$z)

    因此,通常缺少;,中的一个来分隔列表或表达式。

  3. 错误引用的超文本标记语言

    再一次,错误引用的字符串是杂散数的常见来源:

    #                 ↓ ↓
    echo "<td colspan="3">something bad</td>";
    

    这种情况应该或多或少地像意想不到的T_STRING错误一样对待。

  4. 其他标识符

    函数、类和命名空间都不能以数字开头命名:

             ↓
    function 123shop() {
    

    与变量名几乎相同。

意料之外的“?

如果您尝试在<?php中使用<?php,则会给出此错误*。

$var = 'hello '<?php echo 'world'; ?>;

*对于PHP版本4.3.1、4.3.5-4.3.11、4.4.0-4.4.1、5.0.0-5.0.5、4.4.2-4.4.9、5.1.0-5.1.6、5.2.0-5.2.17、5.3.0-5.3.29、5.4.0-5.4.45、5.5.0-5.5.38、5.6.0-5.6.40、7.0.0-7.0.33、7.1.0-7.2.34、7.3.0-7.3.31、7.4.0-7.4.24


如果您尝试在PHP 7之前的PHP版本中使用空合并运算符??,您将收到此错误。

<?= $a ?? 2; // works in PHP 7+
<?= (!empty($a)) ? $a : 2; // All versions of PHP

意外的“?”,期望变量

对于可空类型可能会出现类似的错误,例如:

function add(?int $sum): ?int {

这再次表示正在使用过时的PHP版本(CLI版本php -v或Web服务器绑定的版本phpinfo();)。

意外“继续”(T_CONTINUE)

continue是一个语句(比如for或if),必须独立出现。它不能用作表达式的一部分。部分原因是继续不返回值,但在表达式中,每个子表达式都必须产生某个值,因此整个表达式都会产生一个值。这就是语句和表达式的区别。

这意味着continue不能用于三元语句或任何需要返回值的语句。

意外“中断”(T_BREAK)

当然,break;也是如此。它也不能在表达式上下文中使用,而是一个严格的语句(与foreachif块在同一级别)。

意外“回报”(T_RETURN)

现在这对于return来说可能更令人惊讶,但这也只是一个块级的声明。它确实向更高的作用域/函数返回一个值(或NULL),但它不会作为表达式本身进行评估。

意外=

这可能是由于变量名中包含无效字符造成的。变量名必须遵循以下规则:

变量名遵循与PHP中其他标签相同的规则。有效的变量名以字母或下划线开头,后跟任意数量的字母、数字或下划线。作为正则表达式,它将这样表示:'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'

意外的“终结”(T_ENDWHILE)

语法使用冒号-如果没有冒号,则会发生上述错误。

<?php while($query->fetch()): ?>
....
<?php endwhile; ?>

此语法的替代方法是使用花括号:

<?php while($query->fetch()) { ?>
....
<?php } ?>

http://php.net/manual/en/control-structures.while.php

意料之外”。

如果您尝试在不受支持的PHP版本中使用Splat运算符(...,则可能会发生这种情况。

...首次在PHP 5.6中可用,用于捕获函数的可变数量的参数:

function concatenate($transform, ...$strings) {
$string = '';
foreach($strings as $piece) {
$string .= $piece;
}
return($transform($string));
}


echo concatenate("strtoupper", "I'd ", "like ", 4 + 2, " apples");
// This would print:
// I'D LIKE 6 APPLES

在PHP 7.4中,您可以将其用于数组表达式

$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// ['banana', 'orange', 'apple', 'pear', 'watermelon'];

意外:

1. PHP 8命名参数语法

':',期望','或')'

如果尝试在PHP<8版本中使用PHP 8的新命名参数功能,则会出现以下错误:

$table->string(column:'Name');

解决方案:

  1. 将您的PHP版本升级到PHP 8.0.0或更高版本
  2. 不使用命名参数(按照预期的顺序传递参数)

2.裁剪类::分离器

错误消息开始解析错误:语法错误,意外':'可能是由错误地将类静态引用Class::$Variable写入Class:$Variable引起的。

出现这些错误的另一个原因是意外的空格,就像代码中的类似字符一样,代码行似乎是完美的,但它们包含一些特定的字符,这些字符类似于换行符或空格或制表符,但它们不会被解析器解析。 当我尝试通过简单地复制粘贴将一些代码从网页放入代码编辑器时,我遇到了这个问题,我看到了数组定义的错误。数组定义中的一切都看起来很正确。我无法整理正确的错误,最后我在单行中定义了这个数组,然后错误消失了。然后我再次尝试使该定义像多个,但手动添加中断(回车)每个数组元素并保存了文件,这次编辑器没有解析错误,运行时也没有错误。 例如,我在一个博客上遇到了这个片段的问题,实际上无法发布这些片段,因为堆栈溢出已经知道代码的问题。

然后在解决它之后,我的工作片段是,它看起来与显示解析错误的片段相似

语法错误,意外的“auth”(T_CONSTANT_ENCAPSED_STRING),期望']'

    public $aliases = [
'csrf'=> \CodeIgniter\Filters\CSRF::class,
'toolbar'=> \CodeIgniter\Filters\DebugToolbar::class,
'honeypot'=> \CodeIgniter\Filters\Honeypot::class,
'auth' => \App\Filters\Auth::class,
];

对于VS Code的新手,如果看到语法错误,请检查是否保存了文件。如果语法错误,保存文件,然后修复语法而不重新保存,VS Code会不断向您显示错误。保存文件后,错误信息才会消失。