PHPでターミナルに黒魔道士を出す

via vallog: ターミナルに黒魔道士

PHPでもやってみた。こういうスクリプトを書く。

<?php
// draw.php
foreach (str_split(stream_get_contents(STDIN), 1) as $c) {
    echo ctype_digit($c) ? "\x1b[" . ($c + 40) . "m  \x1b[0m" : ($c === ' ' ? '  ' : $c);
}

kuro.datという以下のような内容のファイルを用意。

               00
             00330
            033320
          0033320
        003333220
 000000033332220
0333333332222220
 02222333322220
  0002222333200
    00002222220
    030000022220
   00300300002220
  04000030000400
 040000000000040
 044400044444440
 00444444400040
0330444440044440
03304444033044440
 004404403304440
 040440040044440
 040444440044440
 044044440044440
 044404444044440
 0444400444044040
044444444444004440
 0000000000000000

コマンドを叩くと黒魔道士が現れる。

$ cat kuro.dat | php draw.php

追記

ワンライナー版も書いた。

<?= preg_replace(['/[0-9]/', '/ /'], ["\x1b[4\$0m \x1b[0m", '  '], stream_get_contents(STDIN));

preg_replaceに配列渡すの初めて使った気がする。

追記2

factor版も書いてみた。

IN: .
USING: io kernel sequences prettyprint strings ;

: colorize ( str -- escaped )
   "\e[4" "m \e[0m" swapd 3append ;

: a ( ch -- str )
    [ 1string ] keep
    "0123456789" member?
    [ colorize ] when ;

: b ( ch -- str )
    1string dup " " = [ drop "  " ] when ;

contents [
    a [ b write ] each
] each

電子書籍やkindleを好きになった理由

自宅の近くには、小さな本屋がある。仕事帰りにはよくその本屋で立ち読みしたり、気になった本は購入したりしているのだけれども、この購入した本が部屋の中に段々溜まってきた。自分の部屋には小さな本棚があるのだけれども、とっくの昔にそれはいっぱいになった。本棚から溢れた本は床にそのままおいてたのだけど、これが段々部屋の面積を圧迫してくるようになった。タチの悪いことに、気になった本をホイホイ買う割には忙しかったり時間が取れなかったりして積んだまま開けていない本もいくつかある。


本棚のキャパシティを超えて溢れだす本。汚い。

これとは別にamazonが日本でも電子書籍を売り始めた頃から、電子書籍を買うようになった。kindleデバイスを持っているわけではなく、手元にあるHTCのAndroid端末にkindleアプリをインストールしてそれで読んでる。amazonのウェブサイトからkindle本を購入することもできるし、Androidkindleアプリだとアプリ内で直接ワンクリック購入もできる。

kindleを使いはじめるようになってから、読書している時間が増えた。というのも、ちょっとした休み時間や待ち時間、 電車の中などでAndroid端末から簡単に読書できるようになったからだ。リアルに存在する本だと、持ち歩くのにも読みたい本を選んでカバンの中に入れて持ち歩かなければならなかった。kindleの場合だと単に携帯端末を持っているだけでそのまま読書できる。そしてそれが想像以上に手軽で快適だった。煩わしい事無くどこででも本を読めるようになった。

場所取らないしいちいち持ち歩かなくてもいいから電子書籍素晴らしいと思った。

自分が職を失った経緯

この記事は、How I Fired Myself.という記事の試訳です。

2010年の7月、私は22歳で、カリフォルニアのあるソーシャルゲームのスタートアップで働いていた。卒業したてで、私にとって初めての本物の職だった。給料をもらってアパートに住んだ。そのころ私は初めて大人になったような気分でいた。

その会社の主力製品であるRPGのコードを書く二人のエンジニアのうちの一人が私だった。大学では哲学を専攻していた。これはどういうことかと言えば、問題に対してどうやって考えればいいかを知っていた一方で、ベストプラクティスや実用的なデザインパターンに関する知識は最低限しか持っていなかった。私は信じられないほどの熱意でもって自分が持っているごく普通のLAMPの知識を駆使した。

