2016年3月16日水曜日

Qiitaに投稿「PHPの【クロージャー、クラスのnew、クローン】の比較」

Qiitaに投稿しました。

PHPの【クロージャー、クラスのnew、クローン】の比較

最初はクロージャーとクラスのnewでの生成の速度比較だったのですが、クローンを足して、つい関数呼び出しも比較したのが問題だった。

指摘されてる通り、関数呼び出しは実行時間であり、その他は生成についての速度なので、比較できない。

にもかかわらず、関数が遅かったので、関数が遅い、と書いてしまった…
しかもタイトルに。

速度測定は難しい、ということがよく分かりました。
(今は修正済み)

クラスのnewについて


さらに言えば、クラスをインスタントする場合で一番時間がかかるのは、コンポーザーのオートローディング。もっと言えばfile_existsでのファイル存在チェック。

同じクラスを何度もnewするなら同じ速度ですが、実際としてはクロージャーを使うほうが速い場合は多いはず。


2016年1月16日土曜日

Qiita:PSR-7のミドルウェアは、何故ああなのか?」

Qiitaに投稿しました。


PSR-7でよく見るミドルウェアの引数(シグネチャ)に関する話です。どうして、あの形が使われるのか考えてみました。

なんというか、LTネタ的な話です。

2016年1月7日木曜日

2015年を振り返って2(Tuum/Respond開発日誌メモ)

2015年を振り返って、の続きです。

昨年の2015年6月から始めたのが「Tuum/Respond」というプロジェクト。開発方針を転換して、PSR-7ベースのマイクロフレームワークに後付でViewの機能を追加するパッケージを目指しました。

面白かったのは、開発してゆくにつれ、どんどんコードが簡単になってゆきました。機能を絞ったことで、何をしているか理解できたからと思います。

いったい何をするのか。
自分で理解した形で説明します。
使い方とかは、Githubのページを見てください。

要するに次の2つを管理するパッケージです。

  • ViewData: ビューを作るのに必要な情報を運ぶデータ転送オブジェクト。これに必要な情報を設定してゆく。
  • ViewInterface: ViewDataからテンプレートなどでビューを構築して、レスポンスを返すインターフェース。

これだけなのですね。

ViewerInterface


Tuum/Respondの肝は、様々な方法でレスポンスオブジェクトを構築すること。

レスポンスを構築する、ということは、ビューを構築する、とほぼ同じです。ということで、次のインターフェースが出来ました。

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Tuum\Respond\Responder\ViewData;
interface PresenterInterface {
    /**
     * renders $view and returns a new $response.
     *
     * @param ServerRequestInterface $request
     * @param ResponseInterface      $response
     * @param ViewData               $view
     * @return ResponseInterface
     */
    public function render(
ServerRequestInterface $request,
ResponseInterface $response,
$view);
}

リクエストとレスポンスのオブジェクト、それとビュー構築に必要なデータの入ったViewDataというオブジェクトを受け取ります。適宜ボディを構築して、レスポンスオブジェクトを返します。

ついでに同じインターフェースで、ちょっと動きの違うAPIを定義しました。

interface ErrorViewInterface extends PresenterInterface {}
interface ViewerInterface extends PresenterInterface {}

この3つのインターフェースの使い分けは、


  • PresenterInterface:
    $viewを受け取って、レスポンスを返す汎用インターフェース。
  • ViewerInterface:
    テンプレートからビューを描画することで、レスポンスを返す。
  • ErrorViewInterface:
    ステータスコードに対応するテンプレートを描画することで、レスポンスを返す。エラー用。


それぞれのインターフェースを実装したクラスを作っておいて、呼び出します。次のコードを見ると、どれがどのAPIに対応するか、すぐわかると思います。

Response::view($req)->view('template/filename');
Response::error($req)->forbidden();
Response::present($req)->call(MyPresenter::class);


ViewData


もう一つの肝が、ViewDataというデータ転送オブジェクト(DTO)。Tuum/Respondとは、要するにViewDataを設定しつつ持ちまわるためのライブラリです。そして、最後にPresenterInterfaceのオブジェクトを呼び出します。

このコードだと、$viewを直接いじってからテンプレートを描画します。

Respond::view($req)
  ->withViewData(function($view) use($value) {
    return $view->setData('key', $value);
  })->view('my/template');

あるいはRedirectでは、ViewDataをSessionのフラッシュに保存します。

Respond::redirect($req)
  ->withMessage('hello')
  ->to('/my/path');

次のリクエストで、フラッシュからViewDataを読み込むことで、データを簡単に利用することが出来ます。


インターフェース遷移


想定しているマイクロフレームワークとTuum/Respondについて、インターフェースという切り口で考えて見たら面白かったので、書いてみます。

最初に、リクエストとレスポンスから**ミドルウェア**が始まります。`$next`が次のミドルウェアですね。

middleware($req, $res, closure $next);

ミドルウェアの最後にルーターが走り、実行する**コントローラー**を呼び出します。`$args`がルートパターンでの変数です。此処から先が、ユーザーコードに入ってゆく感じです。

controller($req, $res, array $args);

