ytake blog

Web Application Developer

Laravel JP Conference 2019開催について

Laravel JP Conference2019

f:id:ytakezawa:20181008205256p:plain

Twitterなどでももう知っている方も多いと思いますが、

2019/02/16 にLaravel JP Conference2019を開催します。

今回自分が実行委員長を努めさせていただきます。

概要について

概要についてはこちらをご覧ください

Laravelを軸としたカンファレンスになりますが、基本的にはPHP関連を扱うカンファレンスと同等の内容でお届けします。
当日は3会場あり、それぞれの会場は以下のカテゴリで分けて同時に開催されます。

  • Laravelメインのセッション
  • PHP関連システムに関連するセッション・
  • その他企画・実装相談会・ワークショップなど

スポンサーについて

ブロンズとドリンクスポンサー以外は売り切れとなっていますが、
まだ枠はございます。
他にもこういう協力ができるかも、という企業様などがありましたら竹澤までご連絡いただけると幸いです。

twitter.com

Twitterについて

公式のアカウントは以下のアカウントです。

twitter.com

スピーカー募集やチケット販売などは公式アカウントからアナウンスさせていただきますので、
ぜひフォローしてください!

スピーカー募集について

募集開始については上記のアカウントからアナウンスとなりますが、
以下の内容でトークできるぜ!という方は是非お願いします!

  • Laravelのノウハウ・Laravelを実際にこうして使っているよ (商用投入系が望ましい)
  • PHPアプリケーション設計技法について (Laravel問わず)
  • アプリケーションを支えるデータベース設計・ミドルウェア周りについて
  • ワークショップをやりたい

スピーカー応募は10月中旬頃から開始予定です。

チケット販売について

チケットについてはカンファレンス本編のチケット・懇親会チケットと分けて販売されます。
こちらも今月中旬以降に開始予定です。

スタッフ募集について

当日スタッフなどの募集は後日アナウンスされます。
コアスタッフについては現在募集枠として用意している訳ではありませんが、
是非やりたい!という方はご連絡ください!

現時点では一度限りのカンファレンスとしており、2020年は開催する予定はありません。

「PHPフレームワーク Laravel Webアプリケーション開発」執筆しました

新しいLaravel本

今回下記の書籍を執筆しました

PHPフレームワーク Laravel Webアプリケーション開発

Laravelリファレンス の延長という訳ではなく、
機能解説ではなく、実際に開発を行う上でヒントになるような内容になっていると思います。
Laravelに慣れた次のステップに進められるような内容だったり、
バージョン問わず導入できる内容を心がけました。

今回担当したのは、3章、4章後半、6章、7章と10章です。

全体を通してドメイン駆動設計などは盛り込まず、責務分割や、API設計などの視点がいくつか入っています。
いくつかとりあげてみると・・・

3章

幅を広げすぎないようにMVCとADRのみに焦点を絞って、簡単な解説などがあります。
どちらがいい、という話ではなく開発チームの体制や、長期運用するかどうかなどで大きく変わります。
この先の知識や学習するにはどうしたらいいんだろう、
公式ドキュメントだけの使い方だけではない選択肢の一つとして読んでみると楽しめると思います。

4章後半

レスポンスとミドルウェアが主ですが、
少し前に取り上げられることも多かったSSE(ストリームを使ったレスポンス)だったり、
APIの REST Level 3 / HALの導入方法だったり、
これからのアプリケーション開発に導入することができる内容になっていると思います。

7章

EventとQueueの基本に触れながら、データ分析処理などでも用いることが多いCQRSライクな方法に発展します。
大きなアプリケーションではメッセージングをApache KafkaやAmazon SQS、Kinesis Data Firehose、
RabbitMQなどに差し替えてみてください。

個人的なオススメは後半のテスト関連の9章と11章です!

本書は必ずしも初めてLaravelを使う、という方には難しい内容かもしれませんが、
多くの内容はLaravelに限らず導入できる内容になっています。

アプリケーション開発のヒントとして役立てていただければ幸いです。

blog.shin1x1.com