私の悩みの種であるゲームデザイナーはしばしばWorld of Warcraftからインスピレーションを得ていた。WoWは、Blizzard社が生み出した信じられないほど成功したフルMMOだ。その頃はWoWの'Raids'という機能が、ユーザに熱狂的に受け入れられていた。私たちはそれを模倣しようとした結果、とんでもないことを引き起こした。

私はそのゲームにRaidsを実装するタスクを担当することになった。チームメイトであるSamは巨大なリファクタリング作業にハマっていて、私は何か新しく楽しいことについて作業できそうなのでラッキーだと感じた。

私の開発環境の奇妙なところは、自分のコードを全て本番のデータベースに対して実行するということだった。振り返ってみれば、それは馬鹿げていることだ。そして私がそれについて疑問を思うほど十分な知識を持っていなかったということは、まさに馬鹿馬鹿しい話だ。

phpmyadminとはまったく違う、OSXの格好良いインターフェイスを持つGUIのクライアントを使って私はMySQLのテーブルを操作しようとした。私の単純なテストプロセスの一部では、RAIDSテーブルを手動で消してからプログラムで再作成する必要があった。

このタスクの単調さは、私をある種の無感覚状態に陥れ、ある怠惰な火曜日の午後、私はうとうとして、USERSテーブルのアイコンにカーソルを置き、メニューを開いて「消去」をクリックした。

私には一瞬何が起こったのかわからなかった。ハッカーの暗い部屋をたなびくような本物の体外離脱を初めて経験してから、私は光を放つターミナルに額を近づけた。

何が起こったのか? そのゲームは課金するカスタマーを数万抱えていた。ユーザーたちは、自身のキャラクターの能力を高めるための特別なアイテムに対してお金を払っていた。キャラクターの能力の全ては、今や無となったUSERSテーブルに格納されていた。

数分後、コンテンツマネージャーの一人が部屋の中に歩いてきた。「何か問題があるみたい」と彼女は言った。私は思い切って声に出した。「はい、何がおかしいのか知ってます」

私はRackspaceへ電話をして、サポートデスクに助けを求め、MySQLインスタンスのバックアップは2ヶ月以上前にキャンセルされているということをそこのエンジニアが根気よく説明する声を聞いていた。ああ。

午後遅く、その建物の最上階に集められた人たちは不安で脂ぎっていた。皆は何かがおかしいことをわかっていたが、実際に何が起こったのかはよくわかっていなかった。私は、エンジニアと上級職の緊急ミーティングが行われた会議室に引き出された。

CEOはテーブルにもたれかかり、私の顔を見てこう言った。「なにもかも、ぶち壊しだ。お前は100万ドルの損害を出そうとしている」Skypeからリモートで出席していた共同創業者は次の言葉に同意した。「クビにならなくて幸運だと思えよ」

会社として、私たちは応急処置のために次の数日間を消費した。ゲームに関する全ての作業は中止した。技術者は、他のテーブルのデータを元にしてUSERSテーブルの上辺を繕おうとした。非技術者は怒ったカスタマーをなだめたり、完全に情報を覚えていると主張する人のために手動でデータを入力した。私は3日間完全に家に帰ることが出来なかった。自分の体からは悪臭が漂っていた。

いかなる時点でも私によってこの事態の全てが引き起こされたという事は公式にはアナウンスされなかった。その代わり、「若いエンジニア」がやらかしたと説明するメールが送信された。私は「若い」と思われていた二人の人間のうちの一人だった。

三日後、原因が自分であることをみんなが知るようになった。みんなは以前とは違う接し方になった。少しだけ同情もあったが、ほとんどは怒りと不信だった。彼らにとっては、この会社は自らが今まで就いたことがない最高の職を提供していた。私は、この会社を危険に追いやり、彼らの幸せや暮らしをぶち壊しにした。

私は罪の意識と疲労とでやせ細っていた。私は、あの日を迎えた会社の全ての人に公式に謝罪した。拍手があった。

ひと月かそれより後に、私はCEOと私のプロジェクトマネージャに職を辞する手紙を書いた。次の日町を離れ、ついにニューヨークへと向かった。

