PHP Early Days (誕生から4まで)

2023/09/17に公開

この記事は何か

某社の某雑誌に寄稿しようと思って書いた記事の初稿… だったのですが、テーマじゃないところに紙幅を割きすぎて本来のテーマを大幅に逸脱した上に、書いてから三年ほど置いてしまったものです (執筆自体はまだ諦めてません)。

ちょうど「LLイベント」ことLearn Languages 2023というイベントで「PHPの20年とこれから」としてPHPの歴史を話したので、せっかくなのでこの期に公開してましょう。

https://ll.jus.or.jp/2023/


インターネットないしWorld Wide Webは1990年代末から2000年代を象徴する技術分野といえるでしょう。その中でもPHPは政府機関から個人サイトに至るまで、非常に多くのWebサイトで利用されてきました。その一方でPHP自体はこれまで普及してきたプログラミング言語の中でも、とりわけ毀誉褒貶の激しい言語だと言えます。Webセキュリティの文脈においては、2007年には情報処理推進機構(IPA)によって『例えば、PHPを避ける』と言及されたことさえありました。

https://blog.tokumaru.org/2014/12/phpphp.html

本稿では、PHPの普及から現代的なWebアプリケーションに至るまでの経緯を追っていきます。

Webという語について

日常的な語法として「インターネット」はWebと同義語のように扱われることがありますが、技術的にはもっぱらネットワークを指します。一方でWWW(World Wide Web、以後は単にWebと呼びます)はインターネット上でHTMLのようなハイパーテキストを基盤としたコンテンツを提供するシステムを総称します。本稿においてはMDN Web Docsでの用法に則ってHTMLによるコンテンツ(Webページ)のみならず、HTTPでコンテンツをサービスするサーバをWebサーバと呼びます。

https://developer.mozilla.org/ja/docs/Learn/Common_questions/Web_mechanics/What_is_a_web_server

HTTPとCGI

PHPの話題に入る前に、その前提となるWebサーバとCGI(Common Gateway Interface)について説明します。

Webサーバはクライアント(ユーザーエージェント)とHTTPで通信し、リクエストに応じたコンテンツを返すソフトウェアです。Webが返すコンテンツの種類は静的コンテンツと動的コンテンツに大別できます。サーバが保有しているファイルをHTTPレスポンスとしてそのまま返すものを静的コンテンツ、リクエストを受けてソフトウェアで生成(あるいは変更)して返すものを動的コンテンツと呼びます。

CGIはWebサーバからプログラムを実行して動的コンテンツを生成できる仕組みのひとつです。Webサーバはリクエストを受けるとプログラムを起動し、HTTPヘッダーを変数として受け渡します。起動されたプログラムは仕様通りにレスポンスヘッダーをレスポンスボディを標準出力することでWebサーバがHTTPレスポンスとして送出します。

