#ajiting Advent Calendar 2日目 AJITOで音楽を楽しむためのメモ

1日目

こんにちは。VOYAGE GROUPの社内Bar「AJITO」に関するAdvent Calendar 2日目です。


2日目は AJITO で音楽を楽しむ方法についてメモっておきます。
というのも、AJITOで飲むときにBGMがあると、ちょっとおしゃれな感じになったり、懐メロをテロ的に流して楽しんだりすることもできますよね!

AJITO でスマホに入っている音楽を流す方法

AJITO には常設の音響機器があるので、それを使って流すだけです。

入り口にテーブルがあり、そこにミキサーやらマイクやらが配置されています。
この中に Bluetooth でペアリングすればスマホの音声出力を AJITO のスピーカーに繋ぐことができる BELKIN のレシーバーがあります


お手持ちのスマホBluetoothを有効にして belkin とペアリングするだけです

ペアリングができれば belkin 本体が青く光ります

ボリュームが最大になっていると、突然大音量が流れて周りがびっくりしますので、ボリュームは最小にしておきます。
あとは、スマホで曲を再生してみてください。再生が始まってからジョジョにボリュームを上げて良い感じに調整してください。

AJITO で生演奏する方法

AJITO では生演奏するための楽器もあったりします。たとえば電子ピアノが AJITOの奥に常設されています。

椅子だと思って座っていたものが実はカホンだったりします。ウクレレもあります。
(supported by VOYAROCK)

お酒を飲んで生演奏したくなったら是非弾いて楽しんでみてください。

明日は _nishigori さんです。

phpcon 2015 で 「営業・運用を支える "気づける" 管理画面」で話します

久しぶりに phpcon にスピーカーとして参加します。
http://phpcon.php.gr.jp/2015/
場所はいつもの大田区産業プラザPiOですね。

ちなみに前回は2012年で 「フラットなPHPからフレームワークへ」でお話させていただきました

で、今回話したいことをざっと書いておきます。

気付ける管理画面とは

現在 Zucks AdNetwork というスマホ向けアドネットワークのサービス開発を行っています。このサービスの管理画面部分のサーバーサイドは PHP で書かれていてそれなりに規模も大きくなってきています。

今回は、この管理画面の開発現場での実践している以下のようなことをお話できればと思っています。

  • 営業、運用の声をどのように管理画面に反映していっているか、言われたままを作ってないですよね?
  • 複雑な権限管理を事故らないためにどのような工夫をしているか
  • 管理画面でデータの可視化ではなく、どのようにして "データの変化" を早く共有できるようにしているか
  • 管理画面に安心して機能追加・デプロイを行っていくための工夫

webアプリケーションエンジニアしたことがある人は、ほぼ誰もが経験するであろう管理画面の開発や運用保守について何か気づきがあれば良いなぁと思っています。

また、こういうことが聞きたいというのがあればコメントいただければ、もしかしたらスライドに盛り込むかもしれません。
当日皆さんがお越しいただけるのを楽しみにしています。

json_encode したらどーなるかクイズ

突然ですが問題です。

<?php
array_unique(array_merge([1,2,3],[4]));

これの実行結果のprint_rはどうなるでしょうか?

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
)

簡単ですね。じゃあ、次は?

<?php
array_unique(array_merge([1,2,3],[2,3,4]));

mergeした結果をuniqueするのだから...

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [5] => 4
)

index番号に違いがあります。valueは同じです。

これらを踏まえて、次のjson_encodeの結果はどうなるでしょう?

<?php
json_encode(array_unique(array_merge([1,2,3],[4])));

簡単ですね。

[1,2,3,4]

はい。じゃあ、次は?

<?php
json_encode(array_unique(array_merge([1,2,3],[2,3,4])));

....

{"0":1,"1":2,"2":3,"5":4}

oh。配列だったのがオブジェクトに。