おまけ
f:id:ytakezawa:20180930032902j:plain
厚さに注目!

PHPカンファレンス福岡2018で「Event Sourcing, CQRS For PHP」の話をしました

先日開催されたPHPカンファレンス福岡2018

f:id:ytakezawa:20180704013557j:plain

いいカンファレンスを開催しよう!という福岡の皆さんの気持ちがとても良いカンファレンスで、
今年も登壇しつつ、いろんなセッションに参加したり、 いろんな方と技術なトークができて最高でした。

phpcon.fukuoka.jp

トーク内容

今年はタイトルにもある通り、「Event Sourcing, CQRS For PHPという話をしました。
DDDと一緒に紹介されたりすることも多いものですが、
データ処理方面からの話が主の内容でお届けしました。

ビックデータの処理、といえばphp以外の言語で処理することがほとんどですが、
こういったミドルウェアアーキテクチャを組み合わせることで、 目の前にある課題をシンプルに解決していくためのヒントになることも多く、
データ周りが複雑で、どうやったらいいんだろう、という方にも参考になれば幸いです。

この辺りのテーマは一昨年、去年、今年としばらくデータ処理系の開発や、 アーキテクチャを考えることが多く、
そこで実際に導入した方法や苦労したことなどをphpを通して話をしてるシリーズの一つです。

ytake.hateblo.jp

デブサミ2018 [Apache Kafkaによるスケーラブルアプリケーション開発] で登壇してきました #devsumi

トーク終了後、Ask the Speakerでもたくさんの方が質問してきてくれたり、
ミドルウェア周りの悩みをみんなで話したり、今回この話を選択してよかったなぁと思いました。

Twitterなどでも色々反応いただけてありがとうございました。

参加したセッション

skaffold を使って Kubernetes してみたLaravel Queue でスケーラブルなバッチシステムの構築

PHPerのためのよくわかるCPU脆弱性解説DDoS攻撃との終わりなき戦い

PofEAAで読み解くDoctrine2AI(人工知能)に聞いてみた! - クラウドサービスではじめる動画解析/音声変換 -

そして最後の 物理層のこと、時々でいいから、思い出してください。

でした。

DDoS攻撃物理層の話が特に印象に残りました。
また来年のカンファレンスも楽しみにしています!

カンファレンス前後

カンファレンス前後はやっぱりいつものところでphper大集結でした

f:id:ytakezawa:20180704013547j:plain

ラーメン食べたり

f:id:ytakezawa:20180704013601j:plain

懇親会では良いビールがあったり

f:id:ytakezawa:20180704013639j:plain

今回は会社のステッカーを持って行きました

f:id:ytakezawa:20180704013611j:plain

最高!

そして Laravel JPカンファレンス

さらっとここに記載しますが、
来年Laravel JPカンファレンスを開催します。
東京で開催しますが、2トラック進行とLaravel相談会を用意する予定です。
Laravelづくしのトラックと、アプリケーション開発・設計におけるトラックを考えていますが、
まだわかりません。

こちらはこちらでお楽しみに・・!

HHVM/Hack マイクロフレームワークにCache追加

ytake.hateblo.jp

以前から作ってたフレームワークで、
都度Cache組み込むのが面倒なのと、HackでPSRに準拠する必要もないだろうということで、
JavaEhcacheっぽい名前(名前だけ) のものを作り、
マイクロフレームワークに組み込みました。

github.com

HHVM/Hackにはmemcachedとredisのエクステンションが含まれているので、
なにもしなくても使えるのが楽で良いです。

後日 Mcrouter 対応ドライバも追加してリリースする予定です。

簡単な使い方

使いたいキャッシュドライバーをCacheManagerで指定すると、
そのキャッシュドライバが利用できるようにしています。

が、ドライバー単体でも利用できます

<?hh
use Nazg\HCache\Element;
use Nazg\HCache\CacheManager;