日本でも大事なテーブルをtruncateしてしまった話をたまに見ますが、恐ろしいですね。教訓としては、開発者が本番のデータベースに簡単に触れられないようにする、バックアップはきちんと取っておく、の2つでしょうか。この記事の作者は今はfollowgenというウェブサービスとその運営会社を立ち上げているようです。

第四回闇PHP勉強会で正規表現リテラルの実装について話した

PHP正規表現リテラルをどんな感じで実装するかを3/2の第四回闇PHP勉強会で話しました。PerlJavaScriptRubyPythonが持っているような正規表現リテラルPHPには無いのですが、この話では実際にパッチを書いてみてどんな感じで正規表現リテラルを追加していったのかをデモをまじえつつだらだら話す感じで説明しました。

他の方の発表も基本的にはZendEngineや拡張の話だったのですが、自分の知らないところの知識やノウハウが話されていて参考になりました。@rskyさん、@do_akiさん、@hnwさん発表お疲れ様でした。

次回の開催はいつぐらいにやるか特に決めてないのですが、だいたい6,7月ぐらいになるかなーと思っています。

クライアントサイドJavaScriptでのビューの作り方4つ

追記: 指摘により、UIオブジェクト型を追加した(thx @kanreisa)。

クライアントサイドJavaScriptでのビューの作り方を大別すると、3つ4つある。

  • DOM操作型
  • テンプレートエンジン型
  • UIオブジェクト型
  • データバインディング

ここでいうビューの作り方とは、有り体にいうとJavaScriptからどうやってDOM要素を生成したりするかどうか。イベントリスナーの登録とかも含む。

DOM操作型

昔ながらのやり方。jQueryとか使って直接DOM操作してビューを作る。

// 例えば、<div class='hoge'>fugafuga</div> みたいなDOMを表示する
var myView = $("<div class='hoge'/>");
myView.text('fugafuga');

// body以下に挿入
$(body).append(myView);
  • 長所
    • 誰でも意味はわかる
  • 短所
    • 読みづらいし書きづらいのでメンテナンス性低い
    • コード量が無駄に多くなる傾向

テンプレートエンジン型

テンプレートエンジンが世の中にはあるのでそれを使う。HTMLでそのままビューのひな形を書けて、データをJavaScriptから嵌めこむ。

下の例ではPUREというライブラリを使っている。

<!-- ここがテンプレート -->
<div class="template">
  Hello <a></a>
</div>

<script>
  var jsonData = {
    who: 'BeeBole!',
    site: 'http://beebole.com'
  },

  //template commands
  directive = { 'a':'who', 'a@href':'site' };
  //select the template and transform it
  $('div.template').render(jsonData, directive)
</script>
<!-- http://beebole.com/pure/documentation/get-started/ から引用 -->
  • 長所
  • 短所
    • ビューに用いるデータを更新するたびにテンプレートエンジンの描画メソッドを叩く必要がある
    • テンプレートエンジンによってはイベントリスナーの登録をサポートしていない

UIオブジェクト型

jQuery UIExt.jsのような、DOM操作を隠蔽してくれるようなオブジェクトを作ってくれるライブラリを利用する。Ext.jsなど、内部ではテンプレートエンジンを使っていたりする場合もある。

// Ext.jsでのタブパネルを表示する例: http://cdn.sencha.io/ext-4.1.1a-gpl/docs/index.html#!/api/Ext.tab.Tab
Ext.create('Ext.tab.Panel', {
    width: 400,
    height: 400,
    renderTo: document.body,
    items: [{
        title: 'Foo'
    }, {
        title: 'Bar',
        tabConfig: {
            title: 'Custom Title',
            tooltip: 'A button tooltip'
        }
    }]
});
  • 長所
    • DOM操作を意識しなくても良い
    • あらかじめコンポーネントの挙動なども構築されている
  • 短所
    • DOM操作が隠蔽されているために実際にはどういう風にDOM操作されているのかわかりづらい
    • そのためカスタマイズしたり詳細をいじったりしづらい