つまり、最初のような配列で必ずjson_encode結果を欲しい場合は array_uniqueしたものを array_valuesするか、再度 array_merge しないといけないらしい。

<?php
echo json_encode(array_values(array_unique(array_merge([1,2,3],[2,3,4]))));
// [1,2,3,4]

echo json_encode(array_merge(array_unique(array_merge([1,2,3],[2,3,4]))));
// [1,2,3,4]


PHPJSON返すAPI作ってたりすると自分と同じく嵌りそうな人もいるかと思うのでメモ。

CRUDっぽいルーティングとアクションを追加できるTraitコントローラーを作ってみた

管理画面で欲しいCRUD処理

いわゆる管理画面作ってると CRUDができるAdmin Gegeneratorっぽいものが欲しくなりますよね。
色々アプローチはあると思います。

  • テンプレートからひな形のコードを生成してしまう Generetor 方式
    • 必要があれば直接編集したり、直接編集しない場合はカスケード機能みたいなものがあったり
    • Symfony だと `generate:doctrine:crud` コマンドっぽいの
  • ymlなどで定義を書けばDSLで管理画面っぽいCRUDをある程度好きにできる方式

そして、これらはWebアプリケーションフレームワークが標準で用意していたり、別途ライブラリ(バンドル)を入れたりすることで実現可能だったりします。

個人的に、完全generateの場合は再generateすると死ねるので、「最初の一発だけ生成させて後は頑張る」になりますし、必要以上に生成されるコードが多すぎたりするのであまり選択したくありません。

ymlで定義できる方式は「DSLで表現できるかぎりは良いけど、複雑なものは無理ゲー」なので積極的に使いたいとは思えません。

というわけで、「おれがかんがえたミニマムなCRUDバンドル」が今回のお話です。

普通のコントローラークラスにTraitで CRUDなルーティングと機能が生える

出落ちですが、そういうことです。
Symfony2のバンドルとして作りました。
Traitを使うというのと、array()は[]で書いてるので、PHP5.4以上じゃないと動きません。
まさか、今どき5.3でry

Traitに慣れていないという方は下記の記事あたりを読んでみてください。
ちょっと語弊ありそうですが、「クラスに機能(メソッド)があたかも最初からあったかのように追加できる」ものです。
scalaのTraitと名前は一緒ですが、できることが違うのでそれはそれで注意が必要だったりします。


つまり方向性としては

  • 既に用意しているコントローラークラスに Trait を use するだけで CRUDな機能を追加できる。です。

つまり、自動生成する部分はありません。Traitで書かれたとおりにしかデフォルトでは動きません。
シンプルですね。

@Crud アノテーションで Entity, Repository, 各テンプレートファイルを指定

とはいえ、どの Entity をどの Repository を使ってどのテンプレートファイルで。というのは指定する必要があります。
Traitなので既存のコントローラークラスにプロパティを生やすことも考えたのですが、Trait を使わなくなったときに use 行を削除するだけでなく無駄なプロパティも削除する必要があるので、今回は Doctrine にあるアノテーション Reader を使ってこれらを書けるようにしました。CRUD機能が要らなくなったらuseを外せばアノテーションは残るけど無視されるだけなんでまだましかなと。(ちょっと微妙な感はあります)


最終的な利用方法は以下のような感じ

<?php
namespace Brtriver\SimpleCrudBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

use Brtriver\SimpleCrudBundle\Controller\SimpleCrudTraitController;
use Brtriver\SimpleCrudBundle\Annotation\Crud;

/**
 * @Route("/simple_crud", name="simple_crud_demo")
 * @Crud(
 *   entity="Brtriver\SimpleCrudBundle\Entity\Demo",
 *   repository="BrtriverSimpleCrudBundle:Demo",
 *   template_list="BrtriverSimpleCrudBundle:Demo:list.html.twig",
 *   template_new="BrtriverSimpleCrudBundle:Demo:new.html.twig",
 *   template_edit="BrtriverSimpleCrudBundle:Demo:edit.html.twig",
 *   template_show="BrtriverSimpleCrudBundle:Demo:show.html.twig",
 *   form="Brtriver\SimpleCrudBundle\Form\DemoType"
 * )
 */