$manager = new CacheManager();
$cache = $manager->createCache('memcached');
$mc = new \Memcached('mc');
$mc->addServers([['127.0.0.1', 11211]]);
$cache->setMemcached($mc);
$cache->createCache('map')?->save('cache', new Element('testing', 0));

独自ドライバ追加方法

Nazg\HCache\CacheProvider クラスを継承していればなんでも使えます
ドライバ実装は下記の通りです。

<?hh // strict

use Nazg\HCache\CacheProvider;

class NullCache extends CacheProvider {
  <<__Override>>
  public function fetch(string $id): mixed {
    return;
  }
  <<__Override>>
  public function contains(string $id): bool {
    return false;
  }
  <<__Override>>
  public function save(string $id, Element $element): bool {
    return true;
  }
  <<__Override>>
  public function delete(string $id): bool {
    return true;
  }
  <<__Override>>
  public function flushAll(): bool {
    return true;
  }
}

上記の登録後は、以下の通りに記述するだけで利用できます。
簡単

<?hh

use Nazg\HCache\Element;
use Nazg\HCache\CacheManager;

$manager = new CacheManager();
$manager->addCache('null', () ==> new NullCache());
$manager->createCache('null'); // return NullCache

Hackで動的メソッドコール実装するヒント

Hackでは基本的に動的メソッドはtype checkerでエラーになりますが、
type checkerが理解できるように記述すればOKです。

CacheManagerは下記の実装になっています

<?hh

  protected Map<string, classname<CacheProvider>> $cache = Map {
    'apc' => \Nazg\HCache\Driver\ApcCache::class,
    'void' => \Nazg\HCache\Driver\VoidCache::class,
    'map' => \Nazg\HCache\Driver\MapCache::class,
    'file' => \Nazg\HCache\Driver\FileSystemCache::class,
    'memcached' => \Nazg\HCache\Driver\MemcachedCache::class,
    'redis' => \Nazg\HCache\Driver\RedisCache::class,
  };

  protected Map<string, (function():CacheProvider)> $userCache = Map{};

  public function createCache(string $namedCache): ?CacheProvider {
    if($this->cache->contains($namedCache)) {
      $cache = $this->cache->at($namedCache);
      return new $cache();
    }
    if($this->userCache->contains($namedCache)) {
      $cache = $this->userCache->at($namedCache);
      return $cache();
    }
    return null;
  }

CacheProviderでは実は下記のようになっており、継承に制約をつけています。

<?hh // strict

namespace Nazg\HCache;

use Nazg\HCache\{Cacheable, FlushableCache};

<<__ConsistentConstruct>>
abstract class CacheProvider implements Cacheable, FlushableCache {

}

コンストラクタに何かを付け加えることはできませんので、
インスタンス生成方法が統一されていますので、type checkerが 通ってよし と判断しています。

フレームワークへの組み込み

Containerにインスタンス生成方法等を登録します。

設定値はクラスでは関与せず、外から与えるものとしてshapeで縛っています。

<?hh

abstract class CacheServiceModule extends ServiceModule {
  protected Driver $defaultDriver = Driver::File;
  <<__Override>>
  public function provide(FactoryContainer $container): void {
    $container->set(
      CacheManager::class,
      $container ==> new CacheManager(),
      \Ytake\HHContainer\Scope::SINGLETON,
    );
    $container->set(
      CacheProvider::class,
      $container ==> {
        $manager = $container->get(CacheManager::class);
        if($manager instanceof CacheManager) {
          return $this->detectCacheProvider(
            $manager->createCache($this->defaultDriver),
            $this->cacheConfigure($container)
          );
        }
        throw new \RuntimeException("Failed to resolve " . CacheProvider::class);
      },
      \Ytake\HHContainer\Scope::SINGLETON,
    );
  }

  abstract protected function cacheConfigure(
    FactoryContainer $container
  ): CacheConfiguration;

