====== Apricot アプリケーションクラス ======
--- //[[http://www.y2sunlight.com|y2sunlight]] 2020-04-25//
[[apricot:top|Apricot に戻る]]
関連記事
* [[apricot:configuration|Apricot プロジェクトの作成]]
* [[apricot:public|Apricot 公開フォルダ]]
* Apricot コア
* [[apricot:core:top|Apricot コア作成の準備]]
* Apricot アプリケーションクラス
* [[apricot:core:basic-class|Apricot 各種基本コアクラス]]
* [[apricot:core:request-class|Apricot リクエストクラス]]
* [[apricot:core:response-class|Apricot レスポンスクラス]]
* [[apricot:core:base-controller|Apricot ベースコントローラ]]
* [[apricot:core:completion|Apricot コアの完成]]
* [[apricot:app:top|Apricot アプリ]]
* [[apricot:ext:middleware|Apricot 拡張]]
さて、準備が出来たのでいよいよコアのクラス群を作って行きたいと思います。
----
===== Applicationクラスの仕様 =====
最初にApplicationクラスを作成しますが、その前にApplicationクラスの責任を明確にしておきます。
=== Applicationクラスの責任 ===
* アプリケーションの初期化
* ルーティングとセキュリティの設定
* ミドルウェアの設定
* アクションの起動
Applicationクラスは、以下の公開メソッドを持っています。詳細はソースコードを参照して下さい。
=== フロントコントローラ(index.php)から呼び出されるメソッド ===
^公開メソッド^機能^
|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クラス(暫定版) =====
以下に暫定版のApplicationクラスを示します。ほとんどの機能は実装できていますが、アクションを実行するメソッド(executeAction)だけが未実装(スタブ)です。
{{fa>folder-open-o}} ** /apricot/core **
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クラスのヘルパー関数 =====
Applicationクラスのゲッターメソッドで良く使用されるものは boilerplates.php にヘルパー関数として追加しておきます。
{{fa>folder-open-o}} ** /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クラスの設定ファイル =====
Applicationクラスは2つの設定ファイル( app.php と routes.php)を持っています。これらはconfigフォルダ内に保存されています。
==== app.php ====
app.php はライブラリとミドルウェアに関する構成とセキュリティーの定義が含まれています。
{{fa>folder-open-o}} ** /apricot/config**
[],
'middleware' =>[],
'auth' =>[],
'csrf' =>[],
];
* setup --- ライブラリの初期化ファイルの所在(フルパス)
* middleware --- ミドルウェアの完全修飾クラス名
* auth --- ユーザ認証(セッション認証)の設定
* csrf --- CSRFトークンの設定
==== routes.php ====
routes.php にはルーティング(URIとアクションの紐づけ)の設定が含まれています。
{{fa>folder-open-o}} ** /apricot/config**
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!';
});
});
};
ルーティング設定に関しては[[https://github.com/nikic/FastRoute|FastRoute]]を参照して下さい。
\\
===== テスト実行 =====
ここまでの実装で一度実行してみましょう。以下のように index.php を編集します。
{{fa>folder-open-o}} ** /apricot/public **
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!
\\