diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6313b56 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/.github/workflows/code_quality.yaml b/.github/workflows/code_quality.yaml new file mode 100644 index 0000000..f79bfad --- /dev/null +++ b/.github/workflows/code_quality.yaml @@ -0,0 +1,92 @@ +name: "Code Quality" + +on: + pull_request: + paths: + - src + - composer.json + - phpcs.xml.dist + - phpstan.neon.dist + - phpunit.xml.dist + push: + branches: + - master + +jobs: + test: + name: "Test" + runs-on: ${{ matrix.operating-system }} + timeout-minutes: 60 + + strategy: + fail-fast: false + matrix: + php-version: [ "8.0", "8.1" ] + operating-system: [ ubuntu-latest, windows-latest ] + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php-version }}" + + - name: "Install" + run: "composer install --no-interaction --no-progress" + + - name: "Test" + run: "composer test" + + format: + name: "Format" + runs-on: ${{ matrix.operating-system }} + timeout-minutes: 60 + + strategy: + fail-fast: false + matrix: + php-version: [ "8.0", "8.1" ] + operating-system: [ ubuntu-latest, windows-latest ] + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php-version }}" + + - name: "Install" + run: "composer install --no-interaction --no-progress" + + - name: "Format" + run: "composer format" + + lint: + name: "Lint" + runs-on: ${{ matrix.operating-system }} + timeout-minutes: 60 + + strategy: + fail-fast: false + matrix: + php-version: [ "8.0", "8.1" ] + operating-system: [ ubuntu-latest, windows-latest ] + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + php-version: "${{ matrix.php-version }}" + + - name: "Install" + run: "composer install --no-interaction --no-progress" + + - name: "Lint" + run: "composer lint" diff --git a/.gitignore b/.gitignore index 94d6d75..c84ab0c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /vendor/ -/composer.lock \ No newline at end of file +/composer.lock +/.phpunit.result.cache diff --git a/LICENSE b/LICENSE index f358a79..3d4b489 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,9 @@ MIT License -Copyright (c) 2017 FratilyPHP +Copyright FratilyPHP -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index c033a0c..436c1fe 100644 --- a/README.md +++ b/README.md @@ -1,93 +1 @@ # FratilyPHP - -Fratily is a web application framework being developed with PHP, for studying. - -# 現状での使い方 - -```php - -/** - * Aura DI コンテナ - */ -class DefaultConfig extends Aura\Di\ContainerConfig{ - - public function define(Aura\Di\Container $di){ - $di->set(Interop\Http\Factory\ResponseFactoryInterface::class, $di->lazyNew(Fratily\Http\Factory\ResponseFactory::class)); - } -} - -/** - * コントローラークラス - */ -class IndexController extends Fratily\Framework\Controller\Controller{ - - public function index(){ - return "This is index page"; - } - - public function page($page){ - return "This is page$page page"; - } -} - -/* - * アクセスログのようなものを出力するミドルウェア - */ -class AccessMiddleware implements Psr\Http\Server\MiddlewareInterface{ - - private $dir; - - public function __construct(string $dir){ - if(!is_dir($dir)){ - throw new InvalidArgumentException(); - } - - $this->dir = realpath($dir); - } - - public function process(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Server\RequestHandlerInterface $handler): \Psr\Http\Message\ResponseInterface{ - $fp = fopen($this->dir . DIRECTORY_SEPARATOR . "log", "a+"); - - fwrite($fp, date("Y-m-d H:i:s") . " " . $_SERVER["REQUEST_METHOD"] . "\t" . $_SERVER["REQUEST_URI"] . PHP_EOL); - - return $handler->handle($request); - } -} - -// DIコンテナを生成 -$c = (new Aura\Di\ContainerBuilder)->newConfiguredInstance([ - DefaultConfig::class -]); - -// ルート定義 -$routes = new Fratily\Router\RouteCollector(); - -$routes->get("index", "/", [ - "action" => [IndexController::class, "index"] -]); - -$routes->get("page", "/page/{page:[1-9][0-9]*}", [ - "action" => [IndexController::class, "page"] -]); - -$routes->get("func", "/func", [ - "action" => function(){ - return "This is function page."; - } -]); - -// アプリケーションインスタンスを生成 -$app = new Fratily\Framework\Application($c, $routes, true); -// ミドルウェアを追加 -$app->append(new AccessMiddleware(__DIR__)); - -// リクエストインスタンスを生成 -$request = (new Fratily\Http\Factory\ServerRequestFactory())->createServerRequestFromArray($_SERVER); - -// ミドルウェアハンドラを実行 -$handler = $app->generateHandler($request); - -// レスポンスを送信 -$emitter = new Fratily\Http\Message\Response\Emitter(); -$emitter->emit($handler->handle($request)); -``` \ No newline at end of file diff --git a/composer.json b/composer.json index 5d13a9b..c5e39d4 100644 --- a/composer.json +++ b/composer.json @@ -8,39 +8,41 @@ } ], "license": "MIT", + "scripts": { + "test": [ "@phpunit:unit" ], + "format": [ "@phpcs" ], + "lint": [ "@phpstan" ], + + "phpunit:unit": "phpunit --testsuite unit", + "phpcs": "phpcs", + "phpcbf": "phpcbf", + "phpstan": "phpstan analyze" + }, "require": { - "php": "^7.1", - "psr/container": "^1.0", - "psr/log": "^1.0", - "fratily/cache": "^0.0.1", - "fratily/router": "^0.4.0", - "fratily/http-message": "^0.1.0", - "fratily/http-message-factory": "^0.2.0", - "fratily/http-server-middleware": "^0.2.0", - "fratily/reflection": "^0.4.0", - "fratily/utility": "^0.1.0", - "fratily/container": "^0.3.0", - "fratily/event-manager": "^0.1.0", - "twig/twig": "^2.0", - "fratily/debugbar": "^0.2.0" + "php": "^8.0", + "psr/event-dispatcher": "^1.0", + "psr/http-message": "^1.0", + "psr/http-server-handler": "^1.0", + "psr/http-server-middleware": "^1.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.6", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1" }, "autoload": { "psr-4": { "Fratily\\Framework\\": "src/" - }, - "files": [ - "src/bootstrap.php" - ] - }, - "require-dev": { - "phpunit/phpunit": "^7.0" + } }, "autoload-dev": { "psr-4": { "Fratily\\Tests\\Framework\\": "tests/" } }, - "scripts": { - "test": "phpunit" + "config": { + "sort-packages": true } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 0000000..09e1d34 --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,22 @@ + + + + + + + + ./src + ./tests + + + + + + + + + + diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..e816fe5 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,10 @@ +includes: + - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/phpstan/phpstan-phpunit/rules.neon + - vendor/phpstan/phpstan-strict-rules/rules.neon +parameters: + paths: + - src + - tests + level: max + ignoreErrors: diff --git a/phpunit.xml b/phpunit.xml deleted file mode 100644 index b9230f1..0000000 --- a/phpunit.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - ./tests - - - \ No newline at end of file diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..d334c13 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,13 @@ + + + + + ./tests/Unit + + + diff --git a/resource/views/base.twig b/resource/views/base.twig deleted file mode 100644 index 0021b27..0000000 --- a/resource/views/base.twig +++ /dev/null @@ -1,85 +0,0 @@ - - - - - {% block title %}Base template{% endblock %} - - - - -
-
- {% block content %}Content undefined.{% endblock %} -
-
- - \ No newline at end of file diff --git a/resource/views/error.twig b/resource/views/error.twig deleted file mode 100644 index cba9333..0000000 --- a/resource/views/error.twig +++ /dev/null @@ -1,34 +0,0 @@ -{% extends "base.twig" %} - -{% block title %}エラーページだよ{% endblock %} - -{% block content %} -{% for error in errors %} -
-

{{ error.name }}

-

{{ error.message }}

-

{{prev.object.file}} {{prev.object.line}}

-
- {{ error.file }} {{ error.line }} -
-{% for line, row in error.script %}
-{{line}}{{ "\t"|raw }}{{ row }}
-{% endfor %}
-
-
-
-

stack trace