  protected function detectCacheProvider(
    ?CacheProvider $provider, 
    CacheConfiguration $cacheConfigure
  ): CacheProvider {
    invariant($provider instanceof CacheProvider, "provider type error");
    if($this->defaultDriver === Driver::File) {
      if($provider instanceof FileSystemCache) {
        $dir = $cacheConfigure->getFileSystemDir();
        if(!is_null($dir)) {
          $provider->setDirectory($dir);
        }
      }
    }
    if($this->defaultDriver === Driver::Memcached) {
      if($provider instanceof MemcachedCache) {
        $m = $cacheConfigure->getMemcached();
        if(!is_null($m)) {
          $provider->setMemcached($m);
        }
      }
    }
    if($this->defaultDriver === Driver::Redis) {
      if($provider instanceof RedisCache) {
        $r = $cacheConfigure->getRedis();
        if(!is_null($r)) {
          $provider->setRedis($r);
        }
      }
    }
    return $provider;
  }
}

このクラスを継承し、利用者がどこから設定値を持ってくるかを指定すれば適切に起動します。
設定値のshapeは下記の通りです。

<?hh // strict

namespace Nazg\Cache;

type MemcachedServer = shape(
  'host' => string,
  'port' => int,
  ?'weight' => int,
);
type FileSystemConfig = shape(
  'cacheStoreDir' => string
);
type MemcachedConfig = shape(
  'servers' => ImmVector<MemcachedServer>,
  ?'persistentId' => string,
);
type RedisConfig = shape(
  'host' => string,
  ?'port' => int,
  ?'password' => string,
  ?'prefix' => string,
  ?'readTimeout' => float,
  ?'persistent' => bool,
  ?'database' => int
);

Skeletonへの組み込み

フレームワークのSkeletonを使えばこの辺りはデフォルトで対応しています。

github.com

設定値は下記で用意しています。(cache.global.php)

<?hh

use Nazg\Cache\Driver;
return [
  \Nazg\Foundation\Service::CACHE => [
    /*
     * Supported "apc", "map", "file", "memcached", "redis", "void"
     * Driver::Apc, Driver::Map, Driver::File, Driver::Memcached, Driver::Redis, Driver::void
     */
    'driver' => Driver::Memcached,
    'drivers' => [
      Driver::File => shape(
        'cacheStoreDir' => __DIR__ . '/../storages/cache/',
      ),
      Driver::Memcached => shape(
        'servers' => ImmVector{
          shape(
            'host' => '127.0.0.1',
            'port' => 11211,
            'weight' => 100,
          ),
        },
        // 'persistentId' => 'rename',
      ),
      Driver::Redis => shape(
        'host' => '127.0.0.1',
        'port' => 6379,
        'database' => 0,
        // 'password' => '', // optional
        // 'prefix' => '', // optional
        // 'readTimeout' => 1, // optional
        // 'persistent' => false, // optional
      ),
    ],
  ],
];

PHPerKaigi2018 Hackについて、と Laravel相談会司会担当で参加しました

*先日開催されたデブサミ2018での発表 [Apache Kafkaによるスケーラブルアプリケーション開発] については、
会社のブログに記載していますので、そちらを参照ください。

PHPerKaigi 2018は最高だった

お世辞抜きで良いイベントでした。
数週間前にデブサミという大きなイベントで登壇していたのもありますが、
規模感がすごくよかった。

それに各登壇者の方々(自分は除き)が、日本のPHPの第一人者といってもおかしくない方々と、
PHPと関わりのあるデータベース界隈から最高な方々が集まり、
参加した全ての方にいい刺激になったイベントだったのでは、と思います。

タイムテーブルの関係でTrack Aのほとんどのセッションに参加できませんでしたが、
ベストトーク賞に選ばれた後藤さんの内容は忘れないように気をつけていきたいと思います。

speakerdeck.com

Laravel相談会司会担当

phperkaigi.jp

3/10の朝一からLaravel相談会の司会を担当していました。

最初は数人から始まったのもあり、
純粋な質問会だったり、実装相談みたいなところから始まりましたが、
参加された方同士で意見交換が行われたり、
実際に使っている情報だったりが出てきて、相談のテーマが広がっていったり
まさにKaigi的な発展をしていったのがすごい楽しかった。

