【问题标题】:PHP closing tag deletes the line feedPHP结束标签删除换行符
【发布时间】:2013-12-15 15:21:46
【问题描述】:

我正在做一个experiment, an html preprocessor,比如 SLIM 或 Jade。

这是看起来正确的 PHP 代码:

nav
  ul id: "test"
    li
      @<?= $Var; ?>
    li
      @About
    li
      @Contact

这是预期的预处理 html(是的,$Var == "Test"):

nav
  ul id: "test"
    li
      @Test
    li
      @About
    li
      @Contact

但是,在 浏览器 中,我得到这个 错误 文本作为 预处理器 html

nav
  ul id: "test"
    li
      @Test    li
      @About
    li
      @Contact

最后,有两种方法可以使它正确。

  1. 手动添加断线:

    nav
      ul id: "test"
        li
          @<?= $Var . "\n"; ?>
      li
        @About
      li
        @Contact
    
  2. 在 PHP 结束标记 (??) 后写一个空格。

为什么第一种情况,&lt;?= $Var; ?&gt;,忽略 PHP 结束标记后的换行符?我真的找不到任何东西,因为谷歌带来了太多关于为什么你应该忽略结束的结果标记我所做的每一次搜索,而不是我想要找到的内容。

【问题讨论】:

  • 当我尝试使用&lt;?= $var ?&gt; 在 HTML &lt;pre&gt;&lt;/pre&gt; 标签内的多行末尾输出变量时,我刚刚注意到了同样的问题。我需要在每个之后添加一个额外的换行符以保持文本输出格式正确。我真的不喜欢这种行为,尽管我可以理解为什么可能会添加它以防止当文件以 ?&gt; 结尾时在文件末尾出现额外的换行符输出(它可能是一个包含的文件应该什么都不输出)。

标签: php html preprocessor line-breaks


【解决方案1】:

更新:
查看 zend 语言扫描器 src,我的 "hunch" 似乎是正确的:T_CLOSE_TAG 标记似乎可能包含换行符。更重要的是,包含结束标记的脚本中最后一条语句的结束分号似乎也是可选的......

<ST_IN_SCRIPTING>("?>"|"</script"{WHITESPACE}*">"){NEWLINE}? {
    ZVAL_STRINGL(zendlval, yytext, yyleng, 0); /* no copying - intentional */
    BEGIN(INITIAL);
    return T_CLOSE_TAG;  /* implicit ';' at php-end tag */
}

只需在the zend_language_scanner.c and zend_language_scanner.l files here 中寻找T_CLOSE_TAG


可以肯定的是,我目前正在扫描 Zend 引擎的源代码,但我猜想,因为您发布的代码的最后一个字符只是结束标记 (?&gt;) ,生成输出的是 PHP。鉴于您没有告诉 PHP 输出换行符,因此 PHP 不会在您正在回显的任何内容中添加新行是理所当然的。
当然,结束标记后面的换行符会被 PHP 忽略,但由于某种原因,PHP 确实似乎使用了该换行符。我正在查看解析 PHP 脚本的 C 代码,但我认为它可能会使用换行符、空格、逗号的分号以及所有这些作为标记来将输入分块到节点中。
看到结束标记 ?&gt; 是一个真正的标记,并且是 PHP 语法的一部分,很可能这是引擎有效消耗换行符的地方,以及为什么它不是输出的一部分。

通过在结束标记后添加空格字符,可能会占用空间,但不会占用换行符,所以这可能就是您仍然看到换行符出现的原因。
我还尝试在一些测试代码中添加 2 个换行符,实际上:输出仅显示 1 个新行:

foo:
    <?= $bar; ?>

    foobar

输出:

foo:
    bar
    foobar

所以看来我的怀疑可能站得住脚。

但是,考虑到所有因素,以免您想破解 Zend 引擎源代码,手动添加换行并不是那么麻烦。事实上,这是确保生成正确换行符的好方法:
假设你在一个健康的 *NIX 系统上编写了一些代码,其中换行符是由\n 转义序列表示的所有意图和目的,手动添加 char 可能不会在 Windows 系统上产生所需的输出(使用\r\n),苹果系统使用\r...
PHP 有一个常量来确保您生成正确的换行符,具体取决于您的代码运行的平台:PHP_EOL。为什么不使用它:

<?= $bar, PHP_EOL; ?>

如果您想知道:是的,那是 $bar 逗号 PHP_EOL 您正在那里看到。为什么?将echo&lt;?= 视为C++ 的COUT,它是一个构造,它只是将你扔给它的任何内容推送到输出流,它是一个连接的字符串,或者只是一个逗号分隔的变量列表:不在乎。