class DemoController extends Controller
{
    use SimpleCrudTraitController;

    ....

動きを変えたいとき

テンプレートなんかは何も書かれていないのに等しいのでがんばって好きなように書いて指定してください。
アクションメソッドはTrait なので、Traitクラスに書かれたメソッドと同じメソッドを自分のコントローラーで定義すればおk。

install 方法や詳しいことは?

github の README 参照。
GitHub - brtriver/simple-crud-bundle: simple crud actions method by trait for Symfony2

わかったこと

  • CRUDのためだけに BaseCrudController みたいなの作ると辛いよね
  • CRUD っていいながら Delete 作ってないことに気づいた
  • Annotation がこんなに簡単に使えるとは知らなかった
  • オレオレ Trait 作って利用すると色々幸せになれる場面は多そう

monologでPHPのFatalエラーをログに残す

この記事はSymfony Advent Calendar 2014の9日目の記事です。

昨日は おかぽんさんの SymfonyのDIを利用してMail送信クラスをインテグレーションテストする:コード編 でした

Symfony のエラーハンドリングはとても優秀 (ただし2.4以降)

Symfony は monolog が標準でバンドルされており、2.4からはエラーハンドリングをFWでとても良い感じにやってくれます。

Symfony 2.4以降の devで Noticeエラー

たとえば、dev環境だと

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction()
    {
        echo $a;
        return $this->render('default/index.html.twig');
    }
}

のような未定義の変数を書いてしまった場合のようなNoticeのエラー時には次のようなデバッグ画面で教えてくれます。

そして、app/logs/dev.logには

[2014-12-09 00:01:57] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Debug\Exception\ContextErrorException: "Notice: Undefined variable: a" at /private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php line 15 {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\ContextErrorException: Notice: Undefined variable: a at /private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php:15)"} []

といった内容のログが残ります。便利です。

Symfony 2.4以降の devで Fatal エラー

また、Fatalエラーが起こるような次のような Syntax Error の場合でも

<?php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     */
    public function indexAction()
    {
        echo
        return $this->render('default/index.html.twig');
    }
}

次のようなデバッグ画面で教えてくれ、

dev.logにも

[2014-12-09 00:05:52] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Debug\Exception\FatalErrorException: "Parse Error: syntax error, unexpected 'return' (T_RETURN)" at /private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php line 16 {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\FatalErrorException: Parse Error: syntax error, unexpected 'return' (T_RETURN) at /private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php:16)"} []

といったように教えてくれます。

Symfony 2.4以降の prodで Noticeエラー

ただ、面倒なのが本番(prod)環境のときです。まず、前提として php.ini で

display_errors = Off

と設定されていて、たとえば nginx で

server {
...
error_log /usr/local/var/log/nginx/project_error.log;
...
}

となっているとします。
(本番環境でdisplay_errors = On なんてことないですよね)

この状態でNoticeエラーを起こしてみるとエラーは表示されず、何事もないかのように処理がおこなわれました。
そして、logs/prod.log をみてもエラーは記録されていません。
その代わりに project_error.log 側にphp-fpmからのエラーが記録されました。

2014/12/09 00:10:33 [error] 1737#0: *27 FastCGI sent in stderr: "PHP message: PHP Notice: Undefined variable: a in /private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php on line 15" while reading response header from upstream, client: 127.0.0.1, server: sf.localhost, request: "GET / HTTP/1.1", upstream: "fastcgi://127.0.0.1:9054", host: "sf.localhost:9191"

つまり、Noticeエラーは標準ではアプリケーションのログには出ないので忘れずに設定したエラー出力先を監視する必要があります。

