Create your PHP Frameworkをやってみる③
Create your PHP Frameworkをやってみる②の続きです。
この記事は、version 2.7に基づいています。
The Front Controller (current)
Goodbyeページの作成
別のページ、Goodbyeページを作成します。
bye.php
<?php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$response = new Response('Goodbye!');
$response->send();
http://127.0.0.1:4321/bye.php
にブラウザでアクセスすると「Goodbye!」と表示されるはずです。
ただ、これだとindex.phpとbye.phpどちらも以下のコードは同じです。
<?php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
また、この方法では、ページを増やすたびに同じコードをコピーすることになり、よくなさそうです。
そこで、重複するコードを別のファイルにまとめるようにリファクタリングします。
共通するコードを別のファイルにまとめる
init.php
を作成し、共通するコードをまとめます。
init.php
<?php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$response = new Response();
index.php
でinit.php
をインクルードするようにします。
index.php
<?php
require_once __DIR__.'/init.php';
$input = $request->get('name', 'World');
$response->setContent(sprintf('Hello %s', htmlspecialchars($input, ENT_QUOTES, 'UTF-8')));
$response->send();
bye.php
も同じように書き換えます。
bye.php
<?php
require_once __DIR__.'/init.php';
$response->setContent('Goodbye!');
$response->send();
かなりすっきりしました。しかし、まだ$response->send();
は重複しています。
フロントコントローラの導入
さらに改良するために、フロントコントローラを導入します。
フロントコントローラとは、すべてのリクエストを受ける1つのPHPファイルのことです。
フロントコントローラとしてfront.php
を作成します。
front.php
<?php
require_once __DIR__.'/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$request = Request::createFromGlobals();
$response = new Response();
$map = array(
'/hello' => __DIR__.'/hello.php',
'/bye' => __DIR__.'/bye.php',
);
$path = $request->getPathInfo();
if (isset($map[$path])) {
require $map[$path];
} else {
$response->setStatusCode(404);
$response->setContent('Not Found');
}
$response->send();
上記のファイルがすべてのアクセスを受け、必要なファイルを呼び出すことになります。
どのファイルを呼び出すかは$map
で定義されています。
hello.php
を作成します。
hello.php
<?php
$input = $request->get('name', 'World');
$response->setContent(sprintf('Hello %s', htmlspecialchars($input, ENT_QUOTES, 'UTF-8')));
bye.php
を変更します。
bye.php
<?php
$response->setContent('Goodbye!');
これで、コードの重複はなくなり、以下のURLでアクセスできるようになりました。
http://127.0.0.1:4321/front.php/hello?name=Fabien
http://127.0.0.1:4321/front.php/bye
フォルダ構成の改良
フォルダ構成を改良します。
フロントコントローラ以外はブラウザからアクセスする必要はありませんので、web
フォルダを作成しその中に移動します。
ページのソースもsrc/pages
フォルダに移動します。
いらないファイルは削除しましょう。
deleted: index.php
deleted: init.php
renamed: bye.php -> src/pages/bye.php
renamed: hello.php -> src/pages/hello.php
deleted: test.php
renamed: front.php -> web/front.php
ということで、以下のようになりました。よりちゃんとしたフレームワークっぽくなりました。
framework/
├── composer.json
├── composer.lock
│ src/
│ └── pages/
│ ├── hello.php
│ └── bye.php
├── vendor/
└── web/
└── front.php
ファイルを移動したので、それに合うようにパスを修正しておきます。
--- a/web/front.php
+++ b/web/front.php
@@ -1,6 +1,6 @@
<?php
-require_once __DIR__.'/vendor/autoload.php';
+require_once __DIR__.'/../vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@@ -9,8 +9,8 @@ $request = Request::createFromGlobals();
$response = new Response();
$map = array(
- '/hello' => __DIR__.'/hello.php',
- '/bye' => __DIR__.'/bye.php',
+ '/hello' => __DIR__.'/../src/pages/hello.php',
+ '/bye' => __DIR__.'/../src/pages/bye.php',
);
$path = $request->getPathInfo();
これで、PHPのビルトインWebサーバを以下のように起動すると、
$ php -S 127.0.0.1:4321 -t web/ web/front.php
http://127.0.0.1:4321/?name=Fabien
のようにフロントコントローラのファイル名を消したURLでアクセスできるようになりました。
私たちのフレームワークの最初のバージョン
現在、ページのソースがPHPファイルですが、これをテンプレートに変更しましょう。
--- a/web/front.php
+++ b/web/front.php
@@ -15,7 +15,9 @@ $map = array(
$path = $request->getPathInfo();
if (isset($map[$path])) {
- require $map[$path];
+ ob_start();
+ include $map[$path];
+ $response->setContent(ob_get_clean());
} else {
$response->setStatusCode(404);
$response->setContent('Not Found');
これでページのソースは以下のようになります。
src/pages/hello.php
<?php $name = $request->get('name', 'World') ?>
Hello <?php echo htmlspecialchars($name, ENT_QUOTES, 'UTF-8') ?>
src/pages/bye.php
Goodbye!
新たにページを増やす場合は、
front.php
の$map
に登録src/pages
フォルダにテンプレートを追加
となり、テンプレート内では$request
と$response
が使えます。
これが、私たちのフレームワークの最初のバージョンです。
Date: 2015/07/16