コントローラーでは、ドメインを操作したり、必要なビューを構築してレスポンスを返します。

ビューの構築を手助けするのが、`Tuum/Respond`で、次のようなインターフェースになります。

viewer($req, $res, ViewData $view);

$viewに情報を設定する、テンプレートを描画する、レスポンスを構築する、など様々な処理を行えます。

なんかMiddleware-View-Controller (MVC)みたい。

開発は楽しい


開発していると、動くところまで作るのは楽しいとよく言われます。

その後でも、リファクタリングしたり作りこむのも、理解が深まったり新しい見方が出てきたりして別の楽しさがあります。自分の趣味のパッケージでないと中々出来ないので、これからも続けてゆきたいです。

2015年を振り返って(Tuum開発日誌メモ)

2016年になったので、自分用のメモとして去年のTuumPHP開発を振り返ってみます。

Tuum/Webの開発


思えば、Tuum/Webを作り始めたのは2014年の11月頃。一度ぐらい満足できるフレームワークを自作してみたい、と思い立ってしまい、

  • 良いとされる設計を積極的に使う(PSR-7、ミドルウェア、DIコンテナなど)、
  • そのうえで、自分の欲しい機能を実装する、

という方向で作り始めました。

2015年5月頃には、ほぼ完成したのですが、どうも気にかかる点が出来てしまいました。おそらく、次の二点。

1. 思った以上に複雑になった。
2. 欲しいのはフレームワークではなかった。

なので、基本ボツに。

えぇ〜!
せっかく作ったのに。

複雑すぎる


一つ一つの設計は自分なりに納得して作ってるので、妙な動きはしてはいないのですが。思った以上に動きが複雑になってしまいました。原因を考えたのですが、次の2点になると思います。

  • 良いと思える設計から少しずれた、
  • 自分が欲しい機能について、理解してなかった、

一番最初にミドルウェアの引数をどうするか悩んだのですが、できるだけ簡単な形を選んでしまいました。使う分には簡単そうに見えるのですが、フレームワーク側が複雑になってしまいました。

自分が採用した形が、

$response = $middleware($request);

の形。
これだと、帰ってくる$responseに対して処理を行うことが出来ません。そのため、戻りループ用の別インターフェースを作って、としているうちにコードが「ちょっと」複雑に。ちなみに、Symfonyも似たようなことをしてるので、それほど悪い実装ではないはず。

一方、ほぼ標準になりつつある形が

$response = $middleware($request, $response, $next);

$nextとか面倒だなと思ってました。が、フレームワーク作ってみて、このAPIの良さがわかってきました。

もう一点は、次のTuum/Respondの開発をして分かりました。自分の欲しかった機能を十分に理解できてなかったからです。単体のパッケージを作ることで、機能についてじっくりと考えられたわけです。


欲しいのはフレームワークではない


で、この理由。

勉強のために作ったというのもあるので、そこそこ良いフレームワークができたら、もういいかなという気になった。と言うのはあります。

さらに、PSR-7ベースで、素晴らしいマイクロフレームワークがいくつも出てきてます。Slim3とかExpressive。それにRelayもミドルウェアディスパッチャーとしてよく出来てます。

結局、自分が欲しいのは、先に書いた「欲しい機能」の部分であり、フレームワークではありません。すでにあるマイクロフレームワークを利用して、自分の欲しい機能が追加できれば、十分なわけです。

コーディングの勉強には最適


とはいえ、フレームワークを自作するのは、コーディングの練習には最適ですね。やってよかったなと思ってます。

ということで、次の開発話に続きます。

2015年7月6日月曜日

Responder Module:フレームワークを分割して考える

作ってきたTuum/Respondが何なのか、良い説明を思いついたので書いてみます。

フレームワークの機能


最初に、フレームワークの機能を次のように分割してみます。

  1. ブートストラップとアプリ構築。
  2. ルーティングとディスパッチ。
  3. モデル/ドメイン実行。
  4. ビューなどのリスポンス構成。

この中で、最も重要と思うのが(3)のドメイン部分。これこそがプロジェクトの本質で、一番時間を使って開発したい部分。

フルスタック・フレームワークとは、(3)以外のほぼすべての機能を使いやすい形にまとめあげておくことで、ドメインの開発に集中するためのものだと考えてます。

一方、俗にいう「マイクロフレームワーク」というのがあります。これは、(1)と(2)の部分を担っていると考えるとスッキリします。返信するのが簡単なJSONであれば、複雑なビューなどを省くことで、コードが簡潔になります。

Responder Module


こんなふうに分割してみると、この(4)だけをターゲットにしたミニフレームワークみたいなものがあってもいいのでは?

と考えて、この(4)の機能の部分を作ってみたのが「Tuum/Respond」というモジュールです。

ついでに、この部分に「Responder Module」と名前をつけてみました。もう名前があるのかもしれないけれど、見つからなかったので、適当に呼んだだけです。

Tuum/Respond


Tuum/Respondの使い方としては、例えばSlim frameworkのようなマイクロフレームワークと一緒に使うとかを想定してます。

レスポンスの種類