データバインディング

モデルをバインドすると、モデルの更新を自動的にビューに反映したりしてくれるやつ。JavaScriptのモデルをいじると勝手にビューに反映してくれるだけではなく、DOM側の変更を自動的にモデルに反映してくれたりするものもある。

MVVMフレームワークKnouckout.jsとかAngular.jsみたいなフレームワークに備わっている仕組み。Win8メトロアプリをJavaScriptで作る際に提供されるライブラリ、WinJSでも採用されている。単体のライブラリだとjsViewとか使う。

実際のコードは、一見テンプレートエンジンを使ったやり方と変わらなく見えるが、モデル(正確にはビューモデル)のデータとビューが自動的に連動するようになる。下の例はknockout.jsを使ったもの。

<div class='liveExample'>   
    <p>First name: <input data-bind='value: firstName' /></p> 
    <p>Last name: <input data-bind='value: lastName' /></p> 
    <h2>Hello, <span data-bind='text: fullName'> </span>!</h2>  
</div>

<script type='text/javascript'>//<![CDATA[ 
var ViewModel = function(first, last) {
    this.firstName = ko.observable(first);
    this.lastName = ko.observable(last);
    this.fullName = ko.computed(function() {
        return this.firstName() + " " + this.lastName();
    }, this);
};
 
var viewModel = new ViewModel("Planet", "Earth");
ko.applyBindings(viewModel); // <= viewModelをいじると自動的にDOM要素に反映されるようになる
</script>
  • 長所
    • データ更新したら勝手にビューも更新されて便利
    • コード量が少なくなる
    • 一番かっこいい
  • 短所
    • 仕組みを理解する必要がある

まとめ

用途にあったものを使いましょう。DOM操作でなんとかするやり方は規模が大きくなると最終的には誰も幸せにならないので他の選択肢を検討しましょう。それなりにリッチなものを作るならデータバインディング型も検討しましょう。

闇PHP勉強会では何をやっているのか? => 拡張ライブラリとかZendEngineの話ばっかりやってる

どうもこんにちは、anatooです。皆さんいかがお過ごしでしょうか。僕は大変元気です。

タイトル通り闇PHP勉強会の話です。ちょうど第四回の参加者募集も始まりましたが、この勉強会は約一年前ぐらいから続けられている小さな勉強会です。名前のとおり、PHPに関する勉強会なのですが、普通にPHPの話しなくてもPHPユーザ会がやっているPHP勉強会があるからもういいよねってことで、「普段光に当たらないところや、メジャーでないPHPの話」をする勉強会として定期的に開催しています。

んで、具体的にはどんな話をしているかというと、9割がたがPHPの拡張ライブラリネタだったりPHP内部のZendEngineの話やそれのハックの話となっています。勉強会のテーマとして拡張ライブラリなどの話に限っているわけではありませんし、PHPで書いたPEGパーサコンビネータに関する話とかも僕が一度やったのですが、なぜか皆拡張ライブラリネタやZendEngineネタなどをやっています。知らない人のために補足しておきますと、ZendEngineとはPHP内部の仮想マシンのことでありRubyで言うところのYARVのことです。

考えてみれば、ZendEngineや拡張ライブラリの話をするということは、それは要するにPHPではなくC言語の話をするということであり、普通のPHPの勉強会で話をするには喋る人のほうが遠慮してしまうのかもしれず、結果として闇PHP勉強会でそういう話ばかりされるのかもしれません(ほんとのところがどうなのかわかりませんが)。

というわけで、以下に今までの発表の動画を紹介しておきます。

さて、ここからが本題ですが第四回闇PHP勉強会の参加者の募集を開始しました。今回は拡張ライブラリの話すらもどこかに行ってしまい、ZendEngineハックの話のみでございます。以下、勉強会概要です。

今回の見所はrsky先生による「オレオレPHPの作り方(仮)」ということで、rskyさんの弁によると「実際にGitHubに公開しているコードを例に挙げながら、シンタックスシュガーの実装方法からコンパイラだけでどうにかできる新機能、さらにはVMへの手の入れ方まで紹介したいです」とのことです。その後には自分も二番煎じっぽい感じですが、今までやってきたようなお遊びでZendEngineいじったりするのとは違って、PHP internalにも提案できるような正規表現リテラルを真面目に実装してみたという話をいたします。

