Skip to content

Commit

Permalink
Merge branch 'dev/infra'
Browse files Browse the repository at this point in the history
  • Loading branch information
clap-and-whistle committed Sep 24, 2022
2 parents b3e9803 + 32efb23 commit 810720c
Show file tree
Hide file tree
Showing 24 changed files with 376 additions and 246 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ autoload.php
/.phpcs.xml
/.phpcs-cache
/vendor-bin/tools/vendor/
/var/db/todo.sqlite3
51 changes: 49 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

## Installation

composer install
```bash
git clone https://github.com/clap-and-whistle/learn-bearsunday.git
cd learn-bearsunday
composer install
```

## Usage

Expand All @@ -15,11 +19,54 @@
composer serve // start builtin server
composer test // run unit test
composer tests // test and quality checks
composer twig-clean // Clear twig cache files
composer coverage // test coverage
composer cs-fix // fix the coding standard
composer doc // generate API document
composer run-script --list // list all commands

## open issue

```
#########################################
### First, delete temporary files
$ rm -rf ./var/tmp/*
#########################################
### Next, run the first phpunit
$ composer test
> ./vendor/bin/phpunit
PHPUnit 9.5.24 #StandWithUkraine
....F..EEEE...F........... 26 / 26 (100%)
Time: 00:04.648, Memory: 34.00 MB
There were 4 errors:
#########################################
### Then, run phpunit for the second time
$ composer test
> ./vendor/bin/phpunit
PHPUnit 9.5.24 #StandWithUkraine
.......................... 26 / 26 (100%)
Time: 00:04.260, Memory: 36.00 MB
OK (26 tests, 99 assertions)
```

Why is it that the first one fails but succeeds from the subsequent second run?

## Links

* BEAR.Sunday http://bearsunday.github.io/
- A memoir of the creation of this project on the author's blog
- https://zenn.dev/clap_n_whistle/articles/b730c5faab7d58
- BEAR.Sunday
- http://bearsunday.github.io/
24 changes: 24 additions & 0 deletions bin/setup.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,27 @@

chdir(dirname(__DIR__));
passthru('rm -rf ./var/tmp/*');

// SQLiteのデータベースが無ければ作成
if (! file_exists(dirname(__DIR__) . '/var/db/todo.sqlite3')) {
echo ' SQLiteのデータベースファイルを作成しています: ./var/db/todo.sqlite3' . PHP_EOL;
chdir(dirname(__DIR__) . '/var/db');
passthru('sqlite3 todo.sqlite3 < todo.sql');
chdir(dirname(__DIR__));
}

// note: ./var/tmp/ を空っぽにした直後の状態で composer test するとなぜか束縛の差し替えが期待通り動かないが、
// 連続して2回目の composer test を実行すると束縛の差し替えが効くようになる(この挙動の原因の究明はできていない)。
// そこで、この setup.php の中で1回目の「失敗する composer test」を済ませておくことにしたのが下記の行。
echo ' phpunit を実行しています(結果の出力内容はすべて捨てられます)' . PHP_EOL;
passthru('php -dextension=pcov.so -d pcov.enabled=1 ./vendor/bin/phpunit --stderr 2>/dev/null');
passthru('rm ./.phpunit.result.cache');

// プロジェクトルートに autoload.php が無ければ生成
chdir(dirname(__DIR__));
if (! file_exists('./autoload.php')) {
echo ' プロジェクトルートへ autoload.php を作成しています: ./autoload.php' . PHP_EOL;
passthru('./vendor/bin/bear.compile \'Cw\LearnBear\' prod-app ./ 2>/dev/null');
}