想定しているレスポンスの種類です。

  • View:HTMLなどのコンテンツのあるレスポンス。
  • Redirect:別URIへのリダイレクト。
  • Error:エラー。コンテンツがある場合もある。

それぞれHTTPステータスの200番台、300番台、そして400と500番台に対応します。

どのレスポンスを返す場合でも同じAPIが使えます。更には、リダイレクトした次のビューにデータを渡す場合でも、同じAPIになります。つまり、こんなコード。

$app = new App(); // some micro-framework app. 

// redirects to /jumped.
$app->get('/jumper', function($request, $response) {
    return Respond::redirect($request, $response)
        ->withMessage('bad input!') // <- set up info.
        ->withInputData(['some' => 'value'])
        ->withInputErrors(['some' => 'bad value'])
        ->toPath('/jumped');
    });

// ...and this is jumped.
$app->get('/jumped', function($request, $response) {
    return Respond::view($request, $response)
        ->asView('template'); // with the 'welcome!' message.
});

どこかで見たコードですね。

必要なサービスとインターフェース


Tuum/Respondの全ての機能を使うには、外部サービスが必要です。全てのサービスにはインターフェースが定義されています。

  • ViewStreamInterface:
    ビュー(テンプレート展開)用API。PSR7のStreamInterfaceを継承していて、内部でテンプレートを展開。
  • SessionStorageInterface:
    セッションおよびフラッシュ用API。ほぼAura.Sessionのsegmentと一致してます。
  • ErrorViewInterface:
    エラー表示用API。

これらのサービスを実装して適宜設定することになります。

もう少し細かい説明は前にブログに書いてあるので、そちらも参考にしてみてください。

2015年7月1日水曜日

汎用ページネーション(PSR7も使えます)

汎用ページネーションのパッケージを作ってみた。
一応、PSR7のServerRequestInterfaceも使えます。

セッションを使って、ページ番号やフォーム入力を覚えるのが特徴。簡単に最後と同じページを作成できます。

何故作ったのか?


仕事でDoctrine2を採用してみたのだが、いい感じのページネーションがなかったので作ってみた。ただし仕事には間に合わなかったので、実サイトでの実績はないです。

こういうページネーションに、どのぐらい需要があるのかわからないけど、この際なのでパッケージとして作ってみた。

ライセンス:MIT
PSR準拠 :Psr-1、Psr-2、Psr-4

使い方


インストール


composerで。

$ composer require "wscore/pagination"


Pagerオブジェクト


Pagerオブジェクトを最初に作ります。

use WScore\Pagination\Inputs;
use WScore\Pagination\Pager;

// construction
$pager = new Pager(['_limit' => 15]);

// set up pager using Psr-7 ServerRequestInterface.
$pager = $pager->withRequest($request);
// or from global data. 
$pager = $pager->withQuery($_GET, '/find');

次に、データベースへのクエリなどを実行します。callメソッドにクロージャーを渡してください。引数はInputsというクラスのオブジェクトです。

$inputs = $pager->call(
    function(Inputs $inputs) use($pdo) {
        // query the PDO!
        $found = $pdo->prepare("SELECT * FROM tbl WHERE type=? and num>? OFFSET ? LIMIT ?")
            ->execute([
                $inputs->get('type'),
                $inputs->get('num'),
                $inputs->getOffset(),
                $inputs->getLimit(),
            ])
            ->fetchAll();
        $inputs->setList($found);
    });
$found = $inputs->getList();
$type  = $inputs->get('type');

後は、クロージャー内で必要な処理を書き込みます。
Inputsオブジェクトから、オフセットとリミットや、フォームの入力を読み取れます。

HTML出力


最後に、HTMLへの出力は、Paginateというオブジェクトを使います。

use WScore\Pagination\Html\Paginate;

$inputs->paginate(new Paginate());
echo $inputs->__toString();


すると、こんなページネーションが表示されます。


リクエストの仕方

ここが、このパッケージの肝になります。
「_page」を使いこなします。

1.検索条件フォーム


ページネーションを行う場合は、検索条件をフォームで選ぶ場合が多いと思います。その場合は、普通にフォームを作ってください。

<form>
<input type="text" name="type" />
<input type="integer" name="num" />
<input type="submit" />
</form>

注意:_pageは使わないで下さい。
フォームの内容は自動でセッションに登録されます。セッションは事前に走らせておいて下さい。

2.ページの指定


指定するページを表示するには_pageにページ番号を指定します。

GET /find?_page=2

検索に使う条件はセッションに保持されているので、同じ条件でオフセットを替えてクエリを実行できます。

3.最後の検索画面


検索画面を表示する際、_pageにページ番号をなしでGetしてみてください。

GET /find?_page

すると、セッションから最後のページ番号と検索条件を読みだします。

To Do


動くはずですが、実際に使ったことがないのでAPIなど変わるかもしれません。なのでアルファリリースです。

2015年6月12日金曜日

PSR7用のヘルパーとレスポンダー

コツコツとフレームワークを作っていて。
ふと、自分が欲しかったのは、もっと簡単なことじゃないか?と思い直して作ったのが「Tuum/Http」というパッケージ。