現在atndのページにて参加者の募集をしています。喋りたい人もまだ募集しています。どしどし来るといいと思います。以上闇PHP勉強会の告知でした。

HTML5によるハイブリッドアプリ開発に関する雑記

HTML5iPhoneAndroid向けのハイブリッドアプリを作るのが最近の流行りみたいです。ハイブリッドアプリとは、外面は普通のアプリとしてAppStoreやGoogle play marketでインストールできるものの、その中身や一部がHTML5で記述されているアプリです。

最近の有名な例だと、CookpadLinkedInはてなスペース、少し前にネイティブに移行してしまいましたがfacebookのモバイルアプリもHTML5を使って記述されていました。GREE界隈で言われているらしいガワネイティブっていう言葉もハイブリッドアプリを指します。ちょっとググってみると、2016年には企業向けのアプリの50%がハイブリッドアプリになるという予測もあります。

ハイブリッドアプリの何がいいかというと、Objective-CとかJavaとかがわからなくてもウェブ系技術者であればAndroidiPhone向けアプリが書けちゃうので、今までウェブでやってきた技術者のスキルをそのまま使えるというところがよく言われます。また、マルチプラットフォーム対応する際には、全てネイティブで書くと同じ内容のアプリをJavaObjective-Cで開発しなければいけませんが、HTML5であればワンソースでAndroidiOS両方のプラットフォームに対応できるという事もよく言われます。世の中ウェブ系技術者はいっぱいいますが、スマートフォンという比較的歴史の浅いプラットフォームの開発者は余る程いるわけではないということなんだろうと思います。

ただ、HTML5でモバイルアプリを開発する際のノウハウというものは、結構細々とした話が多いせいかそれほど共有されていません。おいおいHTML5だから普通のウェブ技術者も普通に開発できるからそんなノウハウとか必要ないんじゃないの、と思うかもしれません。が、同じHTML5でもPCとくらべてスペックの低いAndroid端末やiOS端末できちんと動くようにしなければいけなかったり、モバイルアプリ特有の事情があったりするわけです。あとネイティブとHTML5を橋渡しするのはどうするか、とかも。ハイブリッドアプリも銀の弾丸では無く、色んなハマりどころがあります。よくあるのは、HTML5で作ってみたけど重すぎて使い物にならなかったり、一応動くけどUI的に大変ヘボいアプリができてしまう、またはfacebookが直面したようなHTML5で作ったアプリが落ちるけどWebViewの中身がブラックボックスなのでなんで落ちるのかよくわからない、というところです。

とりあえずハイブリッドアプリ開発の時に必要となる細々としたノウハウなどを雑多にまとめて書いておきます。

