7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

<?php...?>タグの外側の文字列はどういう扱いなのか

Posted at

はじめに

phpでswitch文の「switch($hoge)★ここ★{case0:」に改行いれるとエラーになる。を読んでこれどうなってるんだろうと思ったので調べて見ました。

PHPのマニュアルのPHPタグのところには

つまり、開始タグと終了タグで囲まれている 箇所以外のすべての部分は、PHP パーサに無視されます。

と書いてあるので何を挟んでも良さそうに読めるのですが...

結論

<?php...?>の外側の文字列は、内部的にはその文字列を出力するecho文のように扱われます。ただし?>の直後の改行は除きます。パーサに無視されてなどいなかったのです。

したがって、echo文が許されないところで?>でタグを終了して次の<?phpとの間に1文字でも挟むと構文エラーになります。

先の記事で駄目だった例の

<?php
$i = 0;
?>

<?php
switch($i){
?>

<?php
 case 0:
?>
<p>aaa</a>

<?php
break;
}

<?php
$i = 0;
echo "\n";
switch($i){
 echo "\n";
 case 0:
echo "<p>aaa</a>\n";
break;
}

と書くのと一緒なので、switchの直後の最初のcaseの前にはecho文を書いてはいけないのと同じ意味でNGということになります。

詳しい話

マニュアルを読んでもわからないので該当部分のソースを見てみました。

字句解析

字句解析はZend/zend_language_scannar.lで行っています。

phpタグの外側

INITIALはphpタグの外側にいる状態で、これより前に<?phpなどは処理されているのでそれらに当てはまらない文字列がここで処理されます。最終的にはT_INLINE_HTMLというトークンになります。

<INITIAL>{ANY_CHAR} {
        // 長いので略
	RETURN_TOKEN(T_INLINE_HTML);
}

?>の直後の改行

以下のようになっており、直後の文字が改行なら閉じタグの一部として食われます。だから次に来る<?phpとの間に改行一つだけ挟むのはセーフ。

<ST_IN_SCRIPTING>"?>"{NEWLINE}? {
	BEGIN(INITIAL);
	if (yytext[yyleng-1] != '>') {
		CG(increment_lineno) = 1;
	}
	RETURN_TOKEN(T_CLOSE_TAG);  /* implicit ';' at php-end tag */
}

構文解析

構文解析はZend/zend_language_parser.yで行っています。T_INLINE_HTMLが出てくるのはstatementのところです。ひとつの文として扱われていることがわかります。

statement:
		'{' inner_statement_list '}' { $$ = $2; }
	|	if_stmt { $$ = $1; }
	|	alt_if_stmt { $$ = $1; }
	|	T_WHILE '(' expr ')' while_statement
			{ $$ = zend_ast_create(ZEND_AST_WHILE, $3, $5); }
	|	T_DO statement T_WHILE '(' expr ')' ';'
			{ $$ = zend_ast_create(ZEND_AST_DO_WHILE, $2, $5); }
	|	T_FOR '(' for_exprs ';' for_exprs ';' for_exprs ')' for_statement
			{ $$ = zend_ast_create(ZEND_AST_FOR, $3, $5, $7, $9); }
	|	T_SWITCH '(' expr ')' switch_case_list
			{ $$ = zend_ast_create(ZEND_AST_SWITCH, $3, $5); }
	|	T_BREAK optional_expr ';'		{ $$ = zend_ast_create(ZEND_AST_BREAK, $2); }
	|	T_CONTINUE optional_expr ';'	{ $$ = zend_ast_create(ZEND_AST_CONTINUE, $2); }
	|	T_RETURN optional_expr ';'		{ $$ = zend_ast_create(ZEND_AST_RETURN, $2); }
	|	T_GLOBAL global_var_list ';'	{ $$ = $2; }
	|	T_STATIC static_var_list ';'	{ $$ = $2; }
	|	T_ECHO echo_expr_list ';'		{ $$ = $2; }
	|	T_INLINE_HTML { $$ = zend_ast_create(ZEND_AST_ECHO, $1); }
	|	expr ';' { $$ = $1; }
	|	T_UNSET '(' unset_variables ')' ';' { $$ = $3; }
	|	T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement
			{ $$ = zend_ast_create(ZEND_AST_FOREACH, $3, $5, NULL, $7); }
	|	T_FOREACH '(' expr T_AS foreach_variable T_DOUBLE_ARROW foreach_variable ')'
		foreach_statement
			{ $$ = zend_ast_create(ZEND_AST_FOREACH, $3, $7, $5, $9); }
	|	T_DECLARE '(' const_list ')'
			{ zend_handle_encoding_declaration($3); }
		declare_statement
			{ $$ = zend_ast_create(ZEND_AST_DECLARE, $3, $6); }
	|	';'	/* empty statement */ { $$ = NULL; }
	|	T_TRY '{' inner_statement_list '}' catch_list finally_statement
			{ $$ = zend_ast_create(ZEND_AST_TRY, $3, $5, $6); }
	|	T_THROW expr ';' { $$ = zend_ast_create(ZEND_AST_THROW, $2); }
	|	T_GOTO T_STRING ';' { $$ = zend_ast_create(ZEND_AST_GOTO, $2); }
	|	T_STRING ':' { $$ = zend_ast_create(ZEND_AST_LABEL, $1); }
;
7
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?