Symfony 2.4以降の prod で Fatal エラー

また、fatalエラーを起こしてみます。本番環境なのでエラーページが表示されました。

こちらは、アプリケーションのログ (prod.log)にも記録されています。

[2014-12-09 00:12:07] php.EMERGENCY: Fatal Parse Error: syntax error, unexpected 'return' (T_RETURN) {"type":1,"file":"/private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php","line":16,"level":32767,"stack":}
[2014-12-09 00:12:07] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Debug\Exception\FatalErrorException: "Parse Error: syntax error, unexpected 'return' (T_RETURN)" at /private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php line 16 {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\FatalErrorException: Parse Error: syntax error, unexpected 'return' (T_RETURN) at /private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php:16)"} []

monolog は メモリ不足のエラーもログに残してくれる

そして、偉いのはメモリ不足時のエラーもちゃんと記録します。

[2014-12-09 00:16:23] php.EMERGENCY: Fatal Error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 100000000000000001 bytes) {"type":1,"file":"/private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php","line":15,"level":32767,"stack":}
[2014-12-09 00:16:23] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Debug\Exception\OutOfMemoryException: "Error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 100000000000000001 bytes)" at /private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php line 15 {"exception":"[object] (Symfony\\Component\\Debug\\Exception\\OutOfMemoryException: Error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 100000000000000001 bytes) at /private/var/www/sf-sample/src/AppBundle/Controller/DefaultController.php:15)"} []

なぜ偉いかというと、メモリ不足エラーが発生した時点からログに書きだそうとしても、既にメモリ不足状態なので書き出すことはできない可能性があります。が、monologはとりあえずログを書き出すメモリ分を確保しておいてから処理を行い、エラー時にはその確保したメモリを開放して書き込んでいるようです。

https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php#L94

<?php
...
    public function registerFatalHandler($level = null, $reservedMemorySize = 20)
    {
        register_shutdown_function(array($this, 'handleFatalError'));
        $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize); // ここで事前に確保
        $this->fatalLevel = $level;
    }
...
    public function handleFatalError()
    {
        $this->reservedMemory = null; // エラーを書き込む前に領域開放
        $lastError = error_get_last();
        if ($lastError && in_array($lastError['type'], self::$fatalErrors)) {
            $this->logger->log(
                $this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel,
                'Fatal Error ('.self::codeToString($lastError['type']).'): '.$lastError['message'],
                array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'])
            );
        }
    }

何を言ってるんだ。俺は LTSの 2.3 を使っているんだけど

そうです。LTSは 2.3 では挙動が異なります。その違いはエラー時に画面にデバッグ情報を出してくれないという点です。
Noticeエラーは画面には何もエラーがでずログには書き出されます。Fatalエラーは画面は真っ白に。これもログには書き出されます。
ログに残してくれるのはいいんですが、Fatalエラーで真っ白になるのは悲しいですね。

何を言ってるんだ。俺は monolog は使っているが Symfony を使ってないんだけど

おまけです。monologさえ使っていれば ErrorUnadler::register にロガーを登録しておけばPHPのFatalエラーもよしなにログに残してくれるようになります。(monolog >= 1.6)

<?php
require_once __DIR__.'/../vendor/autoload.php';

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$log = new Logger('name');
$log->pushHandler(new StreamHandler(__DIR__ . '/your.log', Logger::WARNING));
\Monolog\ErrorHandler::register($log); // UtilのErrorHandler::registerにロガーを登録

$a = str_repeat('1234567890', 10000000000000000); // メモリ不足エラーを意図的に発生させる

// add records to the log
$log->addWarning('Foo');
$log->addError('Bar');

するとログには

[2014-12-09 01:05:23] name.ALERT: Fatal Error (E_ERROR): Allowed memory size of 134217728 bytes exhausted (tried to allocate 100000000000000001 bytes) {"code":1,"message":"Allowed memory size of 134217728 bytes exhausted (tried to allocate 100000000000000001 bytes)","file":"/private/var/www/sf-sample/web/app.php","line":15} []