参加者の方々同士のコミュニケーションに発展していったり、
よくあるカンファレンスの一方向のトークではなく、
まさにsocket.ioみたいな双方向通信のコミュニケーションが生まれていくのは、
イベントとしてもかなりいい体験の提供ができる可能性があると思いました。

海外のカンファレンスで同様のものがあって、とりあえずやってみた、という長谷川さん!
やらせていただいてありがとうございました。

他のイベントでも是非またやりたい・・・!

自分は登壇よりもこういった相談会的なものの方が、
いろんな方の話が聞けるし、自分の糧にもなるので好みです。
*自社でもそういうことをしたりしているのもありますが

Hackについて話した

内容は以前にブログに書いたことの裏側です。

ytake.hateblo.jp

ytake.hateblo.jp

Hack自体は結構前から好きで使っていて、
実際に導入した時に今後使い続ける時に、
分離が進む中でPHPフレームワークに左右されるのも嫌だなーという思いから、
Zend Expressiveの挙動が好きだったのと、
LaravelのServiceProviderライクにシンプルに依存定義したい、
というのを混ぜてHTTP Request/ResponseとMiddleware以外の概念を取っ払ったものをHack専用に作り、
興味がある人のきっかけになればいいな、
という想いから今回そのテーマで話すことにしました。

Hackを気軽に始めるには、
数年前のHHVMとPHPの関係性の知識だけでは古くなっているのもあり、
最近の事情だったり、ライブラリを混ぜて紹介しながら、
実際の例も混ぜてみました。
strictにしなければ、結構簡単だったりしますので、
興味持った方は是非チャレンジしてみてください。

現在のPHP界隈で言われる型を意識した実装や、
テーマに興味がある方は結構マッチすると思っており、
逆にゆるふわなところが好みであれば戸惑うことも多いと思います。

Q&A

発表後にいくつかQ&Aがありました。
(いつも自分の発表にQ&Aがあまりないので嬉しかった・・)

バックボーンなどについて

PHPとHackは似ています、がやはり解決したいものが違うため、
JavaScalaのような関係だと自分自身は思っています。

どういう時にHackを選択するのか

I/Oが発生する通信や、MySQLへの問い合わせなど、
今回のトークにはありませんがAsync Awaitがあったりしますので、
そういった部分や、
PHPよりももっと堅実なアプリケーション開発を行う場合や、
Hackではコード内にHTMLを含ませることができなかったり(XHPで)、
XHP自体も脆弱性が生まれないように考えられていたりしますので、
そういったものがマッチすれば十分に選択肢となるのではないでしょうか?

実際に開発で利用するとわかると思いますが、
VectorなどのCollectionは使い勝手もよく堅くなりますので、
コード量が減る、というのもあります。
不確実な型の判定がなくなりますので、これだけでも十分だと思います。

初心者に勧められるかどうか

人によっては勧められる、といえるかもしれませんが、
自分自身 突然Rustを勧めるようなものだと思っており、
型が厳格なためチームでのアプリケーション開発はかなり良いと思っていますが、
教育コストと、導入者自身が理解していないと難しいと思いますので、
必ずしもYesとは言い難く、
HHVM/Hackにはたくさんの概念があり、まずはPHPを触れてみて、というのが良いかなと思います。

ちなみに実はそこまでパワープレイしていません!!!(笑)

当日の資料はこちら

speakerdeck.com

残念・・・!

健康について

去年ヘルニアで入院+しばらく休んでいたのでその体験を元に10分喋りました。

気をつけましょう!

当日の様子はこちらをどうぞ

togetter.com