CGI対応のプログラムは、環境変数と標準入出力の読み書きの機能を備えていればどんなプログラミング言語でも構築できます。基本的にはプログラムとして実行可能ならよいのでC言語などで書いて実行可能形式のバイナリファイルを使うこともできますし、ファイルの先頭にshebang(#!)を記述したスクリプトファイルでも構いません。

かつてはWebアプリケーションといえばCGIであり、その中でもPerlはCGIの代名詞と言ってよいほどよく使われていました。現在Wikiと呼ばれるスタイルのWebサイトのさきがけとなったのは、Ward CunninghamのWikiWikiWebです。このサイトが1995年に公開された時の実装は、PerlによるCGIスクリプトだったとのことです。

PHPの誕生 - PHP Tools 1.0

PHPの歴史によれば、Rasmus Lerdorfが1994年に自身の履歴書のオンライン公開とアクセス履歴を調べるために“Personal Home Page Tools”、縮めて「PHP Tools」を公開しました。これはC言語で書かれたCGIバイナリ群でした。そして翌1995年にはPHP Tools 1.0のソースコードをGPLで公開しました。この時期のコードはC言語で3000行程度しかありません。

PHP Toolsは1.0の時点では制御構文もないHTMLの簡易テンプレートエンジンとWebブラウザから送信されたフォームを受け付けるCGIプログラム、そしてアクセス履歴のビューアーに過ぎませんでした。PHP 2.0より前の構文は現在のものとかなり違うので本稿では詳述しませんが、HTML内に<!--$var-->で変数を埋め込むSSI(Server Side Includes)に近い構文でした(参考: とほほのSSI入門 - とほほのWWW入門)。

PHP 1.0の過激な機能としては<!--!whoami-->のようにシェルコマンドの実行結果を埋め込むことができました(C言語のレベルではlibcのsystem(3)のラッパーです)。

2010年にRasmusが来日した際のインタビュー(PHP開発者 Rasmus Lerdorf氏インタビュー ~PHPは「利己的」な開発者の集まり | gihyo.jp)によれば、この時期にRasmusがPerl CGIではなくPHP Toolsを書いた理由として、負荷の問題を挙げています。Windows 95発売前の時代のPCではリクエストごとにCGIがPerlのプロセスを起動するCGIやメモリの使用量はばかにならず、サーバが応答不能に陥ったようです。このインタビューでは明言していませんが、NCSA HTTPdのSSIに似たシンタックスで自分でハックできるものを自作したかったのではないかと、筆者は考えています。

2007年のMySQL Conferenceの基調講演で「1994年のPHP」として紹介していたコードをそのまま引きましょう。

<!--getenv HTTP_USER_AGENT-->
<!--ifsubstr $exec_result Mozilla-->
Hey, you are using Netscape!<p>
<!--endif-->

<!--sql database select * from table where user='$username'-->

<!--ifless $numentries 1-->
Sorry, that record does not exist<p>
<!--endif exit-->

Welcome <!--$user-->!<p>
You have <!--$index:0--> credits left in your account.<p>

<!--include /text/footer.html-->

http://talks.php.net/show/mysql07key/3 より引用

このバージョンのPHPではプログラミング言語らしい複雑な式は実装されず、ifsubstr $var substringifless $var 1のような比較式とキーワードが一対一対応していることが目立ちます。これに関してはPHP 1.0はプログラミング言語というよりSSIのようなテンプレートエンジンと呼ぶべきもので、後述するように構文解析を不得手としていたため簡単に処理できることを志向していたと見られます。

1994年当時のPHPで使われていたのはmSQL (Mini SQL)という、非営利目的での利用に無償提供されているSQL製品でした。これはいわゆるフリーソフトウェア・オープンソースソフトウェアではありませんが、当時から現在までソースコードが公開されています。

PHP/FI 2.0の公開と拡がり

RasmusはPHPを「FI(Form Interpreter)」や「Personal Home Page Construction Kit」として書き直し、1996年2月には「PHP/FI 2.0」として公開しました。この2.0はただのツールを超えてプログラミング言語としての能力を持ち、じわじわとシェアを伸ばしていた様子が伺えます。この頃のPHPの構文は現在とは互換性はありませんが、設計思想として通じるものがあります。

$entry = $dirs[$ii];
while ($entry);
	if (reg_match(".*\.log$",$entry) || reg_match(".*\.log.dir$",$entry) || reg_match(".*\.log.db$",$entry));
        if (reg_match(".*\.log.dir$",$entry));
            $logfile = $logdir+"/$entry" - strrchr($entry,".dir");
        elseif (reg_match(".*\.log.db$",$entry));
            $logfile = $logdir+"/$entry" - strrchr($entry,".db");
        else;
            $logfile = $logdir+"/$entry";
        endif;
    # 中略
endwhile;
if ($first==1)>
	</table></center>
<?endif>
<center><form action="<?echo $PHP_SELF><?if($QUERY_STRING)>?<?echo $QUERY_STRING><?endif>" method="POST">

https://museum.php.net/php2/php-2.0b7.tar.gz より examples/log.html を一部抜粋、改変。

このコードからPHP/FI 2.0の構文と機能について把握できることはいくつかあります。

  • PHPタグの終端は >?>ではない)
  • 制御構文のキーワード while if 条件式と節を区切るトークンが ;
  • 文字列の加算+および減算-演算子が定義されている
    • strrchr()は現存する関数
    • string - int で末尾の文字を削り取る
  • $PHP_SELF $QUERY_STRING がスーパーグローバル変数として定義されている
  • HTML出力時のエスケープは必要ない

セキュリティを考慮すると気になるのは特に最後の点ですが、PHP/FI 2.0のechoにはHTMLエスケープ機能が備わっていました。PHP/FIはこの時点で既にCGIとして動かすだけではなく、Apacheモジュールとして組み込むこともできました。また、このバージョンのechoprintf()関数に似た機能でした。

PHP: PHP/FI Version 2.0 Documentation #User-Defined Functionに掲載されている別の例も引きましょう。

<?php

function Sum $a,$b,$c (
    return($a+$b+$c);
);