【修正:2015/06/26】
「Tuum/Responder」に名前を変更しました。

そもそものスタートとして、SlimやStackPHPの簡潔な構造に憧れたところから始まっている。が、実際に使うとなると、API作るには便利だけれど、普通のウェブサイトを構築するには作業が面倒そうだなぁと。

面倒だと思った部分は、レスポンスを返す部分。たとえば前のページに戻ったり、ついでにメッセージやエラー情報を付加したりという部分。同じような処理が多い割には、細かな設定が必要な気がする。

そこで、この部分だけをパッケージにしてみた。


何をするパッケージなのか



大きくヘルパーとレスポンダーからできている。
大事なのはレスポンダーの方。

ヘルパー

Psr7に足りなさそうな機能をスタティックなメソッドとして提供している。見れば一発、簡単なものばかり。例えば、

$bool = ResponseHelper::isRedirect($response);

はリダイレクトレスポンスかどうかをチェックできる。RequestHelperとResponseHelperの2つがある。

レスポンダー

レスポンスを構築するためのヘルパー。
例えば、別パスにリダイレクトしたり、その際にメッセージを付加できる(単にセッション・フラッシュに登録してるだけだが)。

Redirect::forge($request, $response)->withMessage('welcome!')->toPath('jump/to');

// ...now in the subsequent request to a server...
Respond::forge($request, $response)->asView('template'); // with the 'welcome!' message.

次のリクエストの際に、前のメッセージが自動でビューに入ってくる。メソッド名がどこかで見たことのあるのは愛嬌。使いやすいと思ったので。

レスポンダーとしては、Respond、Redirect、そしてErrorがある。それぞれ、ビューを返す、リダイレクトを返す、エラーページを返す。

使うのに必要なこと:サービス


レスポンダーの機能を実現するために、ビューやセッションなどのサービスを使っている。これを作っておいて、レスポンダーに渡す必要がある。

セッション用インターフェース

セッションのフラッシュを使ってリクエスト間のデータを受け渡している。そのためのセッションとしてSessionStorageInterfaceという形で定義した。

と行っても、実際はAura.SessionのSegmentと同一のAPI。これを使うのが前提みたいになっている。

use Aura\Session\SessionFactory;

$factory = new SessionFactory();
$session = $factory->newInstance($_COOKIES);
$segment = $session->getSegment('some-name');

ここで作った$segmentがSessionStorageInterfaceと同じオブジェクトになる。

これを$requestに設定する。
use Tuum\Http\RequestHelper;
$request = RequestHelper::withSessionMgr($request, $segment);

あるいは、後で書くようにコンテナに設定しておく方法もある。

ビュー用インターフェース

HTMLを返すにはテンプレートを使ったビューを使いたい。そのために、ビューを展開するViewStreamInterfaceを定義してみた。

テンプレートを展開できるStreamとして扱う。

ViewDataクラス

これは実際のクラス。
ビューやリクエスト間でデータをやりとりするためのデータ転送用オブジェクト。

ビュー用のクラスはViewDataを理解して、実際のレンダラーに渡す必要がある。


コンテナー用インターフェース

サービスを管理するコンテナ。Container-Interopで定義したインターフェースを使った。これに必要なサービスを登録しておいて、$requestに設定する。

次はセッションを設定する方法。

$app = new Container(); // must implement ContainerInterface
$app->set->(SessionStorageInterface::class, $segment);RequestHelper::withApp($request, $app);




2015年5月24日日曜日

PHPの__invokeメソッドの使いドコロは?

PHPにはクロージャー(無名関数)と、クラスを無名関数のように使えるようになるマジックメソッド、__invoke、というのがある。これに関する雑感。

クロージャーと言うのは便利なもので、プログラムをデータとして扱える。

$hello = function($world) {
  return 'hello ' . $
world;
};
便利なケースを思いつかなかったので、適当に書いたコード。


一方で、__invokeメソッドの使いドコロは何だろう。

自分が思いつくのは、
1.流行りのファンクショナルな言語のように書ける。
2.メソッド名を考えなくても使える。
3.クロージャーの代わりにオブジェクトを使いたい場合に必要。

最初の1.と2.は冗談なので、実際は3.の場合かなと思える。つまり何かの関数の引数としてクロージャーを想定している場合でも、オブジェクトが使える。

でも3.の理由は、クラス自体でのメリットというより、必要だからという感じがする。

あとPHPの問題として、意外なところでコードのパースが悪いところがある。せっかくコンパクトに書けるはずのクロージャーなのに、かえってコードが複雑になってしまう。

class Hello {
  private $hello;
  function __construct($hello) {
    $this->hello = $hello;
  }
  function hello($world) {
    return $this->hello($world); // 動かない!
  }
}

なんで、こんなことを書いてるかって?

いや、1.と2.の理由で__invoke使って見たら、意外と面倒で。一方で感じたメリットは3.しかなかったので。それで__invokeメソッドを使うメリットは他に何があるか?そんなことを思ったのでブログに書いてみた。

自作フレームワークでのミドルウェア設計