今年のPHPイベントは始まったばかり!
次のイベントも楽しみですね!
いくぞ!福岡!(いきます

HHVM/Hack Nazgフレームワーク Validationの巻

HHVM/Hack向けに作ったオレオレマイクロフレームワークにおける
HTTPリクエストのバリデーション実装方法を紹介したいと思います!
Hackならではの機能を使ってバリデーションの仕組みを用意しています。

ytake.hateblo.jp

残念ながらLaravelのような細かいバリデーションルール指定方法などは用意していません
アプリケーションに合わせてひたすら実装するべし!
HackではPHPのような動的なメソッドコールはstrictで利用するとtypecheckerに怒られます
回避方法はありますが、実装していくとstrictにしたくなるものです・・

それはさておき

このフレームワークでは、バリデーションはこうしてください、というルールは特に持っていませんが、
専用に用意したAttribute を記述することで
Laravelのフォームリクエスト のような挙動で、
バリデーションを実行することができます。
*Annotationだと思ってください

Validation対象のActionクラス

routeを '/contents/{content}' として、このエンドポイントにアクセスした時にバリデーションを実行するようにします。
下記のようなクラスを作成します

ここに記述されているZend\Diactoros\Response\TextResponseクラスを利用してレスポンスを返却していますが、 Psr\Http\Message\ResponseInterface を実装していればなんでも構いません。

<?hh // strict

namespace App\Action\Document;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\TextResponse;

final class ReadAction implements MiddlewareInterface {

  public function process(
    ServerRequestInterface $request,
    RequestHandlerInterface $handler,
  ): ResponseInterface {
    return new TextResponse('Hello world!');
  }
}

route登録は、 config/routes.global.php ファイルに記述します。

<?hh 

return [
  \Nazg\Foundation\Service::ROUTES => ImmMap {
    \Nazg\Http\HttpMethod::GET => ImmMap {
      '/contents/{content}' => ImmVector {
        App\Action\Document\ReadAction::class
      },
    },
  },
];

忘れずにServiceModuleクラスにインスタンス生成方法を記述しましょう。

<?hh // strict

namespace App\Module;

use App\Action;
use Ytake\HHContainer\Scope;
use Ytake\HHContainer\ServiceModule;
use Ytake\HHContainer\FactoryContainer;

final class ActionServiceModule extends ServiceModule {
  <<__Override>>
  public function provide(FactoryContainer $container): void {
    $container->set(
      Action\Document\ReadAction::class,
      $container ==> new Action\Document\ReadAction(),
      Scope::PROTOTYPE,
    );
  }
}

これでrouteの準備ができました。

http://お好きなdomain/contents/aaaaa などでアクセスできます。

Validationクラスを作る

ここではstringの値が送られているかどうか、というバリデーションを例にしますが、
せっかくなのでHackのshapeを利用して型チェックバリデーションとして実装します。

type-assert install

まずは hhvm/type-assert をインストールします。

github.com

composer require hhvm/type-assert

PHPもインストールされている環境で、
上記コマンドでうまくインストールできない方は下記のようにするといいかもしれません

$ hhvm -d xdebug.enable=0 -d hhvm.jit=0 -d hhvm.hack.lang.auto_typecheck=0 $(which composer) require hhvm/type-assert

バリデーションクラスを、 App\Validation\ContentRequestValidator クラスとして作成します。
Nazg\Foundation\Validation\Validator クラスを継承して実装します。

<?hh // strict

namespace App\Validation;

use Facebook\TypeAssert;
use Nazg\Foundation\Validation\Validator;
use Psr\Http\Message\ServerRequestInterface;

final class ContentRequestValidator extends Validator {
  
  const type ContentRequestShape = shape(
    'content' => string,
  );
  
  protected bool $shouldThrowException = true;
  
  protected Vector<string> $errors = Vector{};

  <<__Override>>
  protected function assertStructure(): void {
    try {
      TypeAssert\matches_type_structure(
        type_structure(self::class, 'ContentRequestShape'),
        $this->request?->getAttributes(),
      );
    } catch (TypeAssert\IncorrectTypeException $e) {
      $this->errors->add("type error");
    }
  }

  protected function assertValidateResult(): Vector<string> {
    return $this->errors;
  }
}

ContentRequestShape

ContentRequestShapeは、リクエストで受け取る値をshapeを使って型を記述しています。
shapeはGoのstructのようなものだと思っておくと理解しやすいかもしれません

shouldThrowException property

フレームワークで、バリデーションエラー時はExceptionを投げないようになっています。
trueにすることでExceptionHandlerクラスで自由にレスポンスを操作することができます。
Laravel/LumenのExceptionHandlerクラスの使い方とほぼ同じです。

assertStructure、assertValidateResultメソッド

フレームワークで用意しているバリデーションで、
型チェックと値自体のバリデーションの両方を実装することができるようになっています。

型チェックが先に実行され、assertValidateResultがバリデーション実行後の結果を返却します。
細かいバリデーションは、クラス内にそれぞれのバリデーションを行いたいメソッドを記述し、
assertValidateResultでそれらを実行し、結果を Vector<string> に詰める、という具合です。

<?hh 
    try {
      TypeAssert\matches_type_structure(
        type_structure(self::class, 'ContentRequestShape'),
        $this->request?->getAttributes(),
      );
    } catch (TypeAssert\IncorrectTypeException $e) {
      $this->errors->add("type error");
    }

Facebook\TypeAssert\matches_type_structure でHTTPリクエストの値が期待している型かどうかをチェックし、
期待していない型の場合は、 Facebook\TypeAssert\IncorrectTypeException がスローされるため、
Vectorに失敗したことを示す文字列を追加しています。

実装後このバリデーションクラスをServiceModuleクラスで登録します。

<?hh // strict

namespace App\Module;

use App\Validation;
use Ytake\HHContainer\Scope;
use Ytake\HHContainer\ServiceModule;
use Ytake\HHContainer\FactoryContainer;

final class ValidationServiceModule extends ServiceModule {
  <<__Override>>
  public function provide(FactoryContainer $container): void {
    $container->set(
      Validation\ContentRequestValidator::class,
      $container ==> new Validation\ContentRequestValidator(),
      Scope::SINGLETON,
    );
  }
}

ServiceModuleクラスはなんでも構いませんが、新たに作った場合はかならず config/modules.global.php に記述してください。

<?hh

return [
  \Nazg\Foundation\Service::MODULES => ImmVector {
    \App\Module\ActionServiceModule::class,
    \App\Module\ExceptionServiceModule::class,
    \App\Module\MiddlewareServiceModule::class,
    \App\Module\LoggerServiceModule::class,
    \App\Module\ValidationServiceModule::class,
  },
];

これでバリデーションの準備が整いました。

バリデーション実行

バリデーションを実行したいクラスのメソッドに Attribute を記述します。

先ほど作成した App\Action\Document\ReadAction クラスで実行するようにするには次の通りです。

<?hh

namespace App\Action\Document;

use App\Validation\ContentRequestValidator;
use App\Responder\IndexResponder;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\TextResponse;

final class ReadAction implements MiddlewareInterface {

  <<RequestValidation(ContentRequestValidator::class)>>
  public function process(
    ServerRequestInterface $request,
    RequestHandlerInterface $handler,
  ): ResponseInterface {
    return new TextResponse('Hello world!');
  }
}

<<RequestValidation(ContentRequestValidator::class)>> この部分がバリデーション指示になります。
指定方法は <<RequestValidation(実行したいバリデーションクラス)>> となります。

最後にExceptionHandlerクラスで任意のレスポンスを返却するように記述すればOKです。
フレームワークのskeletonに継承した App\Exception\AppExceptionHandler クラスが含まれていますので、
そのクラスを利用します。

<?hh

namespace App\Exception;

use Nazg\Http\StatusCode;
use Nazg\Foundation\Validation\ValidationException;
use Nazg\Types\ExceptionImmMap;
use Nazg\Foundation\Exception\ExceptionHandler;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Response\JsonResponse;

class AppExceptionHandler extends ExceptionHandler {
  <<__Override>>
  protected function render(
    ExceptionImmMap $em,
    \Exception $e
  ): ResponseInterface {
    $message = $em->toArray();
    if($e instanceof ValidationException) {
      $message = $e->errors();
    }
    return new JsonResponse(
      $message,
      StatusCode::StatusInternalServerError,
    );
  }
}

Nazg\Foundation\Validation\ValidationException クラスがスローされた場合に返却されるレスポンスを変更しました。

誤った型が送信されるとjson[type error!] と返却されます。
バリデーションの実装は以上になりますが、
実は hack-routerで受け取ったリクエストの値は全てstringになるため、通常はこのバリデーションは絶対にエラーになりませんが、
アプリケーションで利用するものと異なるメソッドなどを指定した場合に発生しますので、
実装時に簡単なエラーなどを見つけることができるようになりますので、
色々チャレンジしてみてください。

以上、簡単なようでちょっと面倒臭いバリデーションの実装方法でした。

デブサミ2018でApache Kafka、PHPerKaigi 2018でHackの話をします

デブサミ2018!

開発者にはおなじみのデブサミですが、
今年は弊社(アイスタイル)もスポンサーとして参加し、急遽わたくしも登壇することになりました。
(先週決まりました・・・)

event.shoeisha.jp

event.shoeisha.jp

ゴールドスポンサー!

今年は登壇だけではなく、スポンサーなどの面でも積極的に参加できればと思っています。

肝心のトーク内容ですが、

Apache Kafkaによるスケーラブルアプリケーション開発 という、
去年のPHPカンファレンスと同タイトルですが、
今回はPHPの枠を取っ払い、Kafka ConnectやKafka Stremasを利用して複雑化した問題や、
データ処理といったところに軸を変えてお話しする予定です。
実際のScalaのコードなども混ぜて実例の紹介なども考えています。

http://event.shoeisha.jp/devsumi/20180215/session/1683/

興味がある方は是非ご参加ください!

つづいてこちら

PHPerKaigi 2018

今年初めて開催されるPHPerKaigi2018

phperkaigi.jp

iOSDCでお馴染みの長谷川さん(@tomzoh) 主催のカンファレンスです。
美味しいビールとPHPの話、まるで海外のカンファレンス様なイベントで、
開催が決まった時から楽しみにしていたイベントの一つですが、
今回そんな初回で Hack の話をさせていただくことになりました。

Hackに関連するトークは、実は2年くらい前からいくつかのカンファレンスに応募してましたが、
毎回違うトークが採用されていましたが、今回ついに・・・!

トークのタイトルは、 Hackで作るマイクロフレームワーク です。

phperkaigi.jp

今年の頭に公開したHack向けマイクロフレームワークが題材ではありますが、
フレームワークの紹介、ではなく、
PHPのライブラリやPSRをHackにも取り入れて、
普段実装しているPHPアプリケーションとほぼ同じ感覚で開発ができる様にするためのいくつかのノウハウや、
コードレビューなどで発揮する厳格モードとの付き合い方、
Hack向けComposer最適化の方法や、
TypeAssertを使った厳格なバリデーション機能(フレームワークで導入した機能ですが紹介します)、
そしておそらくほとんどの人が思うIDEについて(入力補完の方法など教えますよ!)。

これらを活用してみなさんもHackでライブラリやフレームワーク開発などにチャレンジしていただければと思い、
今回お話させていただきます! ちなみに弊社はプロダクション環境でHack製アプリケーションが稼働しています。

同日 LTで弊社のエンジニアもHackの話で登場します。

そして最後に、

Laravel Meetup Tokyo Vol.10

久しぶりに開催します。
PHPerKaigi2018の前日に開催です!!!
前回からまた半年ほど時間が空いてしまいましたが5.5がリリースされたこともあり、
利用者がかなり増えたこともあり、ユーザー同士の交流の場を設けようと思い、
開催となりました。

laravel-meetup-tokyo.connpass.com

定期的に開催しようと毎回思ってはいるものの、なかなか時間がなかったり。。。
そして主催者が最近Laravel/Lumenを選択することも減っているという

どれもきっと楽しいイベントになると思いますので、
是非お越しください!!!!

ここ一年ほどわたくしのアプリケーション開発は
GoのGoaEcho
PHPはほぼ Zend Expressive
それ以外はHack, ScalaでKafka, Sparkばかりになっ・・・