2013-12-15 59 views
9

我在做一个experiment, an html preprocessor喜欢苗条或翡翠。PHP结束标记删除换行符

这是PHP代码似乎是正确的:

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

这是预期预处理HTML(是的,是$ var == “测试”):

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的结束标记(??)后写空间。

为什么是第一种情况,<?= $Var; ?>,忽略闭幕PHP标记后的换行?我真的找不到任何东西,因为谷歌带来了太多的结果,为什么你应该忽略我做的每一个搜索结束标签,而不是我想找到的结果。

+1

我刚刚在使用'''在HTML'

'标签内的多行结尾处输出变量时,我注意到了这个问题。我需要在每一行之后添加一个额外的换行符以保留文本输出格式正确,但我真的不喜欢这种行为,尽管我能理解为什么当文件以'?>'结尾时(可能是一个不包含任何内容的包含文件),可能会添加这个文件以防止在文件末尾出现多余的换行符输出。 –
                        
                            
    Haprog
                                
                            
                        
                    

回答

10

更新:
望着Zend的语言扫描仪SRC,它似乎是我的“预感”是正确的:在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 */ 
} 

只要看看T_CLOSE_TAGthe zend_language_scanner.c and zend_language_scanner.l files here


我扫描当前Zend引擎的来源,可以肯定的,但我猜,因为你发布的代码的最后一个字符(或多个),简单地说,结束标记(?>),这是该PHP正在生成输出。鉴于您不会告诉PHP输出换行符,这意味着PHP不会为您回应的任何内容添加新行。
后面的结束标记,当然,用PHP忽略,但由于某些原因,PHP确实似乎消耗该行料换行字符。我看它分析你的PHP脚本的C代码,但我想作为输入令牌,以块为节点可能会使用新行,空格,逗号分号和所有。
看到结束标记?>是一个真实的标记,也是PHP语法的一部分,很可能这是引擎有效使用换行的原因,也是它为什么不是输出的一部分。

通过关闭标签后添加一个空格字符,空间可能会被消耗掉,但新线是没有,所以这可能是为什么你看到的仍然是换行显示。
我也尝试添加2个换行符一些测试代码,而事实上:输出显示,只有1个新行:

foo: 
    <?= $bar; ?> 

    foobar 

输出:

foo: 
    bar 
    foobar 

所以它似乎是我的怀疑可能会有水。

然而,考虑到所有的事情,为了避免你想要在Zend引擎源代码中进行攻击,手动添加换行符并不是什么大问题。实际上,确保正确的生成的换行符是一个好方法:
假设您在健康* NIX系统上编写了一些代码,其中换行符为\n代表的所有意图和目的序列,手动添加字符可能不会产生所需的输出,例如,Windows系统(使用\r\n),Apple系统使用\r ...
PHP有一个常量,以确保您生成正确的换行符,具体取决于代码运行的平台:PHP_EOL。为什么不使用:

<?= $bar, PHP_EOL; ?> 

如果你想知道:是的,这是$bar逗号PHP_EOL你看到那里。为什么?把echo<?=想象成C++的COUT,它是一个构造,它只是将你要抛出的东西推到输出流上,把它变成一个串联的字符串,或者只是一个用逗号分隔的变量列表:它并不关心

现在,我的回答下面的部分稍微会偏离主题,但它只是一些所以基本和不言自明的,但仍然有许多人是如此未意识到这一点,我不能抵制解释关于字符串连接的一两件事的诱惑。
PHP和我知道的大多数语言都不关心它必须推送到输出流有多少个变量/ val。这是它的目的。 PHP,并再次:大多数语言,确实关心字符串串联:字符串是一种常数值。心情带给你的时候,你不能再长一段。一系列字符必须存储在内存中,必须分配内存以容纳更长的字符串。什么级联有效呢(最好的情况下),是这样的:字符串1和字符串的

  • 计算长度
  • 分配给Concat的字符串2到字符串需要额外的内存1
  • 复制串2到新(额外)分配的内存

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

  • 补偿两个字符串
  • 分配内存,以Concat的两个字符串
  • 副本所需的UTE长度两个字符串到新分配的内存块
  • 分配新指针的变数,需要分配
  • 释放所有内存ISN”吨引用了

第一种情况的一个例子:

$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,如此基本,我感觉好像每个人都应该知道这一点...

+0

我希望我可以不止一次地对此赞赏。我已经使用'normalize()'函数来转换所有换行符,这可能不是问题。关于正在使用的换行符,我可能需要更改标题。但是,它会改变每个人的假设AFAIK,因为单个空格或单个换行符显然不会发送任何头文件。最后,虽然我只知道我在C的基本课程中学到了什么,但我完全理解了你对连接的解释。但是,我认为在回声PHP中'足够聪明',因为不分配任何额外的变量。谢谢你的伟大答案。 –

+1

@FranciscoPresencia:很高兴帮助...我不是说这是一个坏的方式,但_assuming'echo'足够聪明_不是一个选项:'echo'只能回显已被评估为单个值的表达式:'回声1 + 1;'不回声'1 + 1',回声'2'。同样,对于串联字符串:串联必须具有更高的基数,因为您可能会回应实现'__toString'方法的数组或对象,必须在回调任何内容之前调用__toString方法(问题通知,警告......所需的一切优先于'echo')... –

+0

好吧,所以从现在开始,每次我需要回显一个串联时,我会这样做,而不是像你指出的那样'echo $ str1,$ str2;'。我甚至发现[一些指标](http://www.electrictoolbox.com/php-echo-commas-vs-concatenation/)。 –