自作中のウェブアプリケーションフレームワークではミドルウェアベースで動きます。基本は「StackPHPとミドルウェア」に書いたとおりですが、実際のインターフェースに落とすまでの過程についてまとめてみます。

と、Blogspotでコードを書くのが面倒なのを思い出したので、gistに書いた。

フレームワークの文章としては、もっと簡潔に必要な箇所だけにまとめる必要があるだろうな。

2015年4月3日金曜日

ミドルウェアベースでPSR7を使った自作フレームワーク

相変わらずフレームワークを自作している。勉強になるし、万が一、いいものができたら実際に使ってみたいと思いながら作っている。今までは、勉強がメインだったが、今回は本当に使えそうな感じになってきた。

名前は「TuumPHP」。

作り始めたきっかけ

StackPHPとLaravel


2014年の夏頃、Laravel4.2で開発をしていて、これは楽に開発できるフレームワークだなぁと関心した。ただ自分の仕事だとアップデートについて行くのは難しいと感じていた。一旦作ったら、作り変えない程度の機能追加がたまにある程度で、数年は運用する必要がある。まぁなんとかなると思うけど。

そんな時、ミドルウェアとStackPHPについて調べてみて、これは素晴らしいと。単純な構造を組み合わせることで複雑なアプリを構築することができて、PHPの方向はこれだと思った。こんなエントリをQiitaにアップしている

「StackPHPとミドルウェアについて調べてみた」

つまり、

  • StackPHPのような簡潔な構造で、
  • Laravelのように開発しやすいくて、
  • データの流れが自然(ファサードとか使わない)、
  • できればバージョンアップもパッケージ別に対応可、

そんなフレームワークがほしいと思った。

ちょいと探したけれど、いいのが見つからず。じゃ、自分で作ってみるか、というのが始まり。

Psr-7の導入


作ってからしばらくして、標準のHTTPリクエストやレスポンスの規格がもうそろそろ完成するという話が流れてきた。Psr-7という規格。しかも不変オブジェクトが使われている、ときいて早速導入して見た。

その時の経験からQiitaに書いたのが、

「Psr7を使ってみた(というか不変オブジェクトを初めて使った感想)」


TuumPHPの特徴

余り思いつかないので、徒然と書いてみる。

マイクロフレームワーク+ビュー。
つまりSlimやSilexより少し機能が豊富なフレームワーク。モデル側(DB)などは持ってない。

マジックな機能


つまりLaravelから拝借した機能、ということですね。

  • フォームの入力エラーで、フォームにリダイレクトで戻るときに、入力値(withInputs)やバリデーションエラー(withErrors)などを一緒に返せる。
  • フォームに値を表示するときに、リダイレクトの戻り値(withInputs)を簡単に利用できる。
  • APIやメソッド名を参考に。

意外と少なかった。

ミドルウェア


なんでもミドルウェア。

ルーティングもミドルウェアなので、いくつでも追加できる。ルーティングの前のプリ・ルーティングも、ルーティング後のコントローラ内のポスト・ルーティングもできる。

ルーティングの後のディスパッチャーもミドルウェア。これも交換可能。

もちろんコントローラーもミドルウェアの一つ、ので、単純な継承でコントローラークラスを作れる。というか継承する必要は無いはず。

コントローラ内で簡易ルーティングができる。貧者のアノテーション・ルーティングですね。

状態


まだアルファ。
APIも含めて色々と変わる可能性は高い。

独立したコンポーネントを作っていて、こちらはドキュメントとテストはそこそこあるが、フレームワークとしてはドキュメントもテストもほぼ未着手。

その代わり、こんな文章を書いている。フレームワークの使い方ではなくて、どうしてこうなった?の理由を書いたほうが勉強になるかなと思ったから。


今後


すでにZend FrameworkもSlim Framework も次のバージョンでPsr-7でミドルウェアに対応すると表明している。。Laravel 5もすでにミドルウェアベースになっているし、次にはPsr-7を使ってくるのでは無いかと思う。

注目したいのは、今後のPHPで


よく使われるミドルウェアAPI


はどれか、という点。APIが同じなら互換性が高いため、コードやオブジェクトを共通して使える。例えばOAuthとかのクラスを使いまわせるかどうか、というのが大事。

もしフレームワークを自作するなら、APIの目安がついてからのほうがいいと思う。

2014年12月18日木曜日

Software Designの2015年1月号「ソフトウェア開発の未来」

久々に本屋でIT関連の雑誌を買った。
ランチの後に、近くの本屋に入って手にとって見たのが、

Software Designの2015年1月号

タイトルは「Vim使い事始め」だけど、目に止まったのは「ソフトウェア開発の未来」のほう。特にサブタイトル「請負・受託開発は変わるべきか?」が決定的だった。

内容は、渋い。
目新しいことや、びっくりするようなことは書いてない。
知ってた、みたいな話も多い。

でも、こういう普通の話を聞く機会というのは少ないので、タメになる。

そして考えがスッキリした気がする。
モヤモヤと思ってたことを、整頓した感じ。

これからもSIという名前の請負案件はなくならない。日本のIT業界内でのシェアは減ってゆくと思う。が、絶対的な金額としては、特に中小規模は減らないのではないかと思う。