echo 'end setup.' . PHP_EOL;
16 changes: 10 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,25 @@
},
"scripts": {
"post-update-cmd": "@setup",
"post-install-cmd": "@composer bin all install --ansi",
"post-install-cmd": [
"@composer bin all install --ansi",
"@setup"
],
"setup": "php bin/setup.php",
"compile": "./vendor/bin/bear.compile 'Cw\\LearnBear' prod-app ./",
"compile": "./vendor/bin/bear.compile 'Cw\\LearnBear' prod-html-app ./",
"doc": "./vendor/bin/apidoc",
"test": "./vendor/bin/phpunit",
"coverage": "php -dzend_extension=xdebug.so -dxdebug.mode=coverage ./vendor/bin/phpunit --coverage-text --coverage-html=build/coverage",
"pcov": "php -dextension=pcov.so -d pcov.enabled=1 ./vendor/bin/phpunit --coverage-text --coverage-html=build/coverage --coverage-clover=coverage.xml",
"cs": "./vendor/bin/phpcs",
"cs-fix": "./vendor/bin/phpcbf src tests",
"metrics": "./vendor/bin/phpmetrics --report-html=build/metrics --exclude=Exception src",
"twig-clean": [
"rm -rf ./var/tmp/*/twig/*"
],
"twig-clean": "rm -rf ./var/tmp/*/twig/*",
"clean": [
"./vendor/bin/phpstan clear-result-cache",
"./vendor/bin/psalm --clear-cache",
"rm -rf ./var/tmp/*.php",
"rm -rf ./var/tmp/*/*"
"@twig-clean"
],
"sa": [
"./vendor/bin/psalm --show-info=true",
Expand All @@ -65,10 +66,12 @@
"tests": [
"@cs",
"@sa",
"@setup",
"@test"
],
"build": [
"@clean",
"@setup",
"@cs",
"@sa",
"@pcov",
Expand All @@ -91,6 +94,7 @@
"cs-fix": "Fix the coding standard",
"sa": "Run static analysis",
"metrics": "Build metrics report",
"twig-clean": "Clear twig cache files",
"clean": "Clear cache files",
"serve": "Start built-in server",
"app": "Request app resource",
Expand Down
3 changes: 3 additions & 0 deletions src/AppSpi/SessionHandlerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface SessionHandlerInterface
public const SESS_SEGMENT = self::class;

public const FLASH_KEY_FOR_LOGIN_FORM = 'for_index';
public const DUMMY_MESSAGE = 'dummy';

public function setAuth(string $uuid): void;

Expand All @@ -19,4 +20,6 @@ public function clearAuth(): void;
public function setFlashMessage(string $message, string $key): void;

public function getFlashMessage(string $key): ?string;

public function destroy(): void;
}
15 changes: 13 additions & 2 deletions src/Infrastructure/Authentication/CwSession.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,26 @@
namespace Cw\LearnBear\Infrastructure\Authentication;

use Aura\Session\Segment;
use Aura\Session\Session;
use Aura\Session\SessionFactory;
use Aura\Web\WebFactory;
use Cw\LearnBear\AppSpi\LoggerInterface;
use Cw\LearnBear\AppSpi\SessionHandlerInterface;

class CwSession implements SessionHandlerInterface
{
private readonly Session $session;
private readonly Segment $segment;

/**
* @SuppressWarnings(PHPMD.Superglobals)
*/
public function __construct(private readonly LoggerInterface $logger)
{
$session = (new SessionFactory())->newInstance(
$this->session = (new SessionFactory())->newInstance(
(new WebFactory($GLOBALS))->newRequest()->cookies->getArrayCopy()
);
$this->segment = $session->getSegment(self::SESS_SEGMENT);
$this->segment = $this->session->getSegment(self::SESS_SEGMENT);
}

public function setAuth(string $uuid): void
Expand Down Expand Up @@ -54,4 +56,13 @@ public function getFlashMessage(string $key): ?string

return $message;
}

public function destroy(): void
{
if (! $this->session->isStarted()) {
return;
}

$this->session->destroy();
}
}
18 changes: 18 additions & 0 deletions src/Infrastructure/Authentication/FakeIdentityRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Cw\LearnBear\Infrastructure\Authentication;

use Cw\LearnBear\AppSpi\IdentityRepositoryInterface;

class FakeIdentityRepository implements IdentityRepositoryInterface
{
/**
* 照合結果が必ず「照合成功」を示すようにメソッドを設定
*/
public function findByUserNameAndPassword(string $username, string $password): ?string
{
return 'pass_unconditionally';
}
}
42 changes: 42 additions & 0 deletions src/Infrastructure/Authentication/FakeSession.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Cw\LearnBear\Infrastructure\Authentication;

use Cw\LearnBear\AppSpi\SessionHandlerInterface;

class FakeSession implements SessionHandlerInterface
{
private ?string $message = null;

public function setAuth(string $uuid): void
{
$this->message = $uuid; // これはただのダミー処理。なんなら何もしなくても構わない。
}

public function isNotAuthorized(): bool
{
return false;
}

public function clearAuth(): void
{
}

public function setFlashMessage(string $message, string $key): void
{
$this->message = empty($key)
? SessionHandlerInterface::DUMMY_MESSAGE
: $message;
}

public function getFlashMessage(string $key): ?string
{
return $this->message ?? SessionHandlerInterface::DUMMY_MESSAGE;
}

public function destroy(): void
{
}
}
4 changes: 3 additions & 1 deletion src/Interceptor/AuthCheckInterceptor.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

class AuthCheckInterceptor implements MethodInterceptor
{
public const AUTH_ERROR_MESSAGE = 'ユーザー認証をしてください';

public function __construct(private readonly SessionHandlerInterface $cwSession)
{
}
Expand Down Expand Up @@ -49,7 +51,7 @@ private function makeError(AuthBaseResourceObject $resourceObj): AuthBaseResourc
$resourceObj->body = [
'status' => [
'code' => $resourceObj->code,
'message' => 'ユーザー認証をしてください',
'message' => self::AUTH_ERROR_MESSAGE,
],
];

Expand Down
26 changes: 26 additions & 0 deletions src/Module/AppFakeBinderModule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Cw\LearnBear\Module;

use BEAR\Resource\JsonRenderer;
use BEAR\Resource\RenderInterface;
use Cw\LearnBear\AppSpi\IdentityRepositoryInterface;
use Cw\LearnBear\AppSpi\SessionHandlerInterface;
use Cw\LearnBear\Infrastructure\Authentication\FakeIdentityRepository;
use Cw\LearnBear\Infrastructure\Authentication\FakeSession;
use Ray\Di\AbstractModule;

class AppFakeBinderModule extends AbstractModule
{
protected function configure(): void
{
// appコンテキストにおいては、Pageリソースの TwigErrorPageModule への依存を標準レンダラーでフェイクする
$this->bind(RenderInterface::class)->annotatedWith('error_page')->to(JsonRenderer::class);

// appコンテキストにおいては、Pageリソースが「認証機能を備えていない」のと同じ振る舞いをするように、下記2つをフェイクで置き換える
$this->bind(SessionHandlerInterface::class)->to(FakeSession::class);
$this->bind(IdentityRepositoryInterface::class)->to(FakeIdentityRepository::class);
}
}
10 changes: 8 additions & 2 deletions src/Module/AppModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Cw\LearnBear\AppSpi\LoggerInterface;
use Cw\LearnBear\Infrastructure\Logging\DebugLogger;
use Ray\AuraSqlModule\AuraSqlModule;
use Ray\WebFormModule\AuraInputModule;

use function dirname;

Expand All @@ -19,11 +20,16 @@ protected function configure(): void
{
(new Dotenv())->load(dirname(__DIR__, 2));
$this->install(new PackageModule());
$this->install(new CwAuthModule());
$this->install(new HtmlModule());
$appDir = $this->appMeta->appDir;
$dbConfig = 'sqlite:' . $appDir . '/var/db/todo.sqlite3';
$this->install(new AuraSqlModule($dbConfig));
$this->install(new AuraInputModule());
$this->install(new FormModule());

// 各種Pageリソースがhtml-appコンテキスト上で依存する束縛に、appコンテキスト上ではフェイクを設定するモジュール
$this->install(new AppFakeBinderModule());

// 公式チュートリアルのDIセクションで実装するやつ
$this->bind(LoggerInterface::class)->to(DebugLogger::class);
}
}
18 changes: 18 additions & 0 deletions src/Module/FormModule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Cw\LearnBear\Module;

use Cw\LearnBear\Infrastructure\Form\TodoForm;
use Ray\Di\AbstractModule;
use Ray\WebFormModule\FormInterface;

class FormModule extends AbstractModule
{
protected function configure(): void
{
$this->bind(TodoForm::class);
$this->bind(FormInterface::class)->annotatedWith('todo_form')->to(TodoForm::class);
}
}
7 changes: 1 addition & 6 deletions src/Module/HtmlModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,16 @@

namespace Cw\LearnBear\Module;

use Cw\LearnBear\Infrastructure\Form\TodoForm;
use Madapaja\TwigModule\TwigErrorPageModule;
use Madapaja\TwigModule\TwigModule;
use Ray\Di\AbstractModule;
use Ray\WebFormModule\AuraInputModule;
use Ray\WebFormModule\FormInterface;

class HtmlModule extends AbstractModule
{
protected function configure(): void
{
$this->install(new TwigModule());
$this->install(new TwigErrorPageModule());
$this->install(new AuraInputModule());
$this->bind(TodoForm::class);
$this->bind(FormInterface::class)->annotatedWith('todo_form')->to(TodoForm::class);
$this->override(new CwAuthModule());
}
}
Loading

0 comments on commit 810720c

Please sign in to comment.