echo Sum(1, 2, 3);

while ($entry);という構文について、Rasmus自身は先述した2007年のMySQL Conferenceにおいても、言語デザインを含む「構文解析の実装が苦手だった」旨の発言をしています。現代でも「プログラミングは目的ではなく手段に過ぎない(手段と目的を履き違えるな)」といったことがしばしば言及されますが、Rasmusは「プログラミングは好きではない」「手段・道具でしかない」ということを表明しています。

Rasmusは自身を「純粋に利己的なオープンソース開発者」と分析しており、、PHP/FIの大部分はトロント大学の案件のために書かれたようです。1996年当時はまだ「オープンソース」という用語は生まれておらず、Rasmusは「どうやってやったのか」と尋ねられたためにPHPのソースコードを開示し、「寝ている間も世界中からバグ修正のパッチが飛んでくる」という体制になり、顧客からも特に問題視されなかったようです。

Rasmusは私利私欲のためにPHPを開発したとしながらも、オープンソースプロジェクトを運営するためには「すべてを自分のものにしてしまう」のではなく貢献する人々が報いられることを意識しなければうまくゆかず、「最初はそれが難しかった」と述懐しています。そのために、Rasmusは「コントロールを放棄し、一歩下がって特別な権限を持たず、ほかの人と同じように貢献する」ことを基本的なスタンスとしています。オープンソースプロジェクトの創始者はしばしばBDFL(優しい終身の独裁者)と称され、プロジェクトの決定権を委ねられることがあります。Rasmusも稀にBDFLの一人として名前を挙げられることがありますが、あくまでPHP Group一員、PHPの最初の開発者に過ぎず、投票に一票を投じる以上の決定権を下すことはありません。

なお、この頃(1996〜1997年)までには日本にもPHP/FIユーザーコミュニティが成立していたようで、廣川類さんの「PHP日本語ページ」が既に公開されていました。Internet Archive Wayback Machineの最古のアーカイブは1996年4月時点ですが、PHP/FIのQ&AやPHP 2.0や日本語パッチリリース情報について言及があります。

そしてPHP 3へ

本格的にPHPにとっての画期になるのが次のPHP 3.0です。

1997年、イスラエルの学生だったAndi GutmansとZeev Suraskiは大学のプロジェクトでECサイトを構築するためにPHP/FI 2.0を使おうとしていましたが、ECサイトを構築するには機能が不足していたため、二人はPHPを構文解析から書き直すことにしました。この二人はRusmusと連絡をとり、正式に共同でPHP/FI 2.0の後継となるバージョンの開発に取り組むことになりました。

そうして装いリリースされたのがPHP 3.0です。このときPHPというソフトウェアの正式名称も“PHP: Hypertext Preprocessor”とリブランディングされました。

このバージョンにおいて特筆すべき点としては、プログラミングとしての構文がみたび整理された点、オブジェクト指向プログラミングのための機能が採用されたことを挙げられるでしょう。

PHP/FI 2.0の時点では<?if(...);<?endif>のような記法でしたが、PHP 3.0では制御構文開始の記号が;から:になりました。また、<?php endif ?> のように書けるようになりました。ただしif (...) { ... }というC言語風の制御構文はPHP/FI 2.0の時点で存在していました。ソースコードには“PHP HTML Embedded Scripting Language”という記述もあり、この時代のPHPはあくまでHTMLに埋め込むものとして設計されていたことも伺えます。

このとき、現在の関数と同じC言語風の関数定義構文が追加されました。

<?php

function Sum2 ($a,$b,$c) {
    return($a+$b+$c);
}

echo Sum2(1, 2, 3);

オブジェクト指向機能に関してはクラス定義とプロパティ・メソッド呼び出し構文の追加など最低限のように見えますが、現在に続く構文として導入されています。

PHP 3.0系のリリース時期に関しては明確なソースを発見できなかったのですが、WEB+DB Press Vol.1のPHP特集記事での紹介によれば以下のような時系列だったようです[6]。

PHP 3.0a……1997年10月リリース
PHP 3.0b……1997年12月リリース
PHP 3.0……1998年6月リリース

PHP 3.0ではhtmlspecialchars()など現在でも必要とされる関数が定義されました。現在公開されているPHP 3.0系の最古のコードであるphp-3.0b4では126個の組込み関数がありましたが、php-3.0.18では201個まで増えました。この増分にもvar_dump()register_shutdown_function()serialize()など現在でも欠かせない関数が多く追加されています。

PHP 4の時代