そして自分はお客さんと話をして要件を聞いたりまとめたりするのは好きだ。二次三次の下請けにならない限り、請負仕事は続けてゆくと思う。ただ直接お客さんと話せない状態になったらわからないかな。

今日のポイント。

設計力は大事。
シンプルな技術で長期間のサポート。
そしてお客様の困っていることを解決すること。

2014年11月13日木曜日

フレームワーク自作したい病が発生した

あまりに内容が薄いメモなので、自分のブログで書くことに。下書きみたいなものかと思われる。

StackPHPを参考にフレームワーク自作を始めた。自作すると大変、と言うのは経験済みだが、自作したくなってしまったのは仕方がない。

さて、大事な方向性は。

  1. StackPHPのように「ミドルウェアのスタック」でウェブアプリを構築する。ただし、StackPHPは使わずに自作することに(理由は後述)
  2. できるだけミドルウェア側に処理を寄せること。
  3. ミドルウェアはできるだけ疎結合であること。
  4. 既存のライブラリを有効に利用すること。
  5. ウェブサーバーのフロントに特化。DBなどのインフラ周りは含まない。
  6. ひとまずDIコンテナは利用しない。
書いてみたら真っ当な内容で、特に目新しさが感じられない。でも、調べたところではStackPHPを使ったフルフルなフレームワークが見つからなかった。

StackPHPを使わなかった理由

StackPHPのミドルウェアとして次の3つを満たす必要がある。
  1. SymfonyのHttpKernelInterfaceを実装すること。
  2. コンストラクターの第一引数として次のミドルウェアを受け取ること。
  3. (必要であれば)handleメソッドの中で、次のミドルウェアを呼び出して結果を返すこと。
疑問に感じたのは、ミドルウェアの中に、「実際の処理」と「次の処理を呼び出す」、という2つの責務を持っていること。多すぎるんじゃないか?!

例えば認証とかフィルターを考えてみる。
使い方としては、ミドルウェアとしてスタックの中に放り込んで、全アクセスで必ず認証をさせる。あるいは認証する条件(URLなど)をつける。そしてbeforeなど特別な場所から認証フィルターを掛けてみる。
などなど、いろんな使い方が想定できる。

つまり、次のミドルウェアを呼ばない使い方も多いようにみえる。そうなら、このミドルウェアは処理部分とスタックの部分を分離するほうがよくない?と思った。


で作ったのがStackableというクラス。これにHttpKernelInterfaceを実装した処理オブジェクトをpushしてあげると、次のスタックになる。スタックの考えが単純なので、コード自体は簡単だ。

DIコンテナは?

今後、PHPでもDIコンテナは重要な、いや使って当然な技術になってゆくと思う。が、今はいろんなコンテナが乱立しているし、標準もないし、使ってみると「遅いし」。

なので、ひとまずDIコンテナなしで作ってみることに。ただし依存性の注入は行うことにして、単純なfactory methodを使う方向で作ってます。

ミドルウェア作成してみた感想

実際にミドルウェアを作ってみると、意外と疎結合しないことがわかった。不便なままならいいけれど、やっぱり楽に開発したいと思うとマジックが必要になる。

例えばResponseを返すミドルウェアが複数存在する。一方、ここはフレームワークで統一したい(というか簡単にしたい)と思ったとする。結局、リスポンスオブジェクトを作るファクトリをアプリの中に持っておいて、必要に応じて取り出しては使う、みたいなコードがあちこちに散乱している。

データも共有することが多い。例えばリダイレクトしたら、ちょっとしたデータを一緒にセッションで送りたい。つまりリダイレクト部とセッションでデータを共有する必要がある。後CSRFトークンとかね。


こういう疎結合しながら共有するパターンといえばイベント(Event AggregatorやMediatorパターン)らしい。

しかしながらですよ、ミドルウェアは順番に実行するものですよね。勝手にpushするイベント系は、スタックとは相性が悪いのでは?。なので、「ひとまず」データやオブジェクトをアプリにpush/pullをするようにして、必要に応じて共有することにしてみた。もう少し他のFWの実装を調べてみる予定。


ライブラリについて

今、使っているライブラリ。意外と少なかった。
  • symfony/http-foundation
    スタックの基本なので、絶対外せない。
  • symfony/routing
    ルーティングに使う予定。もっと簡単なのを探してます。
  • league/flysystem
    設定ファイルを管理するためのファイルシステム。これにUnionManagerというのをかぶせることで、環境(productionやlocal)による設定を処理させる予定。
  • league/plates, aura/view
    生PHPベースのテンプレート。どちらを使うかは検討中。
  • wscore/form
    自作のHTMLフォーム生成コンポーネント。遅延評価でFluentにフォームが書けます。


2014年4月21日月曜日

LaravelのEloquent ORMについてQiitaに書いてみた

最近LaravelフレームワークのEloquent ORMを触ってます。まだ始めたばかりですが、2つほどQiitaに気がついたことをまとめてみました。

Eloquentのモデル生成法比較:newとcreate

Eloquentでリレーションを作成する方法


