更新:
查看 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 引擎的源代码,但我猜想,因为您发布的代码的最后一个字符只是结束标记 (?>) ,生成输出的是 PHP。鉴于您没有告诉 PHP 输出换行符,因此 PHP 不会在您正在回显的任何内容中添加新行是理所当然的。
当然,结束标记后面的换行符会被 PHP 忽略,但由于某种原因,PHP 确实似乎使用了该换行符。我正在查看解析 PHP 脚本的 C 代码,但我认为它可能会使用换行符、空格、逗号的分号以及所有这些作为标记来将输入分块到节点中。
看到结束标记 ?> 是一个真正的标记,并且是 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 或<?= 视为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,如此基本,我觉得好像每个人都应该意识到这一点......