なぜPHPファイルは <?php が省略できないのか

:::note
短い答え：歴史的経緯
:::

:::note warn
別の答え：実装すればできなくはない *(が、望みは薄い…)*
:::

## PHPが`<?php`開始タグを要求する理由

元々PHPはHTMLに埋め込むテンプレートエンジンのようなものだったからです。

https://zenn.dev/pixiv/articles/756ce0bcbdd1be

<details><summary>Rasmusが紹介する1994年、1995年、2004年のPHPコード(イメージ)</summary>

[![1994's PHP Code](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/7d5aa85e-b7bc-465e-8d57-63f90c6485b5.png)](https://talks.php.net/show/mysql07key/3)
[![1995's PHP Code](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/5fcb1056-175c-4204-9298-3f3ff0b417a0.png)](https://talks.php.net/show/mysql07key/4)
[![2005's PHP Code](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4121/1a1ce024-a2e6-48ad-8be9-ae6fb59ec601.png)](https://talks.php.net/show/mysql07key/5)

</details>

現代のバージョンでも動く、PHPによるHTMLテンプレートのイメージはこのような感じでしょうか：

```html.php
<html>
<h1><?= htmlspecialchars($title)?></h1>
<p>
<?php if (date('H') >= 12): ?>
    午後です
<?php else: ?>
    午前です
<?php endif ?>
</p>
</html>
```

一方で、現在のPHPファイルの大部分は以下のようなクラス定義や関数定義だけが置かれているものが大半です。

```php
<?php

declare(strict_types=1);

namespace App;

class Foo
{
    // ...
}
```

HTMLが混在することはないのに `<?php` から始まるのは、およそ痕跡的なものだと言っていいでしょう。

## 字句解析上での `<?php` `?>` の扱い

[**字句解析**](https://ja.wikipedia.org/wiki/%E5%AD%97%E5%8F%A5%E8%A7%A3%E6%9E%90)(*lexical analysis*)とは構文解析の1ステップです。次のようなソースコードを字句解析することを考えてみましょう。

```php
if ($a == true) { echo "foo bar"; }
```

このようなソースコードの文字列を`['if', '(', '$a', '==', 'true', ')', '{', 'echo', '"foo bar"', ';', '}']`のような配列に分解する作業が字句解析です。

PHPコードの標準関数には[`token_get_all()`](https://www.php.net/token_get_all)関数があります。この関数は文字列を分解するだけでなく、どのような種類の文字の塊なのかの意味付け(トークナイズ)までをセットで行なってくれます。

`token_get_all()`の結果を読みやすく可視化すると次のようになります。

```php
atama<?php echo "karada"; ?>oshiri
```

```json
[
    {
        "T_INLINE_HTML": "atama"
    },
    {
        "T_OPEN_TAG": "<?php "
    },
    {
        "T_ECHO": "echo"
    },
    {
        "T_WHITESPACE": " "
    },
    {
        "T_CONSTANT_ENCAPSED_STRING": "\"karada\""
    },
    {
        "": ";"
    },
    {
        "T_WHITESPACE": " "
    },
    {
        "T_CLOSE_TAG": "?>"
    },
    {
        "T_INLINE_HTML": "oshiri"
    }
]
```

気になる方は以下のページでいろいろ弄れます。

https://php-play.dev/?c=DwfgDgFmBQ0CQGMD2ATApgAgLweHg5AAoASh%2B0AhgC4UC2FokYGaCESGARANYUBOFFBU4BuDCAB8SAM4QAlnznQShEbFbsMAK2lIAdgH00e5OgAU-AQE8D9MGegYnGAGZ6MZuFSTcAlNgkMeio2Dyo%2BAFc0fwBvR2cEuWkDSworT28-AIwAbUzjAz06NAyfHIAGAF1-LECvMoBGSsqAGniEp3QXCgiAGypsnPx8bPruVvaMAF82hPzDAHM0KhTe3s9TaOhfFowAKQBlAHkAOQNCACUAUQAVG4BNc4uASROb3zVJaCA&v=8.4&f=console

PHPは`Hello world`と書くだけで[Hello world](https://ja.wikipedia.org/wiki/Hello_world)が出力できるプログラミング言語として知られていますが、そのソースコードを字句解析するとこうなります。

```php
Hello, world!
```

```php:hello.php
[
    {
        "T_INLINE_HTML": "Hello, world!"
    }
]
```

## オペコードコンパイル後の `<?php` `?>` の扱い

[**バイトコード**](https://ja.wikipedia.org/wiki/%E3%83%90%E3%82%A4%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89)(bytecode)とは、PHPの構文で書かれたソースコードを解釈して、よりシンプルな形式の命令列として表現する中間表現のことです。PHPの文脈では**オペコード**(opcode)と呼ばれますが、同じ意味です。

先ほどのコードを改めてバイトコード(オペコード)コンパイルしてみましょう。

```php
atama<?php echo "karada"; ?>oshiri
```

オペコードコンパイルされたPHPコードを目視確認する方法はいくつかありますが、Webでは3v4lの機能を使ってVLDの出力を表示するのが楽です。

https://3v4l.org/2sPj0/vld

```
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /in/2sPj0
function name:  (null)
number of ops:  4
compiled vars:  none
line      #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
    1     0  E >   ECHO                                                     'atama'
          1        ECHO                                                     'karada'
          2        ECHO                                                     'oshiri'
          3      > RETURN                                                   1
```

`op`というのが命令、`operands`というのが引数のことです。

つまり、`<?php ... ?>`の外側に書いたコードは、その内容を文字列として`echo`に渡すのと同じで、以下のPHPコードと等価ということになります。

```php
<?php
echo 'atama';
echo 'karada';
echo 'oshiri';
```

## `<?php ... ?>`タグの何が問題なのか

玄関で抜いだ靴はきちんと揃えないと気が済まない几帳面な性格の方にとっては非常に不満があることは承知していますが、現代のPHPコードでは `<?php` を書いても **`?>` は書かない**のが基本的な慣習になっています。

ここまで説明したように `<?php ... ?>` の外側は `echo` と等価だというのがPHPの構文および実行時の基本的な振る舞いなので、何か出力する必要がなければ`?>`で終端することによって得られるメリットは特にありません。

また、`<?php`の前や`?>`の後に***うっかり***何か文字を入力されると意図しない`echo`が発生することになります。PHPでの意図しない`echo`出力はHTTPレスポンスに影響しますので、これによって適切なHTTPヘッダを返せないこともあり動作を壊します。

これを防ぐには、PHPUnitで`beStrictAboutOutputDuringTests="true"`の設定をしてあればテスト対象の実行時に意図しない出力があったときに警告を出したりPHPStanのルールでチェックすることもできますが… 経験則では、そういった仕組みが用意されていないプロジェクトに限って `?>` を付けることに拘っているという ~~*偏見*~~ 経験則があります。

## 意図しない出力の害

意図しないスペースが出力されちゃっても」表示崩れなんてそうそう起こらないんじゃないの？」と考える方もいらっしゃるかもしれませんが、大きく分けると二つの害があります。

 1. HTTPヘッダが出力できない
 2. `namespace`や`declare(strict_types=1);`が追加できない

**1**について、HTTPメッセージはヘッダとボディに分かれているので、何かを`echo`すると、その場でヘッダを吐き出してボディの出力を始めてしまいます。その後に[`header()`](https://www.php.net/header)関数や[`setcookie()`](https://www.php.net/setcookie)関数を呼ぶと期待通りに機能しません。

**2**については… 説明するのがめんどくさいので以下の記事を読んでください。

https://qiita.com/tadsan/items/e5fe17eb5f4e46defad5

## PHPから`<?php`タグを消そうとする試み

現代的なPHPプロジェクトでは、ほとんどのファイルではPHPの処理やクラス定義とHTMLテンプレートを混在させることはないので、`<?php`は生存の役に立たない盲腸のようなものだと考えることもできます。

そのため、`<?php`がなくてもコードを書けるようとしようという提案は何度か行なわれています。

### moriyoshiによる提案(四月馬鹿)

PHP開発者の小泉守義さんが:cherry_blossom:2012年4月1日:cherry_blossom:に起草した提案です。

本文のアーカイブは現存していないのですが、手の込んだことにパッチが用意されています。

* https://gist.github.com/moriyoshi/2266652

moriyoshiさんは4月1日中に提案本文を削除しており、その意図するところは*明らか*でしょう……。

:::note
moriyoshiさんが提案して実装された機能としては[ビルトインサーバー](https://wiki.php.net/rfc/builtinwebserver)があります。
:::

### Boutellによる提案 (Source Files Without Opening Tag)

2012年4月8日にThomas Boutellが起草した提案です。

https://wiki.php.net/rfc/source_files_without_opening_tag

https://externals.io/message/59466


ざっくり2種類の案を提示しています。

 1. ファイルロード時にフラグを明示させる
    * `require 'file.php';` 従来通りのHTMLが混在されてる(かもしれない)コード
    * `require 'file.php' AS INCLUDE_PURE_CODE;` HTMLが含まれない純粋なPHPファイル
 2. ファイル名の拡張子で区別する
    * `file.php` ← 従来通りのHTMLが混在されてる(かもしれない)コード
    * `file.phpp` ← HTMLが含まれない純粋なPHPファイル


> I have abandoned this proposal. I have come to feel it changes the spirit of PHP too much, offering too little gain for the degree of unhappiness it inspires and the potential for confusion it creates. I am leaving it here for historical purposes. -Tom Boutell

>  私はこの提案を断念しました。この提案は PHP の精神を大きく変え、不幸の度合いや混乱の可能性に比べて得られるものが少なすぎると感じたからです。歴史的目的のためにここに残しておきます。 -Tom Boutell

ということで断念されています。

:::note
提案自体でもオートローダーは考慮されていますが、現代ではほとんど全てのPHPコードはComposer経由でオートロードされるものになってしまったので、使いどころがあまりない提案になってしまっているという個人的な雑感があります。
:::

### Craigによる提案 (New File Type for Pure-Code PHP Scripts)

上記の議論にも参加していたKris Craigが2012年4月12日に起草した提案です。

https://wiki.php.net/rfc/phpp

https://externals.io/message/59807

Boutell提案(2)の拡張子を切り出したような内容ですが、`.php` `.phpp` `.phpf`の3種類の拡張子に分けることを提案しています。

:::note
先のBoutell提案にも言えることですが、パッチもなく実現可能性があんまりなくて、何が嬉しいのかがいまいち伝わってこない、要領を得ない提案という感じがします。
:::


:::note warn
[2010年にロールバックされたはずのPHP 6](https://news-web.php.net/php.internals/47120)がターゲットなのは現代人にとっては？？？という感じなのですが、これは仕方のないことで、[PHP 6.0をスキップして時期メジャーバージョンを7.0にする](https://wiki.php.net/rfc/php6)方針が定まったのは2014年です。
:::

### yohgakiによる提案 (Optional PHP tags by php.ini and CLI options)


2012年4月9日に大垣靖男さん(yohgaki)がmoriyoshi提案を復活させる形で復活させたものです。

https://wiki.php.net/rfc/nophptags

https://externals.io/message/72429

要約すると、この提案で提案しているのは以下の2点です：

 1. `template_mode=off`の導入
    * `php.ini`, 環境変数, コマンドラインオプションで制御可能
    * 冒頭の `<?php` のみ互換性のために許可
    * 途中の `<?php` はパースエラー
    * 最後の `?>` 以降は互換性ために無視する
 2. `template_mode`の影響を受けない `script` / `script_once()` 言語構造の導入

この提案で主張されているのは**LFI攻撃**(Local File Inclusion)への耐性強化です。パッチが添付されているところが上記の2つと違うところですが… これはmoriyoshiさんのパッチをそのまま載せているだけで、提案された機能は特に実装されていません。

:::note
これも私見にはなりますが… 「既存コードと完全に互換性がある」ということを謳っているものの、実行時設定やロード方法によってパース結果が異なるというところ、物凄く不穏な気配があります……。
フレームワークでLFI攻撃がいくつも報告されているということが挙げられているものの、[PHP: include - Manual](https://www.php.net/manual/ja/function.include.php)で警告されているような筋の悪いファイル読み込みが原因であり、この提案による緩和効果は著しく限定的に思えます。
:::

こちらの提案も建設的な議論は進まず、Rasmus Lerdorf(PHPの原作者)からも<q>ここで防止しようとしているずさんなコーディングの問題よりも、おそらくより深刻なセキュリティ問題を引き起こす</q>と言われてしまいました。私もそう思います。

### yohgakiによる提案2 (Introduce script only include/require)

前から時を経て、2015年2月22日に大垣靖男さん(yohgaki)が再提出した提案です。これは直接`<?php`を省略する機能を提案するものではないのですが、一連の流れの中にあるので紹介しましょう。

https://wiki.php.net/rfc/script_only_include

https://externals.io/message/83488

この提案は`ini_set('zend.script_extensions', '.php .phtml .inc .module .etc');`という実行時設定を追加し、指定された拡張子のファイルだけを`include`/`require`できるようにしようというものです。

ここで問題提起しているのは、PHPの`include`/`require`が他の言語の構文と比べて動的で、LFI攻撃に脆弱な傾向があるということがあります。

```python:python
# モジュール検索パスを走査して foo モジュールをインポートする
import foo
```

```ruby:ruby
# ライブラリ探索パスを走査して foo.rb を読み込む
require 'foo'
```

Pythonではモジュールの動的インポートには[`importlib`](https://docs.python.org/ja/3.13/library/importlib.html)という普通に使わない機能を使う必要があり、Rubyの[`Kernel.#require`](https://docs.ruby-lang.org/ja/latest/method/Kernel/m/require.html)も絶対パスを指定することは可能ですが有効な拡張子が`*.rb`と動的モジュール(`*.so`など、OS依存)に限られています。

一方でPHPはどんなファイルでも指定することが原理的に可能で、ファイル内に`<?php`を書かなければファイルの中身が全て漏洩してしまうので危険だという指摘それ自体は正しいのですが…

:::note
そもそも**外部入力を引数にしてファイルを動的ロードしないでください**。そのような見る人が見れば一瞬で「匂う」コードでも[フールプルーフ](https://ja.wikipedia.org/wiki/%E3%83%95%E3%83%BC%E3%83%AB%E3%83%97%E3%83%AB%E3%83%BC%E3%83%95)にしたいという思想自体はわかりますが、この箇所を改善することで全体的な安全性はそれほど向上しないという意見は妥当だと思います。
:::

この提案は実際に投票まで進んだ上で否決されています。

## Hack

ところで、Meta社(Facebook)が開発している**HHVM**(HipHop Virtual Machine)という言語処理系と、[Hack](https://hacklang.org/)というプログラミング言語をご存じでしょうか……？ もはや旧聞に属する話題なのですが、かつてこのHHVMはPHPの互換処理系として開発され、HackもPHPを発展させた言語として開発されていました。

Hackは2013年に公開された言語ですが、既存PHPコードの冒頭の`<?php`を`<?hh`に置き換えることでHackとして実行できるようになっていました *(過去形)*。基本的にはHackはPHPのスーパーセットだった(当時)のですが、この言語では `?>` で閉じて出力するようなことはできないようになっています。(つまり、LFI攻撃耐性があります)

また、2019年2月リリースの[HHVM 4.0.0](https://hhvm.com/blog/2019/02/11/hhvm-4.0.0.html)からはPHPサポートを終了してHack専用の言語処理系になったほか、拡張子を`.hack`にするとファイル冒頭の`<?hh`開始タグが不要になりました。つまりは拡張子によって挙動を変えるBoutell案やCraig案に近い実装です。

## 銀河戦争P++

これも2019年のことなのですが、**P++** という「野心的な」厳格バージョンのPHPを開発しようというアイディアが発表されたことがありました。

https://wiki.php.net/pplusplus/faq

この事件の契機になったのは `<? ... ?>` という[ショートタグ](https://www.php.net/manual/ja/language.basic-syntax.phptags.php)を廃止しようとしたことによる議論でした。このタグは設定によって無効化されるので、特にライブラリコードなどは、必ず `<?php` か `<?=` だけを使用しなければいけません。

これを廃止して「構文解析処理をシンプルにしたい」vs「既存コードに手を加えて`<?`を`<?php`に変換させる必要があるのか」という大議論です。

https://qiita.com/tadsan/items/80cd844ee612c6cadabf

事件の顛末について気になる方は上記の記事を読んでいただきたいのですが、「壊れていないものを直す必要があるのか」「極端に筋が悪いコードの問題を塞ぐために全利用者の実行環境に影響を及ぼす必要があるのか」というのは持つべき非常に重要な観点かと思います。

## まとめ

後半は非常に話がぶれてしまった感がありますが、結局のところは冒頭でまとめた通りです。

 * `<?php`が必要なのはHTMLテンプレートが出自であるゆえの歴史的経緯である
 * `<?php`を取り除くのは技術的には可能だが、懸念事項も多く簡単ではない
   * Hackは実現したので、誰かが責任を持って取り組めば可能

ここまでの提案は正直あまり冷静な議論の結果で決まったものはほとんどないので、必要だと思った人が説得力のある適切な議論ができたら取り入れられる可能性はなくもないのではないかと思います。

個人的には`.hack`みたいに専用の拡張子を用意するのが推しですが、そもそも現状のPHPファイル自体が`.php`という拡張子にあまり依存していないので、それを覆す攻め手を考える必要がありそうです。私はやらないので誰か頑張って。
PHP 12.0くらいの目玉機能になったらおもしろいかもしれませんね。