やはりMarkdown記法でかけるのがいいな。
特にソースコードの記述が楽だ。githubのお陰です。

Bloggerでも使えるようになるといいのだけど。

2014年1月17日金曜日

PHPでファイルポインターからファイル名を取得する方法

ちょっと調べたらStackOverflowで見つけた方法。
日本語のページが少なかったので書いてみる。

関数内で、ファイルポインターだけ渡されたのにファイル名が知りたい場合が出てきました。

$fp = fopen( $filename, 'r' );
function get( $fp )
{
   $filename = ... // ここでファイル名が知りたい。
}

引数にファイル名を追加すればいいのですが、面倒だったのでちょっと調べたら、すぐに答えが見つかりました。stream_get_meta_dataという関数。さすがPHPです。関数ほぼ一発で取得できました。

$info = stream_get_meta_data($fp);
$filename = $info["uri"];

だそうです。

2014年1月3日金曜日

2014年の抱負は「勉強」

あまり「勉強」は好きな言葉ではないけど。
そして、もう書いたようなものだけど。

簡単に言えば、
昨年は頑張ってアウトプット(コード)したら、
知識不足を痛感したので、
今年はインプット(勉強)を頑張ろう、
という話です。

■

何を勉強するのか。

まずはOOPの基礎やDDDからかなぁ。
マーティン・ファウラーの、あの本は購入済みなので、それを読もう。

後は、Doctrine2やAuraPHPなど、他のフレームワークを勉強したい。ただ全部入りより、各コンポーネントが充実&使いやすい、というコードを勉強したい。

■

ただ、嫌いなことを頑張るのは難しい。
ひとまず、無理をしない程度がよいはず。

今、一番無理をしているのは目かも。
最近、目が疲れて仕事が続かないことが増えた。

ツイッターで、面白そうなリンクやブログを読むのも良いけれど、なるだけモニターを見つめる時間を減らして、目の使い方を変えたい。ということで、できれば本を読んで勉強できればよいなと考えてます。

2013年12月29日日曜日

Frameworkに依存しないcodeをどう書くか

元々はSymfonyのLTSが3年間というところから。
え〜、そんなに短いんだ。

フレームワーク(FW)よりビジネスのほうが長生きなので、フレームワークに依存しないコードが書きたいな、と。
もっともFWのサポート期間とはなんだろう?期間が過ぎても動くものは動いているし、PHPとかアップグレードしなければ(!)大丈夫なはずだし。脆弱性は最初から入ってないという仮定も必要だけど。
本来、プロジェクトで一番大事な部分はFWというより、それ以外の部分だと思う。それ以外の部分、と言うのはMVCでのモデルというところか。あるいはDDDで言うドメインというところか。

その部分がFWに依存しなければ、何も悩む必要はない、はず。

その部分をどう書くのかな、と最近考え始めた。

■DDDとDCI

モデルやドメインといえばDDD。あるいはDCI。

ただDDDとかDCIなど、どうPHPコードに落とせばいいのだろうか?どう書くと、仕様が分かりやすい、テストしやすいコードになるのか。今のところさっぱり。

一方、今の仕事でDDDが必要になるほど複雑な仕様は無いという話もある。ということで、今後少しずつ勉強する予定。

■FWとのインターフェース

モデル・ドメインを分離して書いても、フレームワークとのインターフェースは必要だ。ここがFW依存になるので、何かを挟む、ということになるのだろうか?

モデルを、ひとまずエンティティと考えれば、エンティティとフレームワークとの接点は、
*Persistence、
*Validation、
*Presentation、
それと、DBやHTMLフォームはテキストベースなので、エンティティとテキストの変換機能も必要かも。これぐらいあれば、簡単なことなら出来そう。

ということで、以上を抽象化したFWモデルを作っておけば、フレームワークが変わってもFWモデルを変えるだけで、ドメイン側には修正は必要ない、とか考えている。

■ちょっと作ってみた

この1年以上PHP5.3で新フレームワークを開発していたら、受注した仕事がPHP5.2ばかり。ということで、PHP4時代から使っているライブラリで開発する羽目になった。

頭にきたので、FWモデルを書いてみた。
モデル内で、PHP4時代(コンストラクターがクラス名とかw)のオブジェクトをnewしまくりながら、ぱっと見は格好良いクラスに見せかける。もうテスタビリティとか無視してざざっと書いてみた。

時間がない中で、わけの分からんことを始めたので、方針はブレるしで、ひどいコードだが、考え方自体は気に入ってます。

ただ、このまま機能を追加してゆき、コードや複雑性が増えてゆき、結局FWモデル自体がフレームワーク化してしまうという未来が見えなくもないですが。

2013年を振り返って

年の瀬なので今年を振り返ってみる。
年内中に書けそうだ。

今年の抱負は「アウトプットを増やす」だった。

結果は、
2013 (15) ←今年!
2012 (40)
2011 (23)
2010 (41)
2009 (22)

と減ってしまいました。

ブログは減ったが、増えたのがgithubのプロジェクト。
とうとうフルスタック可能なWScoreフレームワークを作ってしまいました。

作成は楽して、学ぶことが多かった一方、
自分の知識の無さを実感もしました。