以下、箇条書き。

  • ハイブリッドアプリの作り方
  • HTML5からネイティブの機能を呼び出したいときはPhoneGapなどを使える
    • PhoneGapに実装されていない機能でも、プラグインを書くことで実装できる
  • セキュリティ
    • ハイブリッドアプリ内ではJSからネイティブの機能を呼び出せるので、XSSに気をつけるのはもちろん、iframe内で信頼出来ないHTMLを読み込む際にも気をつける
    • PhoneGapだとホワイトリスト式で読み込むHTMLを制限するなど色々対策されている
    • 自前でHTML5とネイティブを通信する仕組みを作って脆弱性生むよりも素直にPhoneGap使ったほうがいい
  • モバイル端末で表示しているHTMLにウェブインスペクタを利用するには
  • 何もかもHTML5で書こうとするとだいたい破綻する
    • android2.3やiOS4だと,CSSのposition: fixedがまともに使えないので、上部に固定されたツールバーが作れない
    • パフォーマンス上HTML5だと無理っぽい部分が出てくる
    • ネイティブですでに用意されているUI部品があるのにHTML5でイミテーションを作る無意味さ
    • 開発効率を上げるためにハイブリッドで、という場合になんでもHTML5でやろうとするとかえって効率悪くなったりへぼいアプリが出来上がったりして本末転倒になる
    • 自分の場合、画面のツールバーやそのボタンなどは、HTML5で書かずにネイティブで書いてる(中身のコンテンツの部分だけHTML5で書いている)
    • あと画面遷移時のスライドアニメーションなどもネイティブのコードで書いてる
    • パフォーマンスや原理的にHTML5では難しい画面の場合、PhoneGapプラグイン書いてその画面だけネイティブにするのが吉
  • ボタンなどにclickイベントは使わない
    • 試してみるとわかるけどびっくりするほど遅延が発生する
    • tappableのようなタップイベントをうまく扱えるライブラリを使う
  • jQueryじゃなくてzepto.js使いましょう
  • アニメーションにはCSSアニメーション使いましょう
    • JavaScriptのみで直接アニメーションするとクソ重い
    • JavaScriptからアニメーション操作するときは domElement.style.webkitAnimation みたいなアニメーション関連のスタイルのプロパティを書き換えて使う
    • アニメーションライブラリを使う場合は内部でCSSアニメーション使っているかどうか確認しておく
    • 仕方なくJavaScriptだけでアニメーションする場合でも、setTimeoutやsetIntervalではなく、できればrequestAnimationFrame使う
    • iOSだと、webkitTransformにtranslateXやtranslateを使うとチラつくので、translate3dの方を使う。
    • アニメーションの描画にはGPUを有効にして早くする
  • パフォーマンスが必要なときは、DOMをいじらない
    • 見た目を変えるときに、class属性を変更するやり方はよくあるが、アニメーション中の処理などのパフォーマンスが必要なときにやると重い。子要素を追加したりするのも同様
    • DOMそのものをいじるのではなく、DOM要素のstyleプロパティをJavaScriptから直接弄ったほうが断然軽い
  • viewport使いましょう
    • viewportはモバイル用のHTML5に特有の概念
    • 端末の幅に合わせて画面を自動的に拡大縮小みたいなことができる
    • androidだとviewportのwidth設定が効かないので、viewport.jsのようなviewportの動作をエミュレーションするライブラリを使う
  • リストのような要素を沢山表示する画面でのイベント処理には、イベントが発生するDOMElement一個一個にaddEventListenerすると重くなる
    • document.bodyにひとつだけイベントハンドラをつけてevent.targetでどのDOM要素でのイベントかを見て処理する
  • android4系だと、WebViewがassetsからリソースを512回以上読み込むとSIGSEGVで必ず落ちるバグがある
    • HTMLだけではなくJSやCSS、画像なども回数に含む。同じリソースを何度も読み込んでも発生する。リソースの読み込みがある画面遷移を繰り返すと確実に落ちる
    • WebView内部のNDKで書かれた部分のバグなのでどうしようもない
    • HTMLなどはassetsじゃなくてファイルシステムから読み込むようにする
    • アプリの初回起動時などにassetからファイルシステムに移すとよい
  • 画面の傾きをハンドリングする
    • 画面が傾いた時には、iOSだとorientationChangeイベントが発火する
    • android2.2以前だとorientationChangeイベントが発火されず、代わりにresizeイベントが発火する
    • なのでandroidではresizeイベントからwindowの縦横のアスペクト比を見て判断する
  • touch関連イベントの扱い
    • iOSAndroidでは指のタッチを扱うイベントとして、touchstart、touchmove、touchend、touchcancelというmouseイベントとは別の扱いのイベントが発火される
    • touchstartイベントが発火された要素で、touchendイベントが発火されるとは限らない
    • touchendの捕捉はその個別のDOM要素ではなくページ全体でやる
    • それとは別の話で、androidだとtouchstartした後touchendイベントがしばしば発火されない
  • iOSで動かすハイブリッドアプリのほうがはるかに速い
    • androidiOS両対応する場合は、まずandroidに合わせる
    • 最初にiOS用のコードを書いて、その後にandroidに対応しようとするとandroidで重すぎて死ぬ