とメモリ不足のFatalエラーが記録されました。素敵ですね。
自前で同じようなことをやろうとするとほんと面倒で涙がでてくるので monolog さまさまです。

このmonolog の Utilの使い方については全くといっていいほど紹介してくれている記事がなかったので、この機会にまとめてみました。

PHPでアプリを作っている方のお役に立てれば幸いです。

明日は PHP Mentorsitemanさんです!お楽しみに!

ミニ四駆よりも熱い ミニッツレーサーのススメ #vgadvent2014

この記事はVOYAGE GROUP エンジニアブログ Advent Calendar 2014の7日目の記事です。
こんにちは、VOYAGE GROUPの Zucksスマホ広告配信サービス Zucks Ad Networkのエンジニアしている 前田 @brtriver です。

去年は簡単にPHPをmakeしてみた だったので、「今年は何をmakeするのですか?」と聞かれましたが、ミニッツレーサーについて語りたいと思います。

ミニッツレーサーとは

今年はミニ四駆が再ブームというニュースもあったぐらい、大人にミニ四駆が流行りました。
自分自身は小さい時はミニ四駆にもハマりましたが、それよりもラジコンが大好きでした。
ミニッツレーサーは一言でいうと「ちっちゃいラジコン」です。ミニ四駆が 1/32 なのに比べて ミニッツは 1/27 なので少し大きいですが、感覚的にはミニ四駆と同じぐらいです。

一見は百聞にしかずということで、以下の紹介動画を見てください。

実際のレースの模様の動画(レースは2:50あたり)

こっちはバギー(しかも海外)

いわゆるトイラジではなく、ガチなラジコンの分野です。
ミニ四駆はTAMIYAですが、ミニッツは京商です。
ちなみに自分が小さいときに走らせていたラジコンは YOKOMOのスーパードッグファイターでした。
広坂雅美は神です。

なぜミニッツレーサー

最初はミニ四駆の再ブームに押され、ミニ四駆をもう一度やろうかとも思ったのですが次のような理由でミニッツにしました。

かっこいい

今のミニ四駆は見れば見るほど自分が知ってたミニ四駆じゃなく、いろんなバーやポールがこれでもかというぐらい付いていてなにかが違う感。
ミニッツはオートスケールシリーズといって、塗装済みのかっこいいボディーが揃っている点。
ポールやダンパーなんて必要なく、余裕があるんだったらライトをつけることもできる。かっこいい。

専用のコースがいらない

また、走らせようとするとミニ四駆だとコースがないとやはり辛い。
その点ミニッツは適当な広さがあればコースづくりは簡単。仕事終わりにコース作って走らせていますが、ホース20m でOK

ただし、本気でコースを買うとそれなりな値段になるので辛いです。
MINI-Z GRAND PRIX CIRCUIT

最後はドライビングテクニック

どちらかというと走らせるまでのセッティング決めを楽しむのがミニ四駆で、セッティングも重要ながらドライビングテクニックを磨かないとどんなに速いクルマでも勝てないのがミニッツです。

ミスを少なく集中しないと勝てません。最初のうちは運転中にケツに力が入りすぎて翌日筋肉痛になりました。そんな感じです。

ミニッツは本格的RCとしては安い

ミニッツは実売価格では 15,000円で全部込み(シャーシ、ボディー、プロポ)です。これは本格的なRCをやっている人からするととても安いです。
実際走行させるためには、単4が8本 (シャーシx4、プロポx4)必要なのは注意です。
大体1セットで 30分ぐらい走ってくれるので12本持っておけば十分楽しめます。

VOYAGE GROUP にはパンゲアがある