- {% for trace in error.trace %} -
- {{ trace.file }} {{ trace.line }} -
-{% for line, row in trace.script %}
-{{line}}{{ "\t"|raw }}{{ row }}
-{% endfor %}
-
-
- {% endfor %} -
-
-{% endfor %} -{% endblock %} \ No newline at end of file diff --git a/src/Application.php b/src/Application.php deleted file mode 100644 index f853886..0000000 --- a/src/Application.php +++ /dev/null @@ -1,558 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework; - -use Fratily\Framework\Container as ContainerConfig; -use Fratily\Container\ContainerFactory; -use Fratily\Container\Container; -use Fratily\Router\RouteCollector; -use Fratily\Router\Route; -use Fratily\Http\Server\RequestHandler; -use Fratily\EventManager\EventManagerInterface; -use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Server\MiddlewareInterface; -use Psr\Http\Server\RequestHandlerInterface; - -/** - * - */ -class Application{ - - use Traits\DebugTrait; - use Traits\PerformanceTrait; - use Traits\LogTrait; - - /** - * @var Container - */ - private $container; - - /** - * @var EventManagerInterface - */ - private $eventMng; - - /** - * @var RouteCollector - */ - private $routes; - - /** - * @var MiddlewareInterface[][] - */ - private $middlewares = [ - "before" => [], - "after" => [], - ]; - - /** - * アプリケーションインスタンスを生成する - * - * @param mixed[] $containerConfig - * コンテナ構築クラスの配列 - * @param bool $debug - * アプリケーションをデバッグモードで起動するか - * - * @return static - * - * @throws LogicException - */ - public static function create(array $containerConfig, bool $debug){ - $startedAt = microtime(true); - $timeline = []; - - // Container initialize - $start = microtime(true); - $containerConfig = static::createContainerConfigList($containerConfig, $debug, $startedAt); - $containerFactory = new ContainerFactory(); - $end = microtime(true); - - $timeline["container.initialize"] = [$start, $end]; - - // Create container - $start = microtime(true); - $container = $containerFactory->createWithConfig($containerConfig, true); - $end = microtime(true); - - $timeline["container.create"] = [$start, $end]; - - // Create Application - // コンテナのmodifyでappを触っているとすごく速くなる - $start = microtime(true); - $app = $container->get("app"); - $end = microtime(true); - - $timeline["app.create"] = [$start, $end]; - - if(!($app instanceof static)){ - throw new \LogicException; - } - - if($debug){ - foreach($timeline as $name => $time){ - $container->get("core.debugbar.performance")->addLine($name, $time[0], $time[1]); - } - } - - return $app; - } - - /** - * コンテナ初期化用のクラスリストを作成する - * - * @param mixed[] $conf - * 追加で指定する初期化用クラス - * @param bool $debug - * デバッグモードが有効か - * @param float $startedAt - * 開始時刻 - * @return mixed[] - */ - private static function createContainerConfigList(array $conf, bool $debug, float $startedAt){ - return array_merge( - [new ContainerConfig\AppConfig($debug)], - $conf, - [ - new ContainerConfig\TraitConfig(), - new ContainerConfig\TypeConfig(), - new ContainerConfig\ActionConfig(), - new ContainerConfig\MiddlewareConfig() - ], - $debug ? [new ContainerConfig\DebugConfig($startedAt)] : [], - [new ContainerConfig\CoreConfig()] - ); - } - - /** - * ミドルウェアリストを正しくする - * - * @param MiddlewareInterface[]|MiddlewareInterface|null $middlewares - * - * @return MiddlewareInterface[] - */ - private static function normalizeMiddlewares($middlewares){ - $return = []; - - foreach((array)$middlewares as $middleware){ - if(!($middleware instanceof MiddlewareInterface)){ - $return[] = $middleware; - } - } - - return $return; - } - - /** - * Constructor - * - * @param Container $container - * アプリケーションで使用するDIコンテナインスタンス - * @param EventManagetInterface $eventMng - * アプリケーションで使用するイベントマネージャインスタンス - * @param RouteCollector $routes - * アプリケーションで使用するルーティングルール定義インスタンス - */ - public function __construct( - Container $container, - EventManagerInterface $eventMng, - RouteCollector $routes - ){ - $this->container = $container; - $this->eventMng = $eventMng; - $this->routes = $routes; - } - - /** - * GETメソッドで受けるルーティングルールを追加する - * - * @param string $path - * ルーティングルール - * @param string|\Closure $action - * アクションを示す。クロージャーの場合はそれをアクションとし、 - * 文字列の場合は「コントローラークラス:アクションメソッド」として理解される。 - * コントローラークラスはクラス名はもちろんDIコンテナに追加したサービス名も指定できる。 - * @param string $host - * 許容するホスト名。ワイルドカード構文を用いる。 - * @param string $name - * ルーティングルール名。指定しなかった場合は適当な文字が割り当てられる。 - * @param mixed[] $data - * このルールに付随するデータ。 - * 今のところルール特有のミドルウェアの追加方法として利用される。 - * - * @return $this - * - * @throws \InvalidArgumentException - */ - public function get( - string $path, - $action, - string $host = "*", - string $name = null, - array $data = [] - ){ - return $this->addRoute($path, $action, "GET", $host, $name, $data); - } - - /** - * POSTメソッドで受けるルーティングルールを追加する - * - * @param string $path - * ルーティングルール - * @param string|\Closure $action - * アクションを示す。クロージャーの場合はそれをアクションとし、 - * 文字列の場合は「コントローラークラス:アクションメソッド」として理解される。 - * コントローラークラスはクラス名はもちろんDIコンテナに追加したサービス名も指定できる。 - * @param string $host - * 許容するホスト名。ワイルドカード構文を用いる。 - * @param string $name - * ルーティングルール名。指定しなかった場合は適当な文字が割り当てられる。 - * @param mixed[] $data - * このルールに付随するデータ。 - * 今のところルール特有のミドルウェアの追加方法として利用される。 - * - * @return $this - * - * @throws \InvalidArgumentException - */ - public function post( - string $path, - $action, - string $host = "*", - string $name = null, - array $data = [] - ){ - return $this->addRoute($path, $action, "POST", $host, $name, $data); - } - - /** - * PUTメソッドで受けるルーティングルールを追加する - * - * @param string $path - * ルーティングルール - * @param string|\Closure $action - * アクションを示す。クロージャーの場合はそれをアクションとし、 - * 文字列の場合は「コントローラークラス:アクションメソッド」として理解される。 - * コントローラークラスはクラス名はもちろんDIコンテナに追加したサービス名も指定できる。 - * @param string $host - * 許容するホスト名。ワイルドカード構文を用いる。 - * @param string $name - * ルーティングルール名。指定しなかった場合は適当な文字が割り当てられる。 - * @param mixed[] $data - * このルールに付随するデータ。 - * 今のところルール特有のミドルウェアの追加方法として利用される。 - * - * @return $this - * - * @throws \InvalidArgumentException - */ - public function put( - string $path, - $action, - string $host = "*", - string $name = null, - array $data = [] - ){ - return $this->addRoute($path, $action, "PUT", $host, $name, $data); - } - - /** - * DELETEメソッドで受けるルーティングルールを追加する - * - * @param string $path - * ルーティングルール - * @param string|\Closure $action - * アクションを示す。クロージャーの場合はそれをアクションとし、 - * 文字列の場合は「コントローラークラス:アクションメソッド」として理解される。 - * コントローラークラスはクラス名はもちろんDIコンテナに追加したサービス名も指定できる。 - * @param string $host - * 許容するホスト名。ワイルドカード構文を用いる。 - * @param string $name - * ルーティングルール名。指定しなかった場合は適当な文字が割り当てられる。 - * @param mixed[] $data - * このルールに付随するデータ。 - * 今のところルール特有のミドルウェアの追加方法として利用される。 - * - * @return $this - * - * @throws \InvalidArgumentException - */ - public function delete( - string $path, - $action, - string $host = "*", - string $name = null, - array $data = [] - ){ - return $this->addRoute($path, $action, "DELETE", $host, $name, $data); - } - - /** - * ルーティングルールを追加する - * - * @param string $path - * ルーティングルール - * @param string|\Closure $action - * アクションを示す。クロージャーの場合はそれをアクションとし、 - * 文字列の場合は「コントローラークラス:アクションメソッド」として理解される。 - * コントローラークラスはクラス名はもちろんDIコンテナに追加したサービス名も指定できる。 - * @param string[]|string $allows - * 許容するHTTPメソッドリスト。 - * @param string $host - * 許容するホスト名。ワイルドカード構文を用いる。 - * @param string $name - * ルーティングルール名。指定しなかった場合は適当な文字が割り当てられる。 - * @param mixed[] $data - * このルールに付随するデータ。 - * 今のところルール特有のミドルウェアの追加方法として利用される。 - * - * @return $this - * - * @throws \InvalidArgumentException - */ - protected function addRoute( - string $path, - $action, - $allows = "GET", - string $host = "*", - $name = null, - array $data = [] - ){ - if(is_string($action)){ - $action = $this->parseActionString($action); - }else if(!($action instanceof \Closure)){ - throw new \InvalidArgumentException(); - } - - $data["_action"] = $action; - - $this->routes->add( - Route::newInstance($path, $host, $allows, $data)->withName($name) - ); - - return $this; - } - - /** - * アクション指定文字列が正しいものか確認して成形する - * - * @param string $action - * - * @return string[] - * - * @throws \InvalidArgumentException - */ - private function parseActionString(string $action){ - if(($pos = strpos($action, ":")) === false){ - throw new \InvalidArgumentException(); - } - - $controller = substr($action, 0, $pos); - $method = substr($action, $pos + 1); - - if(strlen($controller) === 0 || strlen($method) === 0){ - throw new \InvalidArgumentException(); - } - - if($this->container->has($controller)){ - $controller = $this->container->lazyGet($controller); - }else if(class_exists($controller)){ - $controller = $this->container->lazyNew($controller); - }else{ - throw new \InvalidArgumentException(); - } - - return [$controller, $method]; - } - - /** - * ミドルウェアを末尾に追加する - * - * @param MiddlewareInterface $middleware - * - * @return $this - */ - public function append(MiddlewareInterface $middleware){ - $this->middlewares["after"][] = $middleware; - - return $this; - } - - /** - * ミドルウェアを先頭に追加する - * - * @param MiddlewareInterface $middleware - * - * @return $this - */ - public function prepend(MiddlewareInterface $middleware){ - array_unshift($this->middlewares["before"], $middleware); - - return $this; - } - - /** - * ミドルウェアをアクションミドルウェアの直前に追加する - * - * @param MiddlewareInterface $middleware - * - * @return $this - */ - public function addBeforeAction(MiddlewareInterface $middleware){ - $this->middlewares["before"][] = $middleware; - - return $this; - } - - /** - * ミドルウェアをアクションミドルウェアの直後に追加する - * - * @param MiddlewareInterface $middleware - * - * @return $this - */ - public function addAfterAction(MiddlewareInterface $middleware){ - array_unshift($this->middlewares["after"], $middleware); - - return $this; - } - - /** - * レスポンスを生成する - * - * @return Response - */ - public function generateResponse(){ - // Create request - $this->startTimeline("request.create"); - - $request = $this->container->get("app.request") - ->withAttribute("app.debug", $this->debug) - ; - - $this->endTimeline("request.create"); - - // Create response - $this->startTimeline("response.create"); - - $response = $this->container->newInstance(Response::class, [ - "handler" => $this->generateHandler($request), - ]); - - $this->endTimeline("response.create"); - - return $response; - } - - /** - * ミドルウェアハンドラを生成する - * - * @param ServerRequestInterface $request - * - * @return RequestHandlerInterface - */ - protected function generateHandler(ServerRequestInterface $request){ - // Create router - $this->startTimeline("router.create"); - - $router = $this->routes - ->router($request->getUri()->getHost(), $request->getMethod()) - ; - - $this->endTimeline("router.create"); - - // Routing - $this->startTimeline("router.routing"); - - $result = $router->search($request->getUri()->getPath()); - - $this->endTimeline("router.routing"); - - if($result->found){ - $this->info("Match on route {$result->name}."); - - $action = $result->data["_action"]; - }else{ - $this->info("There were no matching routes"); - - $action = function(){ - throw new \Fratily\Http\Message\Status\NotFound(); - }; - } - - // Initialize handler - $this->startTimeline("handler.initialize"); - - $middlewares = array_merge( - $this->createWrapperMiddlewares(), - $this->middlewares["before"], - self::normalizeMiddlewares($result->data["middleware.before"] ?? []), - $this->createActionMiddleware($action, $result->params), - self::normalizeMiddlewares($result->data["middleware.before"] ?? []), - $this->middlewares["after"] - ); - - $this->endTimeline("handler.initialize"); - - // Create handler - $this->startTimeline("handler.create"); - - $handler = $this->container->newInstance(RequestHandler::class); - - foreach($middlewares as $middleware){ - $handler->append($middleware); - } - - $this->endTimeline("handler.create"); - - return $handler; - } - - /** - * アクション実行用ミドルウェアリストを作成する - * - * @param mixed $action - * @param mixed[] $params - * - * @return MiddlewareInterface[] - */ - private function createActionMiddleware($action, array $params){ - $middlewares = []; - - $middlewares[] = $this->container->get("core.middleware.action") - ->setAction($action) - ->setParams($params) - ; - - return $middlewares; - } - - /** - * デバッグ用ミドルウェアリストを作成する - * - * @return MiddlewareInterface[] - */ - private function createWrapperMiddlewares(){ - $middlewares = []; - - $middlewares[] = $this->container->get("core.middleware.error"); - - if($this->isDebug()){ - $middlewares[] = $this->container->get("core.middleware.debug"); - } - - $middlewares[] = $this->container->get("core.middleware.wrapper"); - - return $middlewares; - } -} \ No newline at end of file diff --git a/src/Container/ActionConfig.php b/src/Container/ActionConfig.php deleted file mode 100644 index 9911c5a..0000000 --- a/src/Container/ActionConfig.php +++ /dev/null @@ -1,72 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Container; - -use Fratily\Framework\Controller\Controller; -use Fratily\Framework\Controller\HttpErrorController; -use Fratily\Container\Container; -use Fratily\Container\ContainerConfig; - -/** - * - */ -class ActionConfig extends ContainerConfig{ - - /** - * {@inheritdoc} - */ - public function define(Container $container){ - $container - ->param( - Controller::class, - "debug", - $container->lazyValue("app.debug") - ) - ->set( - "core.controller.httperror", - $container->lazyNew( - HttpErrorController::class - ) - ) - ->value( - "core.action.badRequest", - [$container->lazyGet("core.controller.httperror"), "badRequest"] - ) - ->value( - "core.action.forbidden", - [$container->lazyGet("core.controller.httperror"), "forbidden"] - ) - ->value( - "core.action.notFound", - [$container->lazyGet("core.controller.httperror"), "notFound"] - ) - ->value( - "core.action.methodNotAllowed", - [$container->lazyGet("core.controller.httperror"), "methodNotAllowed"] - ) - ->value( - "core.action.internalServerError", - [$container->lazyGet("core.controller.httperror"), "internalServerError"] - ) - ->value( - "core.action.notImplemented", - [$container->lazyGet("core.controller.httperror"), "notImplemented"] - ) - ->value( - "core.action.serviceUnavailable", - [$container->lazyGet("core.controller.httperror"), "serviceUnavailable"] - ) - ; - } -} \ No newline at end of file diff --git a/src/Container/AppConfig.php b/src/Container/AppConfig.php deleted file mode 100644 index 903cb2c..0000000 --- a/src/Container/AppConfig.php +++ /dev/null @@ -1,65 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Container; - -use Fratily\Framework\Application; -use Fratily\Router\RouteCollector; -use Fratily\Http\Factory\ServerRequestFactory; -use Fratily\Http\Factory\ResponseFactory; -use Fratily\Http\Message\Response\Emitter; -use Fratily\Container\Container; -use Fratily\Container\ContainerConfig; -use Fratily\EventManager\EventManager; - -/** - * - */ -class AppConfig extends ContainerConfig{ - - /** - * @var bool - */ - private $debug; - - /** - * Constructor - * - * @param CacheItemPoolInterface $cache - * @param bool $debug - */ - public function __construct(bool $debug){ - $this->debug = $debug; - } - - /** - * {@inheritdoc} - */ - public function define(Container $container){ - $container - ->set("app", $container->lazyNew(Application::class)) - ->set("app.routes", $container->lazyNew(RouteCollector::class)) - ->set("app.request", $container->lazyCallable( - [ - $container->lazyNew(ServerRequestFactory::class), - "createServerRequestFromArray" - ], - $_SERVER - )) - ->set("app.factory.response", $container->lazyNew(ResponseFactory::class)) - ->set("app.response.emitter", $container->lazyNew(Emitter::class)) - ->set("app.eventManager", $container->lazyNew(EventManager::class)) - ->value("app.debug", $this->debug) - ; - } -} \ No newline at end of file diff --git a/src/Container/CoreConfig.php b/src/Container/CoreConfig.php deleted file mode 100644 index 047cb6a..0000000 --- a/src/Container/CoreConfig.php +++ /dev/null @@ -1,40 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Container; - -use Fratily\Container\Container; -use Fratily\Container\ContainerConfig; - -/** - * - */ -class CoreConfig extends ContainerConfig{ - - /** - * {@inheritdoc} - */ - public function define(Container $container){ - $container->set("core.twig", $container->lazyNew( - \Twig\Environment::class, - [ - $container->lazyNew( - \Twig\Loader\FilesystemLoader::class, - [ - FRATILY_FW_ROOT . "/resource/views" - ] - ) - ] - )); - } -} \ No newline at end of file diff --git a/src/Container/DebugConfig.php b/src/Container/DebugConfig.php deleted file mode 100644 index 47399cd..0000000 --- a/src/Container/DebugConfig.php +++ /dev/null @@ -1,112 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Container; - -use Fratily\Framework\Traits\LogTrait; -use Fratily\Framework\Traits\DumpTrait; -use Fratily\Framework\Traits\PerformanceTrait; -use Fratily\Framework\Middleware\DebugMiddleware; -use Fratily\Framework\Debug\Panel\MessagePanel; -use Fratily\Framework\Debug\Panel\PerformancePanel; -use Fratily\Framework\Middleware\WrapperMiddleware; -use Fratily\DebugBar\DebugBar; -use Fratily\DebugBar\Panel\PHPInfoPanel; -use Fratily\DebugBar\Panel\DumpPanel; -use Fratily\Container\Container; -use Fratily\Container\ContainerConfig; - -/** - * - */ -class DebugConfig extends ContainerConfig{ - - /** - * @var float - */ - private $startedAt; - - public function __construct(float $startedAt){ - $this->startedAt = $startedAt; - } - - /** - * {@inheritdoc} - */ - public function define(Container $container){ - $container - ->set("core.middleware.debug", $container->lazyNew( - DebugMiddleware::class, - [ - $container->lazyGet("core.debugbar"), - ] - )) - ->set("core.debugbar", $container->lazyNew( - DebugBar::class, - [ - $container->lazyArray([ - $container->lazyGet("core.debugbar.phpinfo"), - $container->lazyGet("core.debugbar.performance"), - $container->lazyGet("core.debugbar.message"), - $container->lazyGet("core.debugbar.dump"), - ]), - ] - )) - ->set("core.debugbar.phpinfo", $container->lazyNew( - PHPInfoPanel::class, - [ - "name" => "PHP" - ] - )) - ->set("core.debugbar.message", $container->lazyNew( - MessagePanel::class, - [ - "name" => "Log" - ] - )) - ->set("core.debugbar.performance", $container->lazyNew( - PerformancePanel::class, - [ - "name" => "Performance", - "start" => $this->startedAt, - ] - )) - ->set("core.debugbar.dump", $container->lazyNew( - DumpPanel::class, - [ - "name" => "Dump" - ] - )) - ->setter( - DumpTrait::class, - "setDumpPanel", - $container->lazyGet("core.debugbar.dump") - ) - ->setter( - LogTrait::class, - "setMessagePanel", - $container->lazyGet("core.debugbar.message") - ) - ->setter( - PerformanceTrait::class, - "setPerformancePanel", - $container->lazyGet("core.debugbar.performance") - ) - ->setter( - WrapperMiddleware::class, - "setPerformancePanel", - $container->lazyGet("core.debugbar.performance") - ) - ; - } -} \ No newline at end of file diff --git a/src/Container/MiddlewareConfig.php b/src/Container/MiddlewareConfig.php deleted file mode 100644 index 5ef98a8..0000000 --- a/src/Container/MiddlewareConfig.php +++ /dev/null @@ -1,53 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Container; - -use Fratily\Framework\Middleware\ActionMiddleware; -use Fratily\Framework\Middleware\WrapperMiddleware; -use Fratily\Framework\Middleware\ErrorMiddleware; -use Fratily\Container\Container; -use Fratily\Container\ContainerConfig; - -/** - * - */ -class MiddlewareConfig extends ContainerConfig{ - - /** - * {@inheritdoc} - */ - public function define(Container $container){ - $container - ->set("core.middleware.action", $container->lazyNew( - ActionMiddleware::class, - [ - "action" => $container->lazyValue("core.action.internalServerError"), - "params" => [ - "msg" => "Action is undefined.", - ], - ] - )) - ->set("core.middleware.wrapper", $container->lazyNew( - WrapperMiddleware::class - )) - ->set("core.middleware.error", $container->lazyNew( - ErrorMiddleware::class, - [ - "twig" => $container->lazyGet("core.twig"), - "debug" => $container->lazyValue("app.debug"), - ] - )) - ; - } -} \ No newline at end of file diff --git a/src/Container/TraitConfig.php b/src/Container/TraitConfig.php deleted file mode 100644 index dad723a..0000000 --- a/src/Container/TraitConfig.php +++ /dev/null @@ -1,49 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Container; - -use Fratily\Framework\Traits\DebugTrait; -use Fratily\Framework\Traits\LogTrait; -use Fratily\Framework\Traits\EventTrait; -use Fratily\Container\Container; -use Fratily\Container\ContainerConfig; - -/** - * - */ -class TraitConfig extends ContainerConfig{ - - /** - * {@inheritdoc} - */ - public function define(Container $container){ - $container - ->setter( - DebugTrait::class, - "setDebug", - $container->lazyValue("app.debug") - ) - ->setter( - LogTrait::class, - "setLogger", - $container->lazyGet("app.log") - ) - ->setter( - EventTrait::class, - "setEventManager", - $container->lazyGet("app.eventManager") - ) - ; - } -} \ No newline at end of file diff --git a/src/Container/TypeConfig.php b/src/Container/TypeConfig.php deleted file mode 100644 index 0bf13e6..0000000 --- a/src/Container/TypeConfig.php +++ /dev/null @@ -1,54 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Container; - -use Fratily\Container\Container; -use Fratily\Container\ContainerConfig; -use Fratily\Router\RouteCollector; -use Fratily\Http\Message\Response\EmitterInterface; -use Fratily\EventManager\EventManagerInterface; -use Twig_Environment; -use Twig\Environment; -use Interop\Http\Factory\ResponseFactoryInterface; -use Psr\Container\ContainerInterface; -use Psr\Cache\CacheItemPoolInterface; -use Psr\SimpleCache\CacheInterface; -use Psr\Http\Message\ServerRequestInterface; -use Psr\Log\LoggerInterface; - -/** - * - */ -class TypeConfig extends ContainerConfig{ - - /** - * {@inheritdoc} - */ - public function define(Container $container){ - $container->types([ - Container::class => $container, - RouteCollector::class => $container->lazyGet("app.routes"), - EmitterInterface::class => $container->lazyGet("app.response.emitter"), - EventManagerInterface::class => $container->lazyGet("app.eventManager"), - Twig_Environment::class => $container->lazyGet("app.twig"), - Environment::class => $container->lazyGet("app.twig"), - ResponseFactoryInterface::class => $container->lazyGet("app.factory.response"), - ContainerInterface::class => $container, - CacheItemPoolInterface::class => $container->lazyGet("app.cache"), - CacheInterface::class => $container->lazyGet("app.simplecache"), - ServerRequestInterface::class => $container->lazyGet("app.request"), - LoggerInterface::class => $container->lazyGet("app.log"), - ]); - } -} \ No newline at end of file diff --git a/src/Controller/Controller.php b/src/Controller/Controller.php deleted file mode 100644 index 65330f2..0000000 --- a/src/Controller/Controller.php +++ /dev/null @@ -1,80 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Controller; - -use Psr\Http\Message\ResponseInterface; -use Interop\Http\Factory\ResponseFactoryInterface; -use Twig\Environment; - -/** - * - */ -abstract class Controller{ - - use \Fratily\Framework\Traits\PerformanceTrait; - use \Fratily\Framework\Traits\DumpTrait; - use \Fratily\Framework\Traits\LogTrait; - use \Fratily\Framework\Traits\EventTrait; - use \Fratily\Framework\Traits\DebugTrait; - - /** - * @var ResponseFactoryInterface - */ - private $factory; - - /** - * @var Environment - */ - private $twig; - - /** - * Constructor - * - * @param ContainerInterface $container - */ - public function __construct( - ResponseFactoryInterface $factory, - Environment $twig - ){ - $this->factory = $factory; - $this->twig = $twig; - } - - /** - * レスポンスを生成する - * - * @param int $code HTTPレスポンスステータスコード - * - * @return ResponseInterface - * - * @throws ContainerNotFoundException - */ - protected function response(int $code = 200){ - return $this->factory->createResponse($code); - } - - /** - * テンプレートエンジンのレンダリング結果を取得 - * - * @param string $name - * @param mixed[] $context - * - * @return string - * - * @throws ContainerNotFoundException - */ - protected function render(string $name, array $context = []){ - return $this->twig->render($name, $context); - } -} \ No newline at end of file diff --git a/src/Controller/ControllerMiddleware.php b/src/Controller/ControllerMiddleware.php new file mode 100644 index 0000000..3c76f4a --- /dev/null +++ b/src/Controller/ControllerMiddleware.php @@ -0,0 +1,54 @@ +getAttribute(RequestAttribute::CONTROLLER_CALLBACK); + $args = $request->getAttribute(RequestAttribute::CONTROLLER_ARGS, []); + + if ($controller === null) { + $attribute_name = RequestAttribute::CONTROLLER_CALLBACK; + throw new LogicException("The request attribute {$attribute_name} not found."); + } + + if (!is_callable($controller)) { + $attribute_name = RequestAttribute::CONTROLLER_CALLBACK; + throw new LogicException("The request attribute {$attribute_name} is not callable."); + } + + if (!is_array($args)) { + $attribute_name = RequestAttribute::CONTROLLER_ARGS; + throw new LogicException( + "The request attribute {$attribute_name} must be an array that can be expanded as an argument." + ); + } + + $response = $controller(...$args); + + if (!is_object($response) || !$response instanceof ResponseInterface) { + if ($this->converter !== null) { + return $this->converter->convert($request, $response); + } + + throw new LogicException('todo: write message.'); + } + + return $response; + } +} diff --git a/src/Controller/HttpErrorController.php b/src/Controller/HttpErrorController.php deleted file mode 100644 index 28b9ea1..0000000 --- a/src/Controller/HttpErrorController.php +++ /dev/null @@ -1,155 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Controller; - -use Fratily\Http\Message\Status\BadRequest; -use Fratily\Http\Message\Status\Forbidden; -use Fratily\Http\Message\Status\NotFound; -use Fratily\Http\Message\Status\MethodNotAllowed; -use Fratily\Http\Message\Status\InternalServerError; -use Fratily\Http\Message\Status\NotImplemented; -use Fratily\Http\Message\Status\ServiceUnavailable; -use Psr\Http\Message\ServerRequestInterface; - -/** - * - */ -class HttpErrorController extends Controller{ - - /** - * - * @param ServerRequestInterface $_request - * @param string $msg - * @param int $code - * @param \Throwable $prev - * - * @throws BadRequest - */ - public function badRequest( - ServerRequestInterface $_request, - string $msg = "", - int $code = 0, - \Throwable $prev = null - ){ - throw new BadRequest($msg, $code, $prev); - } - - /** - * - * @param ServerRequestInterface $_request - * @param string $msg - * @param int $code - * @param \Throwable $prev - * - * @throws Forbidden - */ - public function forbidden( - ServerRequestInterface $_request, - string $msg = "", - int $code = 0, - \Throwable $prev = null - ){ - throw new Forbidden($msg, $code, $prev); - } - - /** - * - * @param ServerRequestInterface $_request - * @param string $msg - * @param int $code - * @param \Throwable $prev - * - * @throws NotFound - */ - public function notFound( - ServerRequestInterface $_request, - string $msg = "", - int $code = 0, - \Throwable $prev = null - ){ - throw new NotFound($msg, $code, $prev); - } - - /** - * - * @param ServerRequestInterface $_request - * @param string $msg - * @param int $code - * @param \Throwable $prev - * - * @throws MethodNotAllowed - */ - public function methodNotAllowed( - ServerRequestInterface $_request, - string $msg = "", - int $code = 0, - \Throwable $prev = null - ){ - throw new MethodNotAllowed($msg, $code, $prev); - } - - /** - * - * @param ServerRequestInterface $_request - * @param string $msg - * @param int $code - * @param \Throwable $prev - * - * @throws InternalServerError - */ - public function internalServerError( - ServerRequestInterface $_request, - string $msg = "", - int $code = 0, - \Throwable $prev = null - ){ - throw new InternalServerError($msg, $code, $prev); - } - - /** - * - * @param ServerRequestInterface $_request - * @param string $msg - * @param int $code - * @param \Throwable $prev - * - * @throws NotImplemented - */ - public function notImplemented( - ServerRequestInterface $_request, - string $msg = "", - int $code = 0, - \Throwable $prev = null - ){ - throw new NotImplemented($msg, $code, $prev); - } - - /** - * - * @param ServerRequestInterface $_request - * @param string $msg - * @param int $code - * @param \Throwable $prev - * - * @throws ServiceUnavailable - */ - public function serviceUnavailable( - ServerRequestInterface $_request, - string $msg = "", - int $code = 0, - \Throwable $prev = null - ){ - throw new ServiceUnavailable($msg, $code, $prev); - } -} \ No newline at end of file diff --git a/src/Controller/ResponseConverterInterface.php b/src/Controller/ResponseConverterInterface.php new file mode 100644 index 0000000..80bf4ac --- /dev/null +++ b/src/Controller/ResponseConverterInterface.php @@ -0,0 +1,20 @@ + - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Debug; - -use Fratily\EventManager\EventManagerInterface; -use Fratily\DebugBar\DebugBar; - -class Debug{ - - /** - * @var DebugBar|null - */ - private static $debugbar; - - private static $log; - - private static $dump; - - - public static function getDebugBar(){ - if(self::$debugbar === null){ - self::$debugbar = new DebugBar; - } - } - - public static function attachEventListener(EventManagerInterface $manager){ - $manager->attach("log", [self::class, ""]); - } -} \ No newline at end of file diff --git a/src/Debug/Panel/MessagePanel.php b/src/Debug/Panel/MessagePanel.php deleted file mode 100644 index 788ab4c..0000000 --- a/src/Debug/Panel/MessagePanel.php +++ /dev/null @@ -1,98 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Debug\Panel; - -use Fratily\DebugBar\Panel\AbstractPanel; -use Fratily\DebugBar\Block\TableBlock; -use Fratily\DebugBar\Block\MetricsBlock; -use Psr\Log\LogLevel; - -class MessagePanel extends AbstractPanel{ - - const LEVEL = [ - LogLevel::EMERGENCY => "EMERGENCY", - LogLevel::ALERT => "ALERT", - LogLevel::CRITICAL => "CRITICAL", - LogLevel::ERROR => "ERROR", - LogLevel::WARNING => "WARNING", - LogLevel::NOTICE => "NOTICE", - LogLevel::INFO => "INFO", - LogLevel::DEBUG => "DEBUG", - ]; - - /** - * @var int[] - */ - private $count = [ - LogLevel::EMERGENCY => 0, - LogLevel::ALERT => 0, - LogLevel::CRITICAL => 0, - LogLevel::ERROR => 0, - LogLevel::WARNING => 0, - LogLevel::NOTICE => 0, - LogLevel::INFO => 0, - LogLevel::DEBUG => 0, - ]; - - /** - * @var TableBlock - */ - private $table; - - /** - * @var MetricsBlock - */ - private $metrics; - - /** - * Constructor - * - * @param string $name - */ - public function __construct(string $name){ - $this->metrics = new MetricsBlock(); - $this->table = new TableBlock(["Level", "Message"]); - - parent::__construct($name, [$this->metrics, $this->table]); - } - - /** - * メッセージを追加する - * - * @param string $message - * @param mixed $level - * - * @return $this - */ - public function addMessage(string $message, $level = LogLevel::DEBUG){ - if(!array_key_exists($level, self::LEVEL)){ - return; - } - - $this->table->addRow(self::LEVEL[$level], $message); - - $this->count[$level]++; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function beforeGetIterator(){ - foreach(self::LEVEL as $level => $name){ - $this->metrics->addTextMetric($name, $this->count[$level]); - } - } -} \ No newline at end of file diff --git a/src/Debug/Panel/PerformancePanel.php b/src/Debug/Panel/PerformancePanel.php deleted file mode 100644 index 7ca60a7..0000000 --- a/src/Debug/Panel/PerformancePanel.php +++ /dev/null @@ -1,153 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Debug\Panel; - -use Fratily\DebugBar\Panel\AbstractPanel; -use Fratily\DebugBar\Block\TimelineBlock; -use Fratily\DebugBar\Block\MetricsBlock; - -class PerformancePanel extends AbstractPanel{ - - /** - * @var TimelineBlock - */ - private $timeline; - - /** - * @var MetricsBlock - */ - private $metrics; - - /** - * @var float - */ - private $start; - - /** - * @var float - */ - private $end; - - /** - * @var float[] - */ - private $lineStart = []; - - /** - * Constructor - * - * @param string $name - * @param float $start - */ - public function __construct(string $name, float $start){ - $this->timeline = new TimelineBlock(); - $this->metrics = new MetricsBlock(); - $this->start = $start; - - parent::__construct($name, [ - $this->metrics, - $this->timeline, - ]); - } - - /** - * タイムラインの終了時間を設定する - * - * マイクロ秒精度UNIXタイムスタンプで設定する - * - * @param float $time - * - * @return void - */ - public function setEndTime(float $time){ - if($time <= $this->start){ - throw new \LogicException; - } - - $this->end = $time; - - $this->timeline->setExecutionTime($this->end - $this->start); - } - - /** - * 指定名のタイムラインを開始する - * - * @param string $name - * - * @return void - */ - public function start(string $name){ - $this->lineStart[$name] = microtime(true); - } - - /** - * 指定名のタイムラインを終了する - * - * @param string $name - * - * @return void - */ - public function end(string $name){ - $time = microtime(true); - - if(!array_key_exists($name, $this->lineStart)){ - throw new \LogicException; - } - - $this->timeline->addLine( - $name, - $this->lineStart[$name] - $this->start, - $time - $this->lineStart[$name] - ); - } - - /** - * タイムラインを追加する - * - * @param string $name - * タイムライン名 - * @param float $start - * タイムライン開始時間 - * @param float $end - * タイムライン終了時間 - * - * @return $this - */ - public function addLine(string $name, float $start, float $end){ - $this->timeline->addLine( - $name, - $start - $this->start, - $end - $start - ); - - return $this; - } - - protected function beforeGetIterator(){ - if($this->end === null){ - $this->end = microtime(true); - } - - $this->metrics->addTextMetric( - "Execution time", - round(($this->end - $this->start) * 1000, 2), - "ms" - ); - $this->metrics->addTextMetric( - "Peak memory usage", - round(memory_get_peak_usage(true) / (1024 * 1024), 3), - "MB" - ); - } -} \ No newline at end of file diff --git a/src/Exception/ConfigureException.php b/src/Exception/ConfigureException.php deleted file mode 100644 index 27601e8..0000000 --- a/src/Exception/ConfigureException.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Exception; - -/** - * - */ -class ConfigureException extends \Exception{ - - public static function unexpectedValue(string $key, string $type){ - return new static("{$key} expected type of {$type}"); - } - - public static function overwrite(string $key){ - return new static("{$key} can not overwrite"); - } -} \ No newline at end of file diff --git a/src/Exception/ContainerNotFoundException.php b/src/Exception/ContainerNotFoundException.php deleted file mode 100644 index 64a57f9..0000000 --- a/src/Exception/ContainerNotFoundException.php +++ /dev/null @@ -1,35 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Exception; - -use Psr\Container\NotFoundExceptionInterface; - -/** - * - */ -class ContainerNotFoundException extends \LogicException implements NotFoundExceptionInterface{ - - const MESSAGE = "Not found {id} in dependency injection container."; - - /** - * Constructor - * - * @param string $id - * @param int $code - * @param \Throwable $previous - */ - public function __construct(string $id, int $code = 0, \Throwable $previous = null){ - parent::__construct(str_replace("{id}", $id, self::MESSAGE), $code, $previous); - } -} \ No newline at end of file diff --git a/src/Http/Exception/HttpException.php b/src/Http/Exception/HttpException.php new file mode 100644 index 0000000..b953517 --- /dev/null +++ b/src/Http/Exception/HttpException.php @@ -0,0 +1,40 @@ + $status_code + */ + public function __construct( + string $message, + private int $status_code, + Throwable|null $prev = null, + int $code = 0 + ) { + parent::__construct($message, $code, $prev); + } + + /** + * Returns the http response status code. + * + * @return int + * + * @phpstan-return int<100,599> + */ + public function getStatusCode(): int + { + return $this->status_code; + } +} diff --git a/src/Http/Exception/NotFoundException.php b/src/Http/Exception/NotFoundException.php new file mode 100644 index 0000000..3279a89 --- /dev/null +++ b/src/Http/Exception/NotFoundException.php @@ -0,0 +1,20 @@ +,non-empty-string> + * + * @link https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml + * HTTP Status Code Registry (version 19) + */ + public const STATUS_PHRASES = [ + 100 => 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 103 => 'Early Hints', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', + 208 => 'Already Reported', + 226 => 'IM Used', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 308 => 'Permanent Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Content Too Large', + 414 => 'URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', // It's a famous joke and should be available. + 421 => 'Misdirected Request', + 422 => 'Unprocessable Content', + 423 => 'Locked', + 424 => 'Failed Dependency', + 425 => 'Too Early', + 426 => 'Upgrade Required', + 428 => 'Precondition Required', + 429 => 'Too Many Requests', + 431 => 'Request Header Fields Too Large', + 451 => 'Unavailable For Legal Reasons', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported', + 506 => 'Variant Also Negotiates', + 507 => 'Insufficient Storage', + 508 => 'Loop Detected', + 511 => 'Network Authentication Required', + ]; + + /** + * @param int|null $body_seek_length + * + * @phpstan-param positive-int|null $body_seek_length + */ + public function __construct(protected int|null $body_seek_length = null) + { + } + + /** + * {@inheritDoc} + */ + public function send(ResponseInterface $response): void + { + $this->sendHeaders($response); + $this->sendBody($response); + } + + public function sendHeaders(ResponseInterface $response): void + { + if (headers_sent()) { + return; + } + + header( + sprintf( + 'HTTP/%s %s %s', + $response->getProtocolVersion(), + $response->getStatusCode(), + $response->getReasonPhrase() !== '' + ? $response->getReasonPhrase() + : static::STATUS_PHRASES[$response->getStatusCode()] ?? 'unknown status code' + ), + true, + $response->getStatusCode() + ); + + foreach (array_keys($response->getHeaders()) as $name) { + if (strtolower($name) === 'set-cookie') { + foreach ($response->getHeader($name) as $cookie_value) { + header('Set-Cookie: ' . $cookie_value, false); + } + continue; + } + + header($name . ': ' . $response->getHeaderLine($name), true); + } + } + + public function sendBody(ResponseInterface $response): void + { + $body = $response->getBody(); + + if (!$body->isReadable()) { + throw new LogicException('todo: message'); + } + + if (!$body->isSeekable() || $this->body_seek_length === null) { + echo (string)$body; + return; + } + + $body->rewind(); + while (!$body->eof()) { + echo $body->read($this->body_seek_length); + $body->seek($this->body_seek_length, SEEK_CUR); + } + } +} diff --git a/src/Http/ResponseSenderInterface.php b/src/Http/ResponseSenderInterface.php new file mode 100644 index 0000000..4a518e1 --- /dev/null +++ b/src/Http/ResponseSenderInterface.php @@ -0,0 +1,17 @@ +middleware_list = $middleware_list; + $this->fallback_middleware = $fallback_middleware; + return $this; + } + + /** + * @param EventDispatcherInterface $event_dispatcher + */ + public function setEventDispatcher(EventDispatcherInterface $event_dispatcher): static + { + $this->event_dispatcher = $event_dispatcher; + return $this; + } + + public function handle(ServerRequestInterface $request): void + { + $request_event = $this->dispatchEvent(new RequestEvent($request)); + + if ($request_event->getResponse() !== null) { + $response = $request_event->getResponse(); + } else { + $response = $this->handleMiddleware($request); + } + + $response_decide_event = new ResponseDecideEvent($request, $response); + $this->dispatchEvent($response_decide_event); + $response = $response_decide_event->getResponse(); + + $this->response_sender->send($response); + + $this->dispatchEvent(new TerminateEvent($request, $response)); + } + + private function handleMiddleware(ServerRequestInterface $request): ResponseInterface + { + $handler = new class ($this->middleware_list, $this->fallback_middleware) implements RequestHandlerInterface { + /** + * @param MiddlewareInterface[] $middleware_list + */ + public function __construct( + private array $middleware_list, + private MiddlewareInterface|null $fallback_middleware, + private bool $fallback_called = false + ) { + } + + public function handle(ServerRequestInterface $request): ResponseInterface + { + if (count($this->middleware_list) > 0) { + return array_shift($this->middleware_list)->process($request, $this); + } + + if ($this->fallback_called) { + throw new LogicException(); + } + + if ($this->fallback_middleware === null) { + throw new LogicException(); + } + + $this->fallback_called = true; + return $this->fallback_middleware->process($request, $this); + } + }; + + return $handler->handle($request); + } + + /** + * Provide all relevant listeners with an event to process. + * + * @param object $event The object to process. + * @return object The Event that was passed, now modified by listeners. + * + * @template T of object + * @phpstan-param T $event + * @phpstan-return T + */ + private function dispatchEvent(object $event): object + { + if ($this->event_dispatcher !== null) { + // @phpstan-ignore-next-line but returns object. + return $this->event_dispatcher->dispatch($event); + } + + return $event; + } +} diff --git a/src/KernelEvent.php b/src/KernelEvent.php new file mode 100644 index 0000000..d46facb --- /dev/null +++ b/src/KernelEvent.php @@ -0,0 +1,28 @@ +request; + } +} diff --git a/src/KernelEvent/CanRespondTrait.php b/src/KernelEvent/CanRespondTrait.php new file mode 100644 index 0000000..f093421 --- /dev/null +++ b/src/KernelEvent/CanRespondTrait.php @@ -0,0 +1,36 @@ +response; + } + + /** + * Set the response to this event. + * + * Many events return the response specified here to the client. + * + * @param ResponseInterface|null $response + * @return $this + */ + public function setResponse(ResponseInterface|null $response): static + { + $this->response = $response; + return $this; + } +} diff --git a/src/KernelEvent/ReqeustEvent.php b/src/KernelEvent/ReqeustEvent.php new file mode 100644 index 0000000..6cc2c6a --- /dev/null +++ b/src/KernelEvent/ReqeustEvent.php @@ -0,0 +1,17 @@ +response; + } + + /** + * Set the response to this event. + * + * @param ResponseInterface $response + * @return $this + */ + public function setResponse(ResponseInterface $response): static + { + $this->response = $response; + return $this; + } +} diff --git a/src/KernelEvent/StoppableTrait.php b/src/KernelEvent/StoppableTrait.php new file mode 100644 index 0000000..eff69ef --- /dev/null +++ b/src/KernelEvent/StoppableTrait.php @@ -0,0 +1,37 @@ +propagation_stopped; + } + + /** + * Stop propagation. + * + * @return $this + */ + public function stopPropagation(): static + { + $this->propagation_stopped = true; + + return $this; + } +} diff --git a/src/KernelEvent/TerminateEvent.php b/src/KernelEvent/TerminateEvent.php new file mode 100644 index 0000000..b0ed02d --- /dev/null +++ b/src/KernelEvent/TerminateEvent.php @@ -0,0 +1,32 @@ +response; + } +} diff --git a/src/Middleware/ActionMiddleware.php b/src/Middleware/ActionMiddleware.php deleted file mode 100644 index 93078b9..0000000 --- a/src/Middleware/ActionMiddleware.php +++ /dev/null @@ -1,160 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Middleware; - -use Fratily\Framework\Controller\Controller; -use Fratily\Container\Container; -use Fratily\Reflection\ReflectionCallable; -use Psr\Container\ContainerInterface; -use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Server\RequestHandlerInterface; -use Psr\Http\Server\MiddlewareInterface; - -/** - * - */ -class ActionMiddleware implements MiddlewareInterface{ - - /** - * @var Container - */ - private $container; - - /** - * @var ReflectionCallable - */ - private $action; - - /** - * @var mixed[] - */ - private $params; - - /** - * コントローラーのクラス名とアクションメソッド名からアクションミドルウェアを作成 - * - * @param ContainerInterface $container - * @param string $controller - * @param string $method - * @param mixed[] $params - * - * @return static - * - * @throws \InvalidArgumentException - * - * @todo ここは修正の余地あり。仕様を確定させないといけない - */ - public static function getInstanceWithController( - ContainerInterface $container, - string $controller, - string $method, - array $params = [] - ){ - if(!class_exists($controller)){ - throw new \InvalidArgumentException(); - } - - $controller = $container->has($controller) - ? $container->get($controller) - : new $controller($container) - ; - - if(!($controller instanceof Controller)){ - throw new \InvalidArgumentException(); - } - - if(!is_callable([$controller, $method])){ - throw new \InvalidArgumentException(); - } - - return new static($container, [$controller, $method], $params); - } - - /** - * Constructor - * - * @param Container $container - * @param callable $action - * @param mixed[] $params - */ - public function __construct( - Container $container, - $action, - array $params = [] - ){ - $this->container = $container; - $this->action = $action; - $this->params = $params; - } - - /** - * アクションを設定する - * - * @param mixed $action - * - * @return $this - */ - public function setAction($action){ - $this->action = $action; - - return $this; - } - - /** - * パラメータを設定する - * - * @param mixed[] $params - * - * @return $this - */ - public function setParams(array $params){ - $this->params = $params; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function process( - ServerRequestInterface $request, - RequestHandlerInterface $handler - ): ResponseInterface{ - $action = $this->container->lazy( - $this->action, - array_merge([ - "_request" => $request, - "_params" => $this->params, - ], $this->params) - ); - - $response = $handler->handle($request); - $_response = $action->load(); - - if($_response instanceof ResponseInterface){ - $response = $_response; - }else if(is_scalar($_response) || $_response === null){ - if(!$response->getBody()->isWritable()){ - throw new \LogicException; - } - - $response->getBody()->write($_response ?? ""); - }else{ - throw new \UnexpectedValueException; - } - - return $response; - } -} \ No newline at end of file diff --git a/src/Middleware/DebugMiddleware.php b/src/Middleware/DebugMiddleware.php deleted file mode 100644 index 4522d16..0000000 --- a/src/Middleware/DebugMiddleware.php +++ /dev/null @@ -1,81 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Middleware; - -use Fratily\DebugBar\DebugBar; -use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\StreamInterface; -use Psr\Http\Server\RequestHandlerInterface; -use Psr\Http\Server\MiddlewareInterface; - -/** - * - */ -class DebugMiddleware implements MiddlewareInterface{ - - use \Fratily\Framework\Traits\DebugTrait; - - /** - * @var DebugBar - */ - private $debugbar; - - /** - * Constructor - * - * @param DebugBar $debugbar - * デバッグバーを埋め込むためのインスタンス - */ - public function __construct(DebugBar $debugbar){ - $this->debugbar = $debugbar; - } - - /** - * {@inheritdoc} - */ - public function process( - ServerRequestInterface $request, - RequestHandlerInterface $handler - ): ResponseInterface{ - $response = $handler->handle($request); - - if($this->isDebug()){ - $response = $response->withBody( - $this->addDebugToolBar($response->getBody()) - ); - } - - return $response; - } - - /** - * デバッグ用のツールバーを追加する - * - * レスポンスがhtml形式の場合のみ、 - * bodyの閉じタグ直前にツールバーのブロック要素を追加する。 - * - * @param StreamInterface $body - * - * @return StreamInterface - */ - private function addDebugToolBar(StreamInterface $body){ - $newBody = new \Fratily\Http\Message\Stream\MemoryStream(); - - $body->rewind(); - $newBody->write($this->debugbar->embed($body->getContents())); - - return $newBody; - } -} \ No newline at end of file diff --git a/src/Middleware/ErrorMiddleware.php b/src/Middleware/ErrorMiddleware.php deleted file mode 100644 index 9d408a5..0000000 --- a/src/Middleware/ErrorMiddleware.php +++ /dev/null @@ -1,217 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Middleware; - -use Fratily\Http\Message\Status\HttpStatus; -use Twig\Environment; -use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Server\RequestHandlerInterface; -use Psr\Http\Server\MiddlewareInterface; -use Interop\Http\Factory\ResponseFactoryInterface; - -/** - * - */ -class ErrorMiddleware implements MiddlewareInterface{ - - use \Fratily\Framework\Traits\DebugTrait; - - /** - * @var Environment - */ - protected $twig; - - /** - * @var ResponseFactoryInterface - */ - protected $factory; - - /** - * Constructor - * - * @param Environment $twig - * エラー描画用Twig - * @param ResponseFactoryInterface $factory - * レスポンスファクトリ - */ - public function __construct( - Environment $twig, - ResponseFactoryInterface $factory - ){ - $this->twig = $twig; - $this->factory = $factory; - } - - /** - * {@inheritdoc} - */ - public function process( - ServerRequestInterface $request, - RequestHandlerInterface $handler - ): ResponseInterface{ - try{ - $response = $handler->handle($request); - }catch(\Throwable $e){ - $response = $this->isDebug() - ? $this->createErrorPageInDebugMode($e) - : $this->createErrorPage($e) - ; - } - - return $response; - } - - /** - * 例外を本番で出しても問題ないレスポンスに変換する - * - * @param \Throwable $e - * - * @return ResponseInterface - */ - protected function createErrorPage(\Throwable $e){ - $response = $this->factory->createResponse( - $e instanceof HttpStatus ? $e->getStatusCode() : 500 - ); - - if($e instanceof \Fratily\Http\Message\Status\MethodNotAllowed){ - $response = $response->withHeader( - "Allow", - implode(", ", $e->getAllowed()) - ); - } - - return $response; - } - - /** - * デバッグモード用のエラーページ描画用のレスポンスインスタンスを生成する - * - * @param \Throwable $e - * - * @return ResponseInterface - */ - protected function createErrorPageInDebugMode(\Throwable $e){ - $response = $this->createErrorPage($e); - $context = [ - "errors" => [], - ]; - - do{ - $context["errors"][] = $this->analysisError($e); - }while(($e = $e->getPrevious()) !== null); - - $response->getBody()->write($this->twig->render("error.twig", $context)); - - return $response; - } - - protected function analysisError(\Throwable $e){ - $result = [ - "name" => get_class($e), - "message" => $e->getMessage(), - "code" => $e->getCode(), - "file" => $e->getFile(), - "line" => $e->getLine(), - "script" => $this->getFileContents($e->getFile(), $e->getLine()), - "trace" => [], - ]; - - foreach($e->getTrace() as $t){ - $call = ($t["class"] ?? "") . ($t["type"] ?? "") - . ($t["function"] ?? "unknown") . "(" - . implode(", ", array_map([$this, "getDumpString"], $t["args"] ?? [])) - . ")" - ; - - $result["trace"][] = [ - "call" => $call, - "file" => $t["file"] ?? "unknown", - "line" => $t["line"] ?? 0, - "script" => $this->getFileContents($t["file"] ?? null, $t["line"] ?? null), - ]; - } - - return $result; - } - - /** - * ファイルの指定行と前後3行を取得する - * - * 行番号をキーとした連想配列を返す - * - * @param string $file - * @param int $line - * - * @return string[] - */ - protected function getFileContents($file, $line){ - if(!is_string($file) || !is_int($line) - || !is_file($file) || !is_readable($file) - ){ - return null; - } - - $file = new \SplFileObject($file, "r"); - $current = 1; - $contents = []; - $min = $line - 3; - $max = $line + 3; - - foreach($file as $row){ - if($min <= $current && $current <= $max){ - $contents[$current] = $row; - } - - $current++; - } - - if(empty($contents)){ - return null; - } - - return $contents; - } - - /** - * 変数の値をダンプ用文字列に変換する - * - * @param mixed $value - * - * @return string - */ - protected function getDumpString($value){ - switch(gettype($value)){ - case "boolean": - return $value ? "TRUE" : "FALSE"; - case "integer": - case "double": - return (string)$value; - case "string": - return "'{$value}'"; - case "array": - return "Array(" . count($value) . ")"; - case "object": - return get_class($value); - case "resource": - return "resource(" . get_resource_type($value) . ")"; - case "resource (closed)": - return "closed resource(" . get_resource_type($value) . ")"; - case "NULL": - return "NULL"; - } - - return "unknown type"; - } -} \ No newline at end of file diff --git a/src/Middleware/WrapperMiddleware.php b/src/Middleware/WrapperMiddleware.php deleted file mode 100644 index b73083d..0000000 --- a/src/Middleware/WrapperMiddleware.php +++ /dev/null @@ -1,76 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Middleware; - -use Fratily\Framework\Debug\Panel\PerformancePanel; -use Fratily\EventManager\EventManagerInterface; -use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Server\RequestHandlerInterface; -use Psr\Http\Server\MiddlewareInterface; - - -/** - * - */ -class WrapperMiddleware implements MiddlewareInterface{ - - use \Fratily\Framework\Traits\DebugTrait; - - /** - * @var EventManagerInterface - */ - private $eventMng; - - /** - * @var PerformancePanel - */ - private $performance; - - /** - * Constructor - * - * @param EventManagerInterface $eventMng - */ - public function __construct(EventManagerInterface $eventMng){ - $this->eventMng = $eventMng; - } - - /** - * - * - * @param PerformancePanel $timeline - * - * @return void - */ - public function setPerformancePanel(PerformancePanel $timeline){ - $this->performance = $timeline; - } - - /** - * {@inheritdoc} - */ - public function process( - ServerRequestInterface $request, - RequestHandlerInterface $handler - ): ResponseInterface{ - $response = $handler->handle($request); - - if($this->performance !== null){ - $this->performance->setEndTime(microtime(true)); - } - - return $response; - } -} \ No newline at end of file diff --git a/src/Render/RenderInterface.php b/src/Render/RenderInterface.php deleted file mode 100644 index 6ef1eea..0000000 --- a/src/Render/RenderInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Render; - -interface RenderInterface{ - - /** - * 指定テンプレートに変数配列を渡し描画文字列を取得する - * - * @param string $path テンプレートの指定に使う - * @param mixed[] $context テンプレートに渡す変数配列 - * - * @return string - */ - public function render(string $path, array $context = []): string; -} \ No newline at end of file diff --git a/src/Render/TwigRender.php b/src/Render/TwigRender.php deleted file mode 100644 index af211ce..0000000 --- a/src/Render/TwigRender.php +++ /dev/null @@ -1,52 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Render; - -use Fratily\Utility\Hash; -use Twig\Environment; - -/** - * - */ -class TwigRender implements RendererInterface{ - - /* - * @var Environment - */ - private $twig; - - /** - * Constructor - * - * @param Environment $twig - */ - public function __construct(Environment $twig){ - $this->twig = $twig; - } - - /** - * {@inheritdoc} - */ - public function render(string $path, array $context = []): string{ - $_context = []; - - foreach($context as $key => $val){ - $_context = Hash::set($_context, $key, $val); - } - - return $this->twig->render($path, $context); - } - - -} \ No newline at end of file diff --git a/src/Response.php b/src/Response.php deleted file mode 100644 index 7c61658..0000000 --- a/src/Response.php +++ /dev/null @@ -1,156 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework; - -use Fratily\Http\Message\Response\EmitterInterface; -use Fratily\EventManager\Event; -use Psr\Http\Message\ServerRequestInterface; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Server\RequestHandlerInterface; - -/** - * - */ -class Response{ - - use Traits\EventTrait; - - /** - * @var ServerRequestInterface - */ - protected $request; - - /** - * @var RequestHandlerInterface - */ - protected $handler; - - /** - * @var EmitterInterface - */ - protected $emitter; - - /** - * @var bool - */ - private $send = false; - - /** - * @var \Throwable|null - */ - private $error = null; - - /** - * Constructor - * - * @param RequestInterface $request - * @param RequestHandlerInterface $handler - * @param EmitterInterface $emmiter - */ - public function __construct( - ServerRequestInterface $request, - RequestHandlerInterface $handler, - EmitterInterface $emmiter - ){ - $this->request = $request; - $this->handler = $handler; - $this->emitter = $emmiter; - } - - /** - * 送信時に発生した例外もしくはエラーを取得する - * - * @return \Throwable|null - */ - public function getError(){ - return $this->error; - } - - /** - * リクエストハンドラを実行し生成されたレスポンスを送信する - * - * @return bool - */ - public function send(){ - if(!$this->send){ - $response = $this->handle(); - - if($response !== null){ - $this->emit($response); - } - - $this->send = true; - - $this->event(new Event("response.send.after")); - } - - return !($this->error instanceof \Throwable); - } - - /** - * ミドルウェアハンドラを実行してレスポンスを生成する - * - * @return ResponseInterface|null - */ - private function handle(){ - $params = [ - "start" => null, - "finish" => null, - "response" => null, - "error" => null, - ]; - - try{ - $params["start"] = microtime(true); - $params["response"] = $this->handler->handle($this->request); - }catch(\Throwable $e){ - $params["error"] = $e; - $this->error = $e; - }finally{ - $params["finish"] = microtime(true); - } - - $this->event(new Event("response.handle.after", $params)); - - return $params["response"]; - } - - /** - * レスポンスを送信する - * - * @param ResponseInterface $response - * - * @return void - */ - private function emit(ResponseInterface $response){ - $params = [ - "start" => null, - "finish" => null, - "error" => null, - ]; - - try{ - $params["start"] = microtime(true); - - $this->emitter->emit($response); - }catch(\Throwable $e){ - $params["error"] = $e; - $this->error = $e; - }finally{ - $params["finish"] = microtime(true); - } - - $this->event(new Event("response.emit.after", $params)); - } -} \ No newline at end of file diff --git a/src/Routing/Method.php b/src/Routing/Method.php new file mode 100644 index 0000000..a955478 --- /dev/null +++ b/src/Routing/Method.php @@ -0,0 +1,16 @@ +} + */ + public function match(string $method, UriInterface $uri): array|null; +} diff --git a/src/Routing/RoutingMiddleware.php b/src/Routing/RoutingMiddleware.php new file mode 100644 index 0000000..1d84d69 --- /dev/null +++ b/src/Routing/RoutingMiddleware.php @@ -0,0 +1,42 @@ +router->match($request->getMethod(), $request->getUri()); + + if ($match === null) { + if ($this->not_found_mode === self::NOT_FOUND_MODE_EXCEPTION) { + throw new NotFoundException('todo: write message'); + } + } else { + $request = $request + ->withAttribute(RequestAttribute::CONTROLLER_CALLBACK, $match['controller']) + ->withAttribute(RequestAttribute::ROUTING_MATCH_ROUTE_PARAMS, $match['params']); + } + + return $handler->handle($request); + } +} diff --git a/src/Traits/DebugTrait.php b/src/Traits/DebugTrait.php deleted file mode 100644 index e14df86..0000000 --- a/src/Traits/DebugTrait.php +++ /dev/null @@ -1,45 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Traits; - -/** - * - */ -trait DebugTrait{ - - /** - * @var bool - */ - private $debug; - - /** - * デバッグモードが有効か確認する - * - * @return bool - */ - public function isDebug(){ - return $this->debug ?? false; - } - - /** - * デバッグモードが有効か設定する - * - * @param bool $debug - * - * @return void - */ - public function setDebug(bool $debug){ - $this->debug = $debug; - } -} \ No newline at end of file diff --git a/src/Traits/DumpTrait.php b/src/Traits/DumpTrait.php deleted file mode 100644 index 6f1f04a..0000000 --- a/src/Traits/DumpTrait.php +++ /dev/null @@ -1,56 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Traits; - -use Fratily\DebugBar\Panel\DumpPanel; - -/** - * - */ -trait DumpTrait{ - - /** - * @var DumpPanel - */ - private $dumpPanel; - - /** - * - * - * @param DumpPanel $panel - * - * @return void - */ - public function setDumpPanel(DumpPanel $panel){ - $this->dumpPanel = $panel; - } - - /** - * - * @param mixed $val - * - * @return void - */ - public function dump($val){ - if($this->dumpPanel !== null){ - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); - - $this->dumpPanel->dump( - $val, - $trace[0]["file"] ?? "unknown", - $trace[0]["line"] ?? 0 - ); - } - } -} \ No newline at end of file diff --git a/src/Traits/EventTrait.php b/src/Traits/EventTrait.php deleted file mode 100644 index 5a4e2a8..0000000 --- a/src/Traits/EventTrait.php +++ /dev/null @@ -1,55 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Traits; - -use Fratily\EventManager\EventManagerInterface; - -/** - * - */ -trait EventTrait{ - - /** - * @var EventManagerInterface|null - */ - private $eventManager; - - /** - * - * - * @param EventManagerInterface $eventManager - * - * @return void - */ - public function setEventManager(EventManagerInterface $eventManager){ - $this->eventManager = $eventManager; - } - - /** - * イベントを発動する - * - * @param string|EventInterface $event - * @param mixed[] $args - * - * @return mixed - */ - public function event($event, ...$args){ - if($this->eventManager !== null){ - return $this->eventManager->trigger($event, $args); - } - - return false; - } - -} \ No newline at end of file diff --git a/src/Traits/LogTrait.php b/src/Traits/LogTrait.php deleted file mode 100644 index 30e4831..0000000 --- a/src/Traits/LogTrait.php +++ /dev/null @@ -1,211 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Traits; - -use Fratily\Framework\Debug\Panel\MessagePanel; -use Psr\Log\LoggerInterface; -use Psr\Log\LogLevel; - -/** - * - */ -trait LogTrait{ - - /** - * @var LoggerInterface|null - */ - private $logger; - - /** - * @var MessagePanel - */ - private $messagePanel; - - /** - * - * - * @param LoggerInterface $logger - * - * @return void - */ - public function setLogger(LoggerInterface $logger){ - $this->logger = $logger; - } - - /** - * - * - * @param MessagePanel $panel - * - * @return void - */ - public function setMessagePanel(MessagePanel $panel){ - $this->messagePanel = $panel; - } - - /** - * System is unusable. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function emergency(string $message, array $context = []){ - if($this->logger !== null){ - $this->logger->emergency($message, $context); - } - - if($this->messagePanel !== null){ - $this->messagePanel->addMessage($message, LogLevel::EMERGENCY); - } - } - - /** - * Action must be taken immediately. - * - * Example: Entire website down, database unavailable, etc. This should - * trigger the SMS alerts and wake you up. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function alert(string $message, array $context = []){ - if($this->logger !== null){ - $this->logger->alert($message, $context); - } - - if($this->messagePanel !== null){ - $this->messagePanel->addMessage($message, LogLevel::ALERT); - } - } - - /** - * Critical conditions. - * - * Example: Application component unavailable, unexpected exception. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function critical(string $message, array $context = []){ - if($this->logger !== null){ - $this->logger->critical($message, $context); - } - - if($this->messagePanel !== null){ - $this->messagePanel->addMessage($message, LogLevel::CRITICAL); - } - } - - /** - * Runtime errors that do not require immediate action but should typically - * be logged and monitored. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function error(string $message, array $context = []){ - if($this->logger !== null){ - $this->logger->error($message, $context); - } - - if($this->messagePanel !== null){ - $this->messagePanel->addMessage($message, LogLevel::ERROR); - } - } - - /** - * Exceptional occurrences that are not errors. - * - * Example: Use of deprecated APIs, poor use of an API, undesirable things - * that are not necessarily wrong. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function warning(string $message, array $context = []){ - if($this->logger !== null){ - $this->warning($message, $context); - } - - if($this->messagePanel !== null){ - $this->messagePanel->addMessage($message, LogLevel::WARNING); - } - } - - /** - * Normal but significant events. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function notice(string $message, array $context = []){ - if($this->logger !== null){ - $this->logger->notice($message, $context); - } - - if($this->messagePanel !== null){ - $this->messagePanel->addMessage($message, LogLevel::NOTICE); - } - } - - /** - * Interesting events. - * - * Example: User logs in, SQL logs. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function info(string $message, array $context = []){ - if($this->logger !== null){ - $this->logger->info($message, $context); - } - - if($this->messagePanel !== null){ - $this->messagePanel->addMessage($message, LogLevel::INFO); - } - } - - /** - * Detailed debug information. - * - * @param string $message - * @param mixed[] $context - * - * @return void - */ - public function debug(string $message, array $context = []){ - if($this->logger !== null){ - $this->logger->debug($message, $context); - } - - if($this->messagePanel !== null){ - $this->messagePanel->addMessage($message, LogLevel::DEBUG); - } - } -} \ No newline at end of file diff --git a/src/Traits/PerformanceTrait.php b/src/Traits/PerformanceTrait.php deleted file mode 100644 index 2c47461..0000000 --- a/src/Traits/PerformanceTrait.php +++ /dev/null @@ -1,50 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Framework\Traits; - -use Fratily\Framework\Debug\Panel\PerformancePanel; - -/** - * - */ -trait PerformanceTrait{ - - /** - * @var PerformancePanel - */ - private $performancePanel; - - /** - * - * - * @param PerformancePanel $panel - * - * @return void - */ - public function setPerformancePanel(PerformancePanel $panel){ - $this->performancePanel = $panel; - } - - public function startTimeline(string $name){ - if($this->performancePanel !== null){ - $this->performancePanel->start($name); - } - } - - public function endTimeline(string $name){ - if($this->performancePanel !== null){ - $this->performancePanel->end($name); - } - } -} \ No newline at end of file diff --git a/src/bootstrap.php b/src/bootstrap.php deleted file mode 100644 index 04f534e..0000000 --- a/src/bootstrap.php +++ /dev/null @@ -1,90 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ - -if(!defined("DS")){ - /** - * Alias of DIRECTORY_SEPARATOR. - */ - define("DS", DIRECTORY_SEPARATOR); -} - -if(!defined("FRATILY_FW_ROOT")){ - /** - * fratily/frameworkのルートパス - * - * vendor/fratily/frameworkを指すことになるはず。 - * - * @see Fratily\Framework\Container\CoreConfig - */ - define("FRATILY_FW_ROOT", realpath(__DIR__ . "/..")); -} - -if(!function_exists("getComposerClassLoader")){ - /** - * composerのクラスローダーを取得する - * - * @return \Composer\Autoload\ClassLoader|false - */ - function getComposerClassLoader(){ - static $loader; - - if($loader === null){ - $loader = false; - - foreach(get_declared_classes() as $class){ - if(substr($class, 0, 24) === "ComposerAutoloaderInited" - && method_exists($class, "getLoader") - ){ - $tmp = $class::getLoader(); - - if($tmp instanceof \Composer\Autoload\ClassLoader){ - $loader = $tmp; - } - } - } - } - - return $loader; - } -} - -if(!function_exists("throwThrowableObject")){ - /** - * 引数で与えられた名前のクラスがスロー可能な例外クラスであればスローする。 - * - * @param string $class - * @param string $msg - * @param int $code - * @param Throwable $prev - * - * @return void - * - * @throws InvalidArgumentException - * @throws LogicException - * @throws Exception - */ - function throwThrowableObject(string $class, string $msg = "", int $code = 0, Throwable $prev = null){ - if(!class_exists($class)){ - throw new InvalidArgumentException(); - } - - $instance = new $class($msg, $code, $prev); - - if($instance instanceof Exception){ - throw $instance; - } - - throw LogicException; - } -} \ No newline at end of file diff --git a/tests/ApplicationFactoryTest.php b/tests/ApplicationFactoryTest.php deleted file mode 100644 index 0b2619e..0000000 --- a/tests/ApplicationFactoryTest.php +++ /dev/null @@ -1,36 +0,0 @@ - - * @copyright (c) Kento Oka - * @license MIT - * @since 1.0.0 - */ -namespace Fratily\Tests\Framework; - -use Fratily\Framework\Application; -use Fratily\Framework\ApplicationFactory; -use Psr\Cache\CacheItemPoolInterface; -use Psr\Cache\CacheItemInterface; - -class ApplicationFactoryTest extends \PHPUnit\Framework\TestCase{ - - public function testStandard(){ - $cacheItem = $this->createMock(CacheItemInterface::class); - $cachePool = $this->createMock(CacheItemPoolInterface::class); - - $cachePool->method("getItem")->willReturn($cacheItem); - $cachePool->method("save")->willReturn(true); - - $cacheItem->method("isHit")->willReturn(false); - - $factory = new ApplicationFactory($cachePool); - - $this->assertInstanceOf(Application::class, $factory->create()); - } -} \ No newline at end of file diff --git a/tests/Unit/.gitkeep b/tests/Unit/.gitkeep new file mode 100644 index 0000000..e69de29