PHPはプログラムの実行前にソースコードをコンパイルするんだよ、ってことを雑に書いた。
函数定義はコンパイル時に
単純にソースコードを上から順に実行してるだけではないことは次の例で確認できます。
<?php
echo hello(), PHP_EOL; // hello()の定義はexitの下にある
exit; //ここで処理は終了する
echo "a", PHP_EOL; // この行は実行されない
function hello()
{
return "Hello!";
}
echo "b", PHP_EOL; // この行は実行されない
Hello!
次の例も見てみませう。
<?php
echo "1", PHP_EOL;
function hello()
{
return "Hello! 1";
}
echo "2", PHP_EOL;
function hello() // 同名の函数は定義できない
{
return "Hello! 2";
}
Fatal error: Cannot redeclare hello() (previously declared in /home/jail/prog.php:7) in /home/jail/prog.php on line 15
Cannot redeclare hello()
と怒られますが、1
も2
も出力されることはありません。
クラスの解決は実行時に
存在しないクラスを参照しようとしたり静的ではないメソッドを静的に呼ぼうとしても、怒られるのは実行時だ。
<?php
error_reporting(E_ALL | E_STRICT);
set_error_handler('error_handler');
$foo = new Foo;
echo Foo::bar(), PHP_EOL;
if (false) {
echo Foo::Buz(), PHP_EOL; // メソッドを静的に呼び出そうとする
$hoge = new Hoge; // 存在しないクラスのインスタンス
$hoge->fuga(); // 存在しないクラスの存在しないメソッド
hoge_fuga();
}
echo $foo->buz(), PHP_EOL;
hoge_fuga(); // 存在しない函数を読んでみる
echo "Bye.", PHP_EOL; // ↑で止まるので、この行が出力されることはにあ
exit;
final class Foo
{
public static function bar()
{
return "Foo::Bar!";
}
public function buz()
{
return "foo->Buz!";
}
}
function error_handler($severity, $message, $file, $line)
{
throw new \ErrorException($message, 0, $severity, $file, $line);
}
Foo::Bar!
foo->Buz!
Fatal error: Call to undefined function hoge_fuga() in /home/jail/prog.php on line 16
実行時にしか怒られないといふことは、if (false)
の中身は実行されないので怒られない!
クラスローダー
PHPのクラスローダーは、実行時に未定義のクラスが登場したときに、初めてそのクラスが定義されたファイルを読み込もうとする仕組みだ。php.netのPHP: クラスのオートローディング - Manualも読まれたい。
今回は詳しい説明を省くが、Composerまたはtheseer/Autoloadを利用するのが良い。
$ tree .
.
└── src
├── Hoge
│ └── Fuga.php
├── Hoge.php
└── Piya.php
2 directories, 3 files
Autoload Builderは以下のようなPHPを生成する。
<?php
// @codingStandardsIgnoreFile
// @codeCoverageIgnoreStart
// this is an autogenerated file - do not edit
spl_autoload_register(
function($class) {
static $classes = null;
if ($classes === null) {
$classes = array(
'zonuexe\\hoge' => '/src/Hoge.php',
'zonuexe\\hoge\\fuga' => '/src/Hoge/Fuga.php',
'zonuexe\\piyo' => '/src/Piya.php'
);
}
$cn = strtolower($class);
if (isset($classes[$cn])) {
require __DIR__ . $classes[$cn];
}
},
true,
false
);
// @codeCoverageIgnoreEnd
このような仕組みを利用すれば、require
するタイミングを実行時に遅延できる。
最後に
「PHPはコンパイルしない言語」みたいな意味不明な発言はやめよう。
php hoge.php
のようにコマンドにソースコードのファイルを指定して直接実行してるように見えたとしても、現代的なインタプリタはソースコードをパースしたあと仮想機械のバイトコードにコンパイルする。RubyもPythonもPerl5もEmacsも同様に。
では「PHPはネイティブコードにコンパイルしない」であれば正しいかといふと、Facebookの開発したHHVMでPHPを実行するとJITコンパイラによってネイティブコードにコンパイルされる。
もっと実装寄りの具体的な話を知りたい方は、ぜひPHP による hello world 入門 | 東北ギークといふ神記事をお読みください。