これが一番大きいと思います。本当は1/10のEPをやりたいんですが、都内だとコースがほとんど無いし公園で走らせるなんかは周りに迷惑掛けてしまいますし気楽に遊べません。
でも ミニッツはサイズも小さいですし、カーペットな床さえあればとりあえず走行を楽しめます。パンゲアの会議室はカーペットなので最低限のグリップはありますし、十分な広さも確保できます。

とはいえ、汚れなどでかなりスリッピーなので次はコース用のカーペットを購入したいなーと考えているところです。

もし、ミニッツを持ってる(買った)けど走らせる場所がないという方は声掛けてください。一緒に走らせましょう。

ちなみにまだヒヨッコなのでショップのコースで走らせたことはありません。。

ラップカウンターが欲しくなる

なんとなくコントロールできるようになってきたので、コースをラップタイムで競ったりしたくなってきます。
公式ではラップカウンターが販売されています。
ICタグラップカウンター

コースよりは安いとは言え、実売価格は3万近くしてしまいます...。
無いなら作ってみようということで、作ってみてますというのがこのブログの本題。

ICタグRFID

ラップタイムを測る方法は色々あると思いますが、要件としては

  • 複数台を識別してラップカウントとラップタイムを記録したい

です。

色々調べてみると、スマホアプリでカメラを使ったミニ四駆のラップタイマーなどはありましたが、これだと複数台を識別することはできません。
素直に公式サイトで販売されているラップカウンターの仕組みを調べてみます。

どうやら赤外線ではなくRFIDを利用しICタグを車に貼って、ディテクタで通過したことを検知し専用のアプリで管理している模様。
簡単にいうと、改札のパスモでピッてやるのと同じです。

ネットで調べたところ安いRFIDリーダーは 3,500円程度で売ってありました。
http://strawberry-linux.com/catalog/items?code=15080

USBタイプのリーダーで、USBキーボードとして認識されるのでmacでも普通に動作しました。
タグを認識すると「識別したタグを入力し最後にエンターを押す」という動作をします。まさにバーコードリーダーです。

ただ、このリーダーだとアンチコリジョン機能がないので、同時に2台が検知されたときにどちらも無視されるという痛い欠点がありますが、ICタグをそれぞれのシャーシに貼ることで個別に通過する限りは個別に管理できそうです。

ちなみに、リーダーやタグをもっと高性能のものにすると、こんなことができるようです。
(本当はこういうのをやりたかったけど更に高額になるので諦めた)

アンテナ制作はWIP

今回購入したリーダーは外部アンテナ端子をどうにかやれば作れるので、良い感じに検知してくれるディテクタを作れば勝てる!!!!1

ということでがんばっているんですが、まだ誠意作成中です。。。
どういう感じでがんばろうとしているかは次の動画みてもらえばわかるかと思います。

ガムテープの部分が外部アンテナ部分です。IC タグそれぞれに固有のIDが振られているので、リーダーが認識するとそれをキーボードから入力してくれます。

ラップカウンターアプリは Vue.js で

あとはこのディテクタからの入力をテキストボックスで受ければできんじゃね?ってことで、
Vue.js でさくっと作ってみました。

本当は巷で話題のReactでやりたかったんですが、時間があるならミニッツをもっと走らせてたいとか年末にボヤロックがあるためバンド練習やらと忙しいためエンジニアインターン Treasure でも採用した慣れている Vue.js でやってしまいました。

去年のボヤロックの様子

興味がある方は twitter で #ajiting で参加表明してもらえれば。

話がそれましたが、完成したコードはこちら
https://github.com/brtriver/laptimer

最初にIDを通してエントリーします。そのあとに名前を登録しておけばその名前で読み上げてくれます。ブラウザが。


フリー走行モードとレースモードを用意していています。
フリー走行モードではひたすらラップタイムを読み上げてくれます。ブラウザが。
もしベストラップ更新した場合は「ベストラップ更新」も教えてくれます。ブラウザが。