PHP 3の時代からPHPの開発に加わったZeevとAndiの二人組ですが、その名を歴史に刻むことになるのは2000年にリリースされたPHP 4で導入されたZend Engineと名付けられた仮想機械(バーチャルマシン)の存在でしょう。それまでのPHPはソースコードを構文解析した結果の構文木を直接実行する古典的なインタプリタでしたが、PHP 4からはソースコードをZend Engineの命令(opcode[オペコード]とかバイトコードと呼ばれます)にコンパイルしてから実行される形式になりました。現代で実用的なスクリプト言語の多くはこの方式をとっています。

なぜスクリプト言語は仮想機械(バーチャルマシン)化するのでしょうか。毎回プログラム実行時にソースコードのパースから始めるスクリプト言語において、[字句解析→構文解析→実行]から[字句解析→構文解析→コンパイル→実行]とステップが増えるのは不利なようにも思えます。ところが実際にはこの過程を設けることでユーザー向けに提供する高級言語とシンプルな低級言語に分割でき、構文解析器と実行エンジンが疎結合になりそれぞれのコードの見通しがよくなる、見通しがよくなれば個別にパフォーマンスチューニングができるようになる、そもそもコンパイル結果をキャッシュすることで構文解析プロセスが省略できるようになる…… などなど、数々のご利益があります。

また、PHP 4で基本的な改善として実装された参照カウント方式のガベージコレクションforeach構文ヒアドキュメントの追加、セッション機能の追加などは、現代のPHPの基礎にもなっています。

「例解PHP」という書籍においては、PHP 3と4との違いとして以下のようなコードが挙げられています。

PHP 3で有効な方法
<?php
$names = array("Jackie", "Greg", "Grant", "Julie", "Peter");

while (list(, $name) = each($names))
{
    eval('if (!strcmp($name, "Grant")) break;');
    print "$name\n";
}
?>

(「例解PHP」 2001年11月13日 初版第1刷、Starling Hughes・Andrei Zmievski著、武藤健志訳、翔泳社刊より引用)

PHP 4では、このコードが以下のように書けるようになりました。

PHP 3で有効な方法
<?php
$names = array("Jackie", "Greg", "Grant", "Julie", "Peter");

foreach ($names as $name) {
    if ($name === "Grant") break;
    print "$name\n";
}

上記のコードのようにPHP 3では eval 内で break; continue; と書かれていた場合に親スコープでループからの脱出が起こっていましたが、PHP 4からは明示的に禁止されています。

PHP 4ではパッケージマネージャとしてPEAR (PHP Extension and Application Repository)が提供されるようになりました。これはPHP標準のパッケージリポジトリとそれをインストールするコマンドラインツールからなり、有用なクラスライブラリがオンラインで取得可能になりました。

このPEARは現代のComposerのように自由にパッケージを公開できるようなものではなく、PHP標準のクラスライブラリのリポジトリとしてある程度の基準を満たしたものだけが公開されていました。PEARコーディング標準もPHPのコーディングスタイルガイドとして参照されていた時期もありますが、現行のPSRスタイルガイドとの差異も大きく更新されていないため、現代ではあまり省みられていません。

PHP 4とPHPコミュニティの拡大

ともあれ、PHP 4では大幅なパフォーマンス改善や機能追加が達成されました。この時期の2000年には日本および世界でPHPカンファレンスが開催されるようになった時期でもあります。

https://twitter.com/phpcon/status/1195595301161070597

初回のPHPカンファレンスで廣川さんが発表した最新PHP事情 (2000年7月22日,PHPカンファレンス)でも「新バージョンPHP4のご紹介とPHPのXML等への応用について」という副題で語られています。廣川さん自身も後の2019年のインタビューで[20回開催記念企画]廣川類氏と徳丸浩氏に、これまでのPHPとの関わりをを聞く。 ——そしてPHPカンファレンス2019へ | gihyo.jpとして2000年に日本語のコミュニティとして「日本PHPユーザ会」ができたことをターニングポイントの一つとして挙げています。

PHP 4の進化とそれからのPHP

2000年5月にPHP 4.0がリリースされ、2001年12月に4.1、2002年4月に4.2、2002年12月に4.3が公開されたあと2004年7月にPHP 5.0がリリースされたあともPHP 4系は2008年8月に最終版の4.4.9がリリースされるまでメンテされていました。それからのPHPも興味深い発展を遂げていますが、それはまた別の話で…

https://tadsan.fanbox.cc/posts/6684037

Discussion