大学も機械工学、卒業後の10年はほぼマネージャー、プログラマーになってからは一人での開発がほとんどだったので、よくて知識が偏っている感じ。はっきり言えば知識不足。

だからか人と会話をする際に、自分の考えを正確に説明できない。まぁツイッターで絡んでも、今ひとつ相手の内容が理解できてないので、こちらも色々と書き込めないことが多かった。やはり網羅的な知識を一度は頭に入れたいと思うようになった。

これが来年の課題かなと考えている。

2013年12月20日金曜日

「寄付」は英語でDonationかContribution?

ちょっと英訳をしていて。

英語で寄付といえば「Donation」とばかり思ってたが、
「Contribution」という言い方もあると知った。
http://ejje.weblio.jp/content/寄付

何が違うのだろう?
と調べてみたら、
Donation refers to a gift to a charitable organization whereas a contribution is generally associated with a gift to a common fund or collection.

つまり「Donation」は慈善団体に寄付するときに使い、
それ以外の組織に対する寄付の場合は「Contribution」になると。


Dictionary.comで「Donation」調べてみたが、はっきりとは書いてないなぁ。DonationはContributionでもあると書いてあるし。

確かにContributionの方が広い意味があり、Donationのように善意を受け付けるというより自分たちに賛同する人の助けを(お金として)受け取る、という使い方になるのかもしれない。

ということで、今回は「Contribution」を使うことにした。

2013年10月29日火曜日

Marvericks上でVirtualBoxが不安定に。

どうやらMacのOSをMarvericksにアップデートしたら、VirtualBoxが不安定になったようです。

参考:

https://forums.virtualbox.org/viewtopic.php?f=8&t=58058
http://blog.offline-net.com/2013/10/27/virtualbox-mavericks-update-error/

■症状は、

VirtualBoxを走らせた後、仮想マシーン(VM)を立ち上げようとすると

Kernel driver not installed (rc=1908)
Make sure the kernel module has been loaded successfully.


とエラーメッセージが出てVMが立ち上がりません。
■対応方法1

このエラーが出たら、VirtualBoxを再インストールするしかないようです。インストール作業は5分もかからないのですが、面倒です。

再びVMを立ち上げると、動きました。
VMのイメージ自体は問題ないようです。
よかった。

とは言え毎度再インストールは、本当に面倒です。

■対応方法2

原因は、マックのウィンドウの赤い「×ボタン」、名前なんて言うんでしょうね?を使うと問題が出るようです。つまり、VMが正しくシャットダウンされてない状態になるようです。

なので、*nix系であれば、
sudo shutdown -h now
とコンソールからシャットダウンすれば問題ないようです。

【追記:2013/10/30】
本日また同じ問題が発生。どうやら、上記の対策では対応できないようです。残念。こうなると、↓ですか。

■Oracle様待ち

Marvericksに対応したVirtualBox修正版がリリースされるまで待つしかなさそう。


2013年10月27日日曜日

時代は「CYOFW」

時代は「CYOFW」。
BYOFWは「Create Your Own FrameWork」の略ということで。

自分が言ってるのではなくて、SymfonyやComposer、PSR-3、そしてBEAR.Sundayが何を達成しようとしているかを考えれば、この方向に未来があると思う。

好みのコンポーネントを組み合わせて、自分のFWを作る。Laravel風も、FuelPHPだって、CodeIgniter風も作れるはず。


■コンポーネントが輝く

そうなると、コンポーネントが競争し始めると思う。Smartyかtwigか、プレーンPHPのどれ使うか悩むよね、程度の話なんですが。

いまさらフレームワークを新しく作るより、キラリと光るコンポーネント作ったほうが使われる気がする。

全く新しい機能を提供できれば一番。
それが難しくても、早い&小さい、あるいは、使いやすい、といった面で特徴を出せる方がいいのかなぁと。

理想のコンポーネントは、簡単で小さくて使いやすく、その上、必要になれば様々な機能を簡単に追加できる、なんていうことができれば文句はないだろう。

自分で言えばCenaは他にはないコンポーネント。
と考えると、今後はCenaに集中すべきだろう。

一方、Requestやテンプレートは、今更作ってもと思う。


■グルー(糊)をどうするか

コンポーネント間をつなぐのがグル〜、つまり糊なのだけれど、コンポーネント間、あるいはコンポーネント内の依存性は、どう解決すべきなのだろう。

DI対応しているのが前提で、コンテナをどうするのか?
JSR-330のように、PHPでのDIの仕方も標準化されるのだろうと思う。


■FWってなんだろう?

CYOFWとなると、FWってなんだろうと思う。
さすがに全てのPHPerがFW自作するとも思えないので、有名なFWを使うという状態は変わらないと思う。なので、あまり意味のない疑問かもしれないが。

何を持ってFWというのか?
何がSymfonyで、どこからSilexになるのだろう。

FWがアップデートしても、それまで書いたものが無駄にならない、というのがFWを特定するものだとすると、

  • 設定の仕方・手順、
  • MVの分け方(コントローラーの書き方)、

あたりがFWの肝になるのかな。