レースモードでは指定された時間でラップタイムを測ってくれます。スタート時のカウントダウン付きです。ブラウザが。

名前を変更したらラップの履歴の名前も全部書き換えるとか、ラップタイムを履歴に追加し即座にviewにも反映させるとか
こういうものをさくっと作るには Vue.js はとてもマッチしている感じがしました。

それにしても、ブラウザの読み上げ機能すごいですね。漢字も普通に読んでしまう。
この程度だったらそれほど違和感もないですよね。
ただ、たまに突然しゃべらなくなることがありその制御方法がまだよくわからず...。

しばらくはラップタイマー作りで楽しめそうです。

まとめ

ミニッツレーサーを買ってパンゲアで一緒に走らせましょう。
皆さんの参加お待ちしておりますー。

明日は @chocopie116 で "どの会社も3ヶ月に1回必ずやるであろうアレについて書く予定です" とのことです。
アレってなんでしょね?!お楽しみに!!

Hack で JSON返すシンプルなマイクロWebアプリケーションフレームワーク書いてみた その(2)

前回とりあえず書いてみたHackで書いたコードですが、

  • strictモードで hh_client check を通したい
  • strictモードで書きたいが Aura.Router が PHP なので Strictモードの指定ができない
  • 高負荷でベンチとると composer がエラーを吐いて hhvm (HipHop VM 3.4.0-dev (rel)) が落ちる

という不満点から改良してみました。

autoload を composer から Aura.Autoload に

loaderは自前で書こうかどうか悩んだんですが、Aura.Autoloadを使うと安定したので、自前で書かずにAura.Autoloadを使うことに。

なので、Hakuを動かすためには composer install が必要ですが、名前空間の定義は app.php で書く形に。

Router を Hack で

とりあえず、マイクロwebアプリフレームワークで最低限必要な、リクエストメソッドと、URIからルーティングできる機能をHackで書きました。preg_matchなどは strictモードでは PREG_SET_ORDER のようなグローバル定数を使うと怒られちゃうので一部 //UNSAFE 指定が必要だったりと、このあたりは妥協。

web/app.php
<?hh // partial

$loader = include(__DIR__ . '/../src/Loader.php');

// add your Namespace to loader like below, if you need.
// $loader->addPrefix('YourVendor', __DIR__ . '/../src');

use Haku\Request;

$app = new Haku\Application();

// simple JSON response
$app->get('/home', () ==> ['message' => 'this page is home']);

// uri parameter
$app->get('/hello/{name}', $name ==> ['message' => $name]);

// with condition
$app->get('/user/{id}', $id ==> ['id' => $id])->addToken(Pair{'id',  '\d+'});

// with query parameter
$app->get('/search', (Request $r) ==> {
        if (!$r->query('q')) {
            http_response_code(400);
        }
        return ['q' => $r->query('q')];
    });

$app->run((new Request()));

ほとんど同じです。loaderが変わったことによる指定の仕方が変わったことと、ルーターをHackで書いたので addToken に渡すパラメータが Pairになったぐらいです。

より速くなったの?

前回までのコードがhhvmをビルドした環境だとセグフォで落ちるようになってしまったため よりstrictモードな構成になったことでどれだけ速くなったかは検証できず。でも、前回同様 PhalconPHP と比べてみると Requests per second で見ると 10% 程度高い値なので、やはり十分速いということは間違いないかなと。

ただし、hhvm構成のほうが php-fpm + phalcon 構成よりメモリ、CPUのリソースを使うように見えました。
これはhhvmをdevでビルドしたからかもしれないので一概に断定はできず。

hh_client 便利

型チェックをしてくれる hh_client は自分で呼ばないといけないので面倒ですが、事前にチェックできるのはやはり便利。
LLなので、チェックでエラーになってても動く場合があといえばあるので CIで必ず忘れずにチェックするとか必要な感じ。

Hack楽しいですね。(辛いけど)