— y2sunlight 2020-04-25
関連記事
さて、準備が出来たのでいよいよコアのクラス群を作って行きたいと思います。
最初にApplicationクラスを作成しますが、その前にApplicationクラスの責任を明確にしておきます。
Applicationクラスは、以下の公開メソッドを持っています。詳細はソースコードを参照して下さい。
公開メソッド | 機能 |
---|---|
setup(array $app=[]) | アプリケーションのセットアップ |
run(callable $routeDefinitionCallback) | アクションの実行 |
公開メソッド | 機能 |
---|---|
getInstance():Application | Applicationインスタンスの取得 |
getSetting($dot = null, $default=null) | アプリケーション設定(app.php)の取得 |
getProjectDir():string | プロジェクトフォルダの取得 |
getConfigDir():string | config(一般設定)フォルダの取得 |
getAssetsDir():string | assets(資源)フォルダの取得 |
getVarDir():string | var(データ)フォルダの取得 |
getPublicDirectory():string | public(公開)の取得 |
getRouteBase():string | URIルートベースの取得 |
getControllerName():string | カレントコントローラ名の取得 |
getActionName():string | カレントアクション名の取得 |
以下に暫定版のApplicationクラスを示します。ほとんどの機能は実装できていますが、アクションを実行するメソッド(executeAction)だけが未実装(スタブ)です。
/apricot/core
<?php namespace Core; /** * Application Class */ class Application { /** * Application Instance * var Application */ private static $instance = null; /** * Application Setting * @var array */ private $app = []; /* * Project Directories */ private $projectDir; private $configDir; private $assetsDir; private $varDir; /* * Public Directory */ private $publicDir; /* * Route Base Path */ private $routeBase; /* * Controller Name */ private $controllerName; /* *Action Name; */ private $actionName; /** * Get Project dir * @return string */ public function getProjectDir():string {return $this->projectDir;} /** * Get config dir * @return string */ public function getConfigDir():string {return $this->configDir; } /** * Get assets dir * @return string */ public function getAssetsDir():string {return $this->assetsDir; } /** * Get var dir * @return string */ public function getVarDir():string {return $this->varDir; } /** * Get Public Directory * @return string */ public function getPublicDirectory():string {return $this->publicDir; } /** * Get Route Base Path * @return string */ public function getRouteBase():string {return $this->routeBase; } /** * Get controller Name * @return string */ public function getControllerName():string {return $this->controllerName; } /** * Get Action Name * @return string */ public function getActionName():string {return $this->actionName; } /** * Get Application instance. * @return \Core\Application */ static public function getInstance():Application { if (!self::$instance) { throw new \RuntimeException('Application has not been set.'); } return self::$instance; } /** * Create Application * @param string $projectDir * @param string $publicDir */ function __construct(string $projectDir, string $publicDir) { if (!self::$instance) { // Set Project Directories $this->projectDir = $projectDir; $this->configDir = $projectDir.'/config'; $this->assetsDir = $projectDir.'/assets'; $this->varDir = $projectDir.'/var'; // Set Public Directory $this->publicDir = $publicDir; // Set Route Base Path $routeBase = dirname($_SERVER['SCRIPT_NAME']); if (preg_match('/^[\\.\\\\]$/', $routeBase)) $routeBase=''; $this->routeBase = $routeBase; // Set Dotenv \Dotenv\Dotenv::createImmutable($projectDir)->load(); // Set timezone date_default_timezone_set(env('APP_TIMEZONE','UCT')); self::$instance = $this; } } /** * Get an application setting value * @param string|null $dot Dot-notation key * @param mixed $default * @return mixed */ public function getSetting($dot = null, $default=null) { return array_get($this->app, $dot, $default); } /** * Checks if an application setting key is present * @param string $dot Dot-notation key * @return bool */ public function hasSetting(string $dot):bool { return array_has($this->app, $dot); } /** * Setup Application * @param array $app Application Setting */ public function setup(array $app=[]) { $this->app = $app; // Application setup if (!empty($this->app) && array_key_exists('setup', $this->app)) { foreach($this->app['setup'] as $setup) { $func = require_once $setup; if (!is_callable($func) || ($func()===false)) { throw new \RuntimeException("Application Setup Error: {$setup}"); } } } } /** * Run Application * @param callable $routeDefinitionCallback */ public function run(callable $routeDefinitionCallback) { // Create Dispatcher $dispatcher = \FastRoute\simpleDispatcher($routeDefinitionCallback); // Fetch method and URI from somewhere $httpMethod = $_SERVER['REQUEST_METHOD']; $uri = $_SERVER['REQUEST_URI']; // Strip query string (?foo=bar) and decode URI if (false !== $pos = strpos($uri, '?')) { $uri = substr($uri, 0, $pos); } $uri = rawurldecode($uri); $routeInfo = $dispatcher->dispatch($httpMethod, $uri); switch ($routeInfo[0]) { case \FastRoute\Dispatcher::NOT_FOUND: abort(404, 'Page Not Found'); break; case \FastRoute\Dispatcher::METHOD_NOT_ALLOWED: abort(405, 'Method Not Allowed'); break; case \FastRoute\Dispatcher::FOUND: $handler = $routeInfo[1]; $params = $routeInfo[2]; if (is_callable($handler)) { // Case of callable $handler($params); } elseif(strpos($handler,'@')!==false) { // Case of Controller/Action list($this->controllerName, $this->actionName) = explode('@', $handler); // Ecexute action $this->executeAction($this->controllerName, $this->actionName, $params); } else { abort(500,'Action Not Found'); } break; } } /** * Ecexute action * @param string $controllerName * @param string $actionName * @param array $params */ private function executeAction(string $controllerName, string $actionName, array $params=[]) { // TODO: Stub Version $controller = "\\App\\Controllers\\{$controllerName}"; $instance = new $controller(); call_user_func_array(array($instance, $actionName), $params); } }
Applicationクラスのゲッターメソッドで良く使用されるものは boilerplates.php にヘルパー関数として追加しておきます。
/apricot/core/helpers
// ... /** * Get application setting value * @param string|null $dot Dot-notation key * @param mixed $default * @return mixed */ function app($dot = null, $default=null) { return Core\Application::getInstance()->getSetting($dot, $default); } /** * Checks if an application setting key is present * @param string $dot Dot-notation key * @return bool */ function app_has($dot = null) { return Core\Application::getInstance()->hasSetting($dot); } /** * Get project directory * @param string|null $default * @return string project directory */ function project_dir($path = null):string { return add_path(Core\Application::getInstance()->getProjectDir(), $path); } /** * Get config directory * @param string $path Sub path, if necessary * @return string config directory */ function config_dir($path = null):string { return add_path(Core\Application::getInstance()->getConfigDir(), $path); } /** * Get assets directory * @param string $path Sub path, if necessary * @return string assets directory */ function assets_dir($path = null):string { return add_path(Core\Application::getInstance()->getAssetsDir(), $path); } /** * Get var directory * @param string $path Sub path, if necessary * @return string var directory */ function var_dir($path = null):string { return add_path(Core\Application::getInstance()->getVarDir(), $path); } /** * Get public directory * @param string $path Sub path, if necessary * @return string public directory */ function public_dir($path = null):string { return add_path(Core\Application::getInstance()->getPublicDirectory(),$path); } /** * Get application URL * @param string $path Sub path, if necessary * @return string URL */ function url($path = null):string { // TODO: APP_URLが無い時は、DomainとProtocolから絶対URLを作る $base = env('APP_URL', Core\Application::getInstance()->getRouteBase()); return add_path($base,$path); } /** * Get file URL With version * @param string $filename * @return string URL */ function url_ver(string $filename) { return url($filename).'?v='.env('APP_VERSION'); } /** * Get routing path * @param string $path Sub path, if necessary * @return string routing path */ function route($path = null):string { return add_path(Core\Application::getInstance()->getRouteBase(),$path); } /** * Get current controller name * @return string name */ function controllerName():string { return Core\Application::getInstance()->getControllerName(); } /** * Get current action name * @return string name */ function actionName():string { return Core\Application::getInstance()->getActionName(); }
Applicationクラスは2つの設定ファイル( app.php と routes.php)を持っています。これらはconfigフォルダ内に保存されています。
app.php はライブラリとミドルウェアに関する構成とセキュリティーの定義が含まれています。
/apricot/config
<?php return [ 'setup' =>[], 'middleware' =>[], 'auth' =>[], 'csrf' =>[], ];
routes.php にはルーティング(URIとアクションの紐づけ)の設定が含まれています。
/apricot/config
<?php //------------------------------------------------------------------- // Route Definition Callback //------------------------------------------------------------------- return function (FastRoute\RouteCollector $r) { $base = Core\Application::getInstance()->getRouteBase(); $r->addGroup($base, function (FastRoute\RouteCollector $r) use($base) { // Home // TODO: Stub Version $r->get('/', function() use($base){ header("Content-type: text/html; charset=utf-8"); echo 'Hello, Apricot!'; }); }); };
ルーティング設定に関してはFastRouteを参照して下さい。
ここまでの実装で一度実行してみましょう。以下のように index.php を編集します。
/apricot/public
<?php //------------------------------------------------------------------- // オートローダーの登録 //------------------------------------------------------------------- require dirname(__DIR__).'/vendor/autoload.php'; //------------------------------------------------------------------- // パスの設定 //------------------------------------------------------------------- $project_path = dirname(__DIR__); $public_path = __DIR__; //------------------------------------------------------------------- // アプリケーション初期化 //------------------------------------------------------------------- $application = new Core\Application($project_path, $public_path); // TODO: セッション開始 // アプリケーションセットアップ $application->setup(require_once config_dir('app.php')); //------------------------------------------------------------------- // アクションの実行 //------------------------------------------------------------------- $application->run(require_once config_dir('routes.php'));
Applocationクラスの実装が出来たので、index.php に新しく2つの機能が追加されました。
ブラウザ上で以下のURLにアクセスしてみて下さい。
http://localhost/ws2019/apricot/public/
次のように表示されます:
Hello, Apricot!