Закрывающий тег PHP удаляет перевод строки


Я провожу эксперимент , препроцессор html, такой как SLIM или Jade.

Это 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? Я действительно ничего не мог найти, так как Google принес слишком много результатов о том, почему вы должны игнорировать закрывающий тег для каждого поиска, который я делал, а не то, что я хотел найти.

Author: Francisco Presencia, 2013-12-15

1 answers

Обновление:
Глядя на src сканера языка zend, может показаться, что моя "догадка" была правильной: маркер 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_TAG в файлах zend_language_scanner.c и zend_language_scanner.l здесь


В настоящее время я сканирование источника движка Zend, чтобы быть уверенным, но я бы предположил, что, поскольку последние символы кода, которые вы опубликовали, являются просто закрывающим тегом (?>), PHP генерирует вывод. Поскольку вы не говорите PHP выводить перевод строки, само собой разумеется, что PHP не добавит новую строку к тому, что вы повторяете.
Символ перевода строки, следующий за закрывающим тегом, конечно, игнорируется PHP, но по какой-то причине PHP, похоже, действительно использует этот перевод строки. Я ищу в коде C, который анализирует ваш PHP-скрипт, но я думаю, что он может использовать новые строки, пробелы, точки с запятой и все такое в качестве маркеров для разделения входных данных на узлы.
Учитывая, что закрывающий тег ?> является добросовестным маркером и частью грамматики PHP, вполне может быть, что именно здесь поток строк эффективно используется движком и почему он не является частью вывода.

Добавляя символ пробела после закрывающего тега, пробел может быть использован, но новая строка не используется, так что, возможно, именно поэтому вы все еще видите, как отображается строка.
Я также попытался добавить 2 перевода строк в некоторый тестовый код, и действительно: вывод показал только 1 новую строку:

foo:
    <?= $bar; ?>

    foobar

Вывод:

foo:
    bar
    foobar

Так что, похоже, мои подозрения могут иметь под собой почву.

Однако, учитывая все обстоятельства, если вы не хотите взламывать исходный код движка Zend, добавление перевода строки вручную не так уж и сложно. На самом деле, это хороший способ обеспечить правильное генерируются потоки строк:
Предположим, вы написали какой-то код в системе healty *NIX, где потоки строк, по сути, представлены escape-последовательностью \n, добавив, что char вручную может не дать желаемого результата, скажем, в системе Windows (которая использует \r\n), системы Apple используют \r...
В PHP есть константа, гарантирующая, что вы создаете правильные потоки строк, в зависимости от платформы, на которой выполняется ваш код: PHP_EOL. Почему бы не использовать это:

<?= $bar, PHP_EOL; ?>

В случае, если вы задаетесь вопросом: да, это так $bar запятая PHP_EOL ты видишь это там. Почему? Подумайте о echo или <?= как о C++ COUT, это конструкция, которая просто выталкивает все, что вы бросаете в нее, в выходной поток, будь то объединенная строка или просто список переменных, разделенных запятыми: это неважно.

Теперь следующий раздел моего ответа немного отклоняется от темы, но это просто что-то такое базовое и самоочевидное, и все же многие люди вы настолько не осознаете этого, что я не могу устоять перед искушением объяснить пару вещей о сцеплении строк.
PHP, как и большинство известных мне языков, не заботится о том, сколько vars/val он должен передать в выходной поток. Это то, для чего он предназначен. PHP и еще раз: большинство языков заботится о конкатенации строк: строка - это своего рода постоянное значение. Вы не можете просто сделать струну длиннее, когда у вас такое настроение. Ряд символов должен храниться в памяти, памяти, которая должен быть выделен для размещения более длинной строки. Что эффективно делает конкатенация (в лучшем случае), так это:

  • вычислить длину строки1 и строки2
  • выделите дополнительную память, необходимую для объединения строки 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 намного проще, чем даже начать обдумывать написание кода требуется правильно объединить все эти значения вместе...
Извините, что немного отвлекся, но, видя, как это, ИМО, так просто, я чувствую, что все должны знать об этом...

 10
Author: Elias Van Ootegem, 2013-12-15 20:46:24