现在,我的回答的以下部分有点跑题了,但这只是一些如此基本的、不言而喻的东西,但很多人都没有意识到这一点,以至于我无法抗拒解释关于字符串连接的一两件事的诱惑。
PHP,以及我所知道的大多数语言,都不关心它必须推送到输出流的 vars/vals 数量。这就是它的用途。 PHP,再一次:大多数语言,确实关心字符串的连接:字符串是一种常量值。当心情带你时,你不能只是把绳子拉长。一系列字符必须存储在内存中,必须分配内存以容纳更长的字符串。串联有效的作用(最佳情况)是这样的:

  • 计算string1和string2的长度
  • 分配将字符串 2 连接到字符串 1 所需的额外内存
  • 将字符串 2 复制到新(额外)分配的内存中

然而,在很多情况下,实际发生的是:

  • 计算两个字符串的长度
  • 分配内存,需要连接两个字符串
  • 将两个字符串复制到新分配的内存块中
  • 将新指针分配给需要分配的任何变量
  • 释放所有不再被引用的内存

第一种情况的例子:

$str1 = 'I am string constant 1';
$str2 = ' And I\'ll be concatenated';
$str1 .= $str2;

可以翻译成以下C代码:

char *str1, *str2;
//allocate mem for both strings, assign them their vals
str1 = realloc(str1,(strlen(str1) + strlen(str2)+1));//re-allocate mem for str1
strncat(str1, str2, strlen(str2);//concatenate str2 onto str1

但是,只需这样做:

$str3 = $str1 . $str2;

你实际上在做的是:

char *str3 = malloc((strlen(str1) + strlen(str2) + 1)*sizeof(char));
strcpy(str3, str1);//copy first string to newly allocated memory
strcat(str3, str2);//concatenate second string...

好像那个还不够,想想这个代码的含义:

$str1 = $str2 . $str1;

是的,果然:

char *str3 = malloc((strlen(str1) + strlen(str2) + 1)*sizeof(char));
strcpy(str3, str2);//copy seconds string to start of new string
strcat(str3, str1);//add first string at the end
free(str1);//free memory associated with first string, because we're reassigning it
str1 = str3;//set str1 to point to the new block of memory

现在我什至还没有遇到真正的串联噩梦(别担心,我也不会这样做)。像$foo = 'I ' . ' am '. 'The'. ' ' .$result.' of some'.1.' with a dot'.' fetish'; 这样的东西。看看它,那里有变量,可能是任何东西(数组,对象,huuuge 字符串......,那里也有一个整数......用逗号替换点并将其推送到 echo 构造只是这比开始考虑编写正确连接所有这些值所需的代码要容易得多...
很抱歉稍微偏离了这里,但鉴于这是,IMO,如此基本,我觉得好像每个人都应该意识到这一点......

【讨论】:

  • 我希望我能多次对此表示赞同。我已经在使用normalize() 函数来转换所有换行符,所以这可能不是问题。关于正在消耗的换行符,我可能不得不更改标题。但是,它改变了每个人对 AFAIK 的假设,因为单个空格或单个换行符显然不会发送任何标题。最后,虽然我只知道我在 C 的基础课程中学到了什么,但我完全理解了你对串联的解释。但是,我认为在 echo PHP 中“足够聪明”,不会分配任何额外的变量。感谢您的精彩回答。
  • @FranciscoPresencia: 乐于助人...我的意思不是坏话,但是假设echo 足够聪明 不是一个选择:@ 987654350@ 只能回显已评估为单个值的表达式:echo 1+1; 不回显1+1,它回显2。同样对于连接字符串:连接必须具有更高的基数,因为您可能正在回显实现 __toString 方法的数组或对象,必须在回显任何内容之前调用该方法(发出通知、警告......优先于echo)...
  • 好的,所以从现在开始,每次我需要回显连接时,我都会按照您指出的那样进行,echo $str1, $str2;。我什至找到了some metrics
  • @FranciscoPresencia:好吧,我不会强迫任何人使用逗号。这只是其中之一:编写代码确实,IMO,需要对细节有一定程度的关注。对我来说,这个细节表明程序员知道他的代码在较低级别上正在做什么,这可能表明编码人员不仅对他碰巧编写的语言有更深入的了解,而且对计算机的工作方式有更深入的了解。
  • @FranciscoPresencia:我认为按照设计:很多编辑器在保存文件时会在文件末尾添加换行符,因此如果您的脚本是 PHP-only,而 PHP 不会匹配换行,发送回客户端的响应将始终包含该换行。但它只会在 PHP 完成其工作后到达客户端,将输出发送到 http 守护程序(主要是 apache),然后谁会注意到 PHP 没有处理 all 的字符。然后服务器可能会尝试使用其他 (f)cgi 进程来理解该换行符,然后才将其发送出去。这将是我的疯狂猜测,为什么会这样
猜你喜欢
  • 1970-01-01
  • 2013-03-13
  • 2019-04-30
  • 2023-03-12
  • 1970-01-01
  • 2016-01-30
  • 1970-01-01
  • 2012-03-10
  • 1970-01-01
相关资源
最近更新 更多