PHPカンファレンス2021でスポンサーツアーのスタッフを募集します

コロナ禍でのテックカンファレンスの問題

昨年春からはじまったコロナ禍で、特に東京の IT 系イベントはオンライン化を余儀なくされた状態が現在まで続いています。オフラインとオンラインでは、イベントの空気感も楽しみ方も、もちろん運営ノウハウもまったく異なっていて、運営者/参加者ともに試行錯誤の中で色々手探りで試しているのが現状だと思います。

オンラインカンファレンスの協賛の意味とは

コミュニティ主体のテックカンファレンスで特に問題に思えるのが、協賛社へのメリットをどう出していくかです。オンライン開催となり会場やブース準備などの問題は軽減されるとはいえ、オンラインになった分の配信機材の準備などが多く掛かるようになり、労力も経費も以前とさほど変わっていません。つまり従来通り多くの企業から協賛金を集めて、イベントを運用していくという大きな枠組みは依然変わらないわけです。

ところがオンラインイベントになると、これまでのリアルイベントで培われてきた協賛メリットを提供するやり方が、ほぼ有効に機能しません。チラシを配布することもできません、物理的なブースも存在しません、目の前に参加者がいるわけでもありません。自らの企業を知ってもらったり、そこからより関係を深めて採用につなげたいと考えていても、そのきっかけさえ作り難いのが現在のオンラインイベントの姿です。

参加者とスポンサー企業を繋げる

10/2,3 に PHP カンファレンス 2021 が開催されますが、そこで「スポンサーツアー」という企画を実施することにしました。昨年オンライン開催された PHP カンファレンス 2020 では、参加者同士のコミュニケーションに Discord を利用しましたが、その場で参加者とスポンサー企業をつなげる施策はできないだろうか、と考えたものです。KOFOSC で行われているブースツアーのようなものを、Discord 上で行います。

Discord 上のスポンサーツアーチャンネルに、あらかじめ決まったスケジュールでスポンサー企業の担当者の方に入っていただき、運営からのインタビューやそのチャンネルに入っている参加者の方からの質問に答えていただくという形です。リアルイベントのブースの前にみんなで雑談しているような雰囲気にしたいと考えています。

手伝ってくれる人を募集します

現在 PHP カンファレンス 2021 では、20社弱のスポンサーから「スポンサーツアー」への申し込みを頂いており、私一人で回すのはちょっと無理なので、お手伝いしていただける方を 1〜2 名募集中です。参加スポンサーさんとの事前のやりとりや、当日のチャンネルの司会などを手伝っていただきます。

表向きのスタッフ募集は 7/31 で終了しているので、手伝ってみたい方は Twitter@koyhoge までリプライをください。

どうぞよろしくお願いします。

2021-08-03 23:30 追記)

無事に2名の方が申し出てくださいましたので、募集は終了します。どうもありがとうございました。

PHPerKaigi 2020で発表して「トーク盛り上がったで賞」をもらった

2020年2月9日〜11日に開催されたPHPerKaigiに参加しました。

phperkaigi.jp

3日間フル参戦ですが、3日目はなんか調子が悪くて午後からの参加でしたw

Inside SWOOLE: 非同期処理はどのように動くのか

初日の Day 0 に15分の発表をしました。最近は Swoole づいていたので、Swoole がどうやって処理を切り替えているのかを探っていく内容です。

speakerdeck.com

発表は 3.5 分くらい残して終わってしまったので長めの質疑応答になったのですが、そこで @uzulla さんと @koriym さんとの楽しい議論が始まりまして、これのおかげで後ですごいことになったわけです。

ちなみに登壇後に何人かの人と話して、このスライドは不完全でまだ深堀りが足りないことが判明したので、後日アップデートする予定です。

しかし複数日開催イベントの初日の発表は良いですね。初っ端でどっと肩の荷が下りて、残り2日間すごく気楽にイベントを楽しめました。

トーク盛り上がったで賞を受賞した

そして3日目(Day 2)のクロージングで、全セッションの中から「トーク盛り上がったで賞」を決める発表があったのですが、上に書いた発表後の議論が面白かったという理由で、私の発表が選ばれました。すげえ、というか棚からぼたもちですなw

副賞は Insta360 Go と専用ケースでした。ライフログ専用カメラですね。ちょっと欲しかったのでありがたいです。

コミュニケーションを促進する様々な仕掛け

公式で謳っているとおり PHPerKaigi は参加者同士のコミュニケーションを志向しているカンファレンスで、そのための様々な仕掛けが随所に仕込まれており、1参加者としてみてもとても楽しめました。


PHPerKaigi 2020: Opening

イベント受付に来た早々に渡されるのがコレですよ。

参加者の写真入りトレカw イベント中これを名刺交換のように他の参加者と次々交換していくわけです。実際のゲームとしての遊び方はわかんないんだけどもw

トレカ作った まつぴー さんすげえ。

他にも IRT (Intaractive Roud Table) というあるテーマにしたがって丸テーブルで話し合う場があったり、雑談スペースが常に人で賑わっていたり、カードゲームで遊べるようになっていたり。

私が長年ガッツリ関わっている「PHP カンファレンス東京」の方はマスを相手にする特性上、参加者間の濃いコミュニケーションはある意味諦めているのですが、だからこそこういう心地よい空間にいるのはとても楽しいものです。

スタッフの皆さん、素晴らしいイベントを開催していただいてどうもありがとうございました。

丸善丸の内本店で開催されている「プロが教える!読んでおきたい言語書籍フェア」に行ってきた

https://c2.staticflickr.com/4/3942/33056973633_c2034a8a6b_c.jpg

4月1日から丸善丸の内本店 4Fのロビー横の一角で、「達人選書!! プロが教える!読んでおきたい言語書籍フェア」が開催されています。これは、丸善丸の内本店とコンピュータ出版販売研究機構(CPU)の共同企画で、各言語コミュニティからおすすめ本を5冊選出してもらって、それに言語の紹介を加えるパネルを用意することで、販促につなげようという内容です。

t-wada こと和田卓人さんから、PHPで誰かやってもらえませんかというお誘いがあって、面白そうな企画だったので自分で引き受けることにしました。

コミュニティが解説を書いているプログラミング言語は以下の9つ。

あとCPUが書籍を選出している言語が4つあります。

展示場所に行ってみた

今日時間があったので、東京駅近くの丸善丸の内本店まで行ってみました。

https://c1.staticflickr.com/3/2876/33870375275_ac25ff94cd_b.jpg 展示場所は丸の内本店4Fの技術書棚近くのロビー横。

https://c1.staticflickr.com/3/2830/33485729600_5b06ec674a_z.jpg こんな形でパネルと書籍が展示されています。

https://c2.staticflickr.com/4/3940/33485729390_241d8be524_z.jpg

Ruby 高橋さんのパネル

https://c1.staticflickr.com/3/2905/33026486864_831f92db49_z.jpg

小山のパネル

https://c1.staticflickr.com/3/2840/33026486744_11696c7ecb_z.jpg

Python 鈴木さんのパネル

https://c1.staticflickr.com/3/2852/33870373335_aabe9f4584_z.jpg

Perl/Go 牧さんのパネル

https://c1.staticflickr.com/3/2879/33829273186_4735f4e41c_z.jpg

Scala 水島さんのパネル

https://c2.staticflickr.com/4/3727/33870371765_c386204668_z.jpg

HTML 吉川さんのパネル

https://c1.staticflickr.com/3/2921/33870372895_b0b70e7068_z.jpg

CPU の推薦書籍たち

https://c2.staticflickr.com/4/3856/33829273706_bacf64f7f0_z.jpg

言語の解説文が載っている小冊子はご自由にお持ち下さいでした。

私の紹介文

このフェア、Web的に見える部分にはコンテンツが存在しないようです。このままだと数年も経てばきっと検索に引っかからなくなる気がするので、せめて自分が書いた紹介文をここに記載しておくことにします。

あと本の紹介の際は

  • 1冊は初心者用のものを入れる
  • 1冊は言語に依らないお勧め本を入れる

という縛りがありました。

PHPの紹介

PHPの発端は、1995年にRasmus Rerdorf氏がC言語で書いたCGI用のツール集です。当時はウェブがまだ生まれたばかりで、ちょっとしたことをウェブサーバにやらせるための便利なツールとして徐々に広まっていきました。

ウェブに特化したプログラミング言語としてのPHPの位置づけが明確になったのは、1998年のPHP 3からです。他の言語から有用な構文をどんどん取り込んでいく一種の無節操さは、プログラミング言語に厳格さを求める人達からは嫌われましたが、「何となく書いてもそれなりに動く」というPHPのとっつきやすさがウェブプログラミングの敷居を大きく下げたのも確かです。

近年ではPHPにもモジュール指向の波が到来し、PHPライブラリのパッケージマネージャComposerを用いて多くのライブラリを利用し、また自分にあったフレームワークを使いつつ、効率的に開発を進めていくのがトレンドになっています。未だに乱立しているフレームワークも、徐々にモジュールベースに書き直され始めており、Composerを軸とした共通基盤ができつつあります。

2015年にリリースされたPHP 7は、内部構造の抜本的な改善によって大幅に性能を向上させています。今後は厳密な型チェックを可能にする方向で構文が強化される流れです。

お勧め本

確かな力が身につくPHP「超」入門

確かな力が身につくPHP「超」入門

確かな力が身につくPHP「超」入門

ウェブの動作の仕組みから、PHPの文法、データベースの連携までわかりやすく解説されています。

いまどきのアルゴリズムを使いこなすPHPプログラミング[開発テクニック]

PHPアルゴリズムを学ぶという大変珍しい書籍です。

PHPはどのように動くのか

PHPはどのように動くのか ~PHPコアから読み解く仕組みと定石

PHPはどのように動くのか ~PHPコアから読み解く仕組みと定石

PHPが内部でどのように動作しているのか、その内部構造を解説している貴重な本です。

Laravel リファレンス

Laravel リファレンス[Ver.5.1 LTS 対応] Web職人好みの新世代PHPフレームワーク

Laravel リファレンス[Ver.5.1 LTS 対応] Web職人好みの新世代PHPフレームワーク

近年大躍進しているPHPフレームワークの解説本です。

スティーブズ (1)

スティーブズ 1 (ビッグコミックス)

スティーブズ 1 (ビッグコミックス)

コンピューターが個人の手に届き始めた頃の熱いやつらの物語。最初から決まってることなんか無い。

自己紹介

フリーランスエンジニア。Webシステムの開発を行うかたわら、日本PHPユーザ会の運営に関わる。毎年開催される「PHPカンファレンス」には2003年よりスタッフとして関わる。共著書に「超・極める! PHP」「PHPエンジニア養成読本」「Laravelエンジニア養成読本」など。好きな作家は藤井太洋、好きな声優は杉田智和

おまけ

その他枠は少し悩んで、一般的なエンジニア向け名著と言われるものはきっと誰かが出してくるだろうと思ったので、ちょっと遊んで「スティーブズ」を選びました。蓋を開けてみたら、こちらが想定していた名著はあまり選ばれてなかったわけですが、まあいいやということでそのままスティーブズで行くことにしましたw

技術書に限らず出版業界は苦しい状況がずっと続いているので、このように複数の出版社と書店を横断した取り組みが行われるのは素晴らしいことだと思います。4月は新入社員としてエンジニアの門をくぐる人も多数いると思われるので、こういうことがきっかけでエンジニアリングの面白さに気づいてくれれば言うことはないです。

フェアはゴールデンウィークくらいまでは開催中だそうです。お近くに寄った際は覗いてみて頂けるとありがたいです。

日本のPHPコミュニティのあゆみ

ソフトウェアデザイン2016年9月号が発売されました。

この号の第2特集でPHPが取り上げられていて、私もコミュニティについて2Pほど執筆しています。

ソフトウェアデザイン編集長の池本(@XR230)さんから企画案をいただきまして、PHPのslackチャンネルで執筆者を募集して、実現された記事となっています。

私が執筆した第4章のコミュニティについての文章は、各グループの紹介にほぼ終始していて、あまり読み応えのある内容とはいえません。自分が締め切りすぎまでもたもたしたせいでページ数が減った結果、前半に用意していた導入部を全部ボツにするという判断で、現在のものになったのでした。

ということで、せっかく執筆した前半導入部なので、ここで公開することにします。

日本のPHPコミュニティのあゆみ

ソフトウェアデザイン2016年9月号 PHP特集ボツ原稿

コミュニティの発端

PHPに関するコミュニティらしきものが日本に生まれたのは、1997年のPHP-jpメーリングリストに遡ることができるでしょう。当時のPHPのバージョンは PHP2.0、「漢字patch」と呼ばれるものを各人が当てて日本語を使用していました。そのままでは日本語を扱えないものに対して、有志の人々が協力して日本語を扱えるように作業をし、そこでコミュニティが形作られていくのは90年代の日本のソフトウェアシーンとしてはごく当たり前の光景でした。

その後、PHP3.0.7に対して日本語を扱えるようにしたものを、日本のコミュニティとして独自に配布を始め、日本語も問題なく使用できるウェブプログラミング言語として評判になっていきます。そこからさらにコミュニティは発展して、2000年に「日本PHPユーザー会」の発足へとつながっていきます。この時はまだ自分はユーザ会には関わっていなかったですが、当時のニュース記事などを読むときちんとメディアを呼んで記者発表会が行われたようです。日本PHPユーザ会は、2000年7月2日に日本で最初のPHPカンファレンスを開催し、その後毎年欠かすことなく開催を続けています。

メーリングリストからブログへ

2000年代前半は、コミュニティのやりとりの中心はもっぱらメーリングリスト(ML)でした。普段MLでやり取りしている人達と、年に数回行われるPHPカンファレンスやLinuxConferenceなどのリアルイベントで顔を合わせて話すことで関係が深くなっていったということは、この頃なら誰しも経験していると思います。MLの流量も今とは比較にならないほどで、多いときは1日に100通以上が流れていたと思います。

そこから大きく変わってきたのは2004年頃にブログが流行ってきてからです。簡単な方法で広く世界に情報発信できるブログというツールは、その界隈での有名人を多く生み出すことになりました。PHPコミュニティでもその傾向はあり、PHPに関する情報を発信し続けるブログとそのコメント欄というコミュニケーション手段が加わることで、人のつながりが多層になったように思います。

情報をブログで発信する人が増えてくると、それをまとめて読みたい要望も生まれてきて、特定ジャンルの各ブログのRSSを一箇所に集約して配信する「プラネット」と呼ばれるサービスが流行したこともありました。

そしてSNS

今で言うSNSが最初に使われ始めたのは2004年頃でした。mixiGreeが日本発のSNSとして相次いでサービスを開始したのも2004年ですし、海外サービス勢もその後次々に日本に上陸してきました。しかしSNSが流行し始めた当初は、ITエンジニアコミュニティではさほど影響を与えていなかったように思います。

この流れが変化したのは2007年頃からのTwitterの流行だと思われます。Twitterは最初マイクロブログと呼ばれていたように基本的にはオープンな場で、人々の情報発信の敷居を大幅に下げました。ブログほど堅苦しくなく、日々の疑問やコードを書いていて感じたちょっとしたことなどがどんどん投稿されていき、そこから様々なコミュニケーションが生まれました。Twitterが広く一般に使われ始めるのは2010年頃からですが、それ以前よりIT関係者はこの新しいツールを上手く使いこなしていたように思います。

追って日本に上陸したFacebookも、ITエンジニアコミュニティにじわじわ浸透し、現在では一部のPHPコミュニティの主要コミュニケーション拠点にまでなっています。

あいまいになるコミュニティ

ブログやSNSがコミュニケーションの中心になることによって、ITエンジニアコミュニティに大きな変化が表れました。それは個人のブランド力の増大と、それに呼応した組織への帰属意識の希薄化です。2000年前後のエンジニアコミュニティは、組織や団体という看板を必要としました。そのメンバーの一員になるという帰属意識が、人々を結びつけるのに大きな影響力を持っていたのです。しかしブログやSNSでの活動は、それを書いているのが「どこのだれ」ではなく、ただ単に「だれ」ということのみが注目される傾向が強く、組織としてのコミュニティの役割は一面ではすでに失われつつあります。

個人が特定の組織に帰属することなく、その人それぞれの興味ある対象ごとに広くゆるく繋がっている状態、それが現在のITコミュニティの実態だと言えるでしょう。そういう意味では、GitHub上の各プロジェクトや、各Q&Aサイトも今風のコミュニティの一形態と見ることもできます。

PHP BLT #4 で PHP の興味深い挙動を知った

photo by Dana McMahan

昨日は PHP BLT #4 でメルカリに行ってきた。

phpblt.connpass.com

そこで @uzulla さんが雑談的に発表された内容に面白いものがありまして。

たしかこんな感じのやつ。

$values = [
  'a' => null,
  'b' => 'abc',
];
foreach ($values as $key => &$value) {
    $value[$key] = '123';
    // 本当は $key[$value] = '123' の意図
}

ここで間違えて $key と $value を入れ替えて記述したのはいいけど、$value が null の時にはエラーにならなくて、abc の時に初めて

PHP Warning: Illegal string offset 'b' in /some/path/hoge.php on line X

なんてのが出るという話。

値が null の変数は未定義と等しく扱われるので、そこで変数の生成と代入が同時に行われる。null は未定義と等しいのは以下の例をみても分かる。

$a = null;
var_dump(isset($a)); // 結果は bool(false)

で、先の uzulla さんのコードを、エラーを出さないようにキーが 'b' の要素を抜いて実行すると、当然エラーにはならず $values は以下のようになる。

$values = [
    'a' => null,
];
foreach ($values as $key => &$value) {
    $value[$key] = '123';
}
var_dump($values);

実行結果:

array(1) {
  ["a"]=>
  &array(1) {
    ["a"]=>
    string(3) "123"
  }
}

あれ? なんで配列が入れ子になるんだろうと不思議に思ったので、色々試してみたところ挙動が理解できた。

この場合 foreach でループを回す際の $value に & がついて参照になっていることがキモで、ループ内で新たな配列が作られた後、それが $values キー 'a' の値として上書きされているということになる。

分かりやすく代替コードで示すとこう。

foreach ($values as $key => &$value) {
    $tmp[$key] = '123';
    $value = $tmp;
}

参照をやめれば、当然ながら要素の上書きは起こらない。

$values = [
    'a' => null,
];
foreach ($values as $key => $value) {
    $value[$key] = '123';
}
var_dump($values);

実行結果:

array(1) {
  ["a"]=>
  NULL
}

FuelPHPでMongoDBをちょびっと便利に使う

MongoDB Advent Calendar 2013の14日目です。まぁ途中で一度途切れているので気楽に行きましょうw


さて、このエントリはここ連続で続いている FuelPHP ネタでもあります。

MongoDBでSQL的なシーケンスをどうするか

FuelPHPでMongoDBを使うには、Coreに含まれている Mongo_Dbクラスを使うのが普通だと思います。基本的なメソッドはひと通り用意されていて、通常使う文にはまあ困らないでしょう。ただしちょっと突っ込んだことをやろうとすると、そのままでは使えなくて何らかの拡張を自分ですることになるのもよくあることです。


MongoDBをアプリケーションデータの格納に使うとして、SQL的なシーケンス(SEQUENCE)が欲しいことがあります。MySQLでいうところのSERIAL属性と同様で、一意な値を自動採番していくれる仕組みです。むろんMongoDBでもちょっとした仕掛けで実現可能で、そのための手法はちょっとググれば簡単に見つかります。ただし FuelPHP の Mongo_Db クラスでは、PHP の Mongo オブジェクトがプライベート変数になっていて直接アクセスできるメソッドもないので、直接アクセスするには Mongo_Db クラスを継承したクラスを用意する必要があります。そうやって作ったのが、以下の Util_MongoPlus クラスです。

<?php

class Util_MongoPlus extends \Fuel\Core\Mongo_Db
{
    protected $seq_collection = 'sequences';

    public function getRawMongo()
    {
        return $this->db;
    }

    public function seqNext($name)
    {
        $query = array(
            '_id' => $name,
            );
        $update = array(
            '$inc' => array('seq' => 1),
            );
        $options = array(
            'new' => true,
            'upsert' => true,
            );

        $result = $this->db->{$this->seq_collection}->findAndModify(
            $query,
            $update,
            null,
            $options
            );
        return $result['seq'];
    }
} 

直接 Mongo オブジェクトにアクセスする getRawMongo メソッドと、シーケンスを実現する seqNext メソッドを持っています。シーケンスは sequences というコレクションを作り、そこにシーケンス名がキーのレコードを作成して、アトミックにインクリメントを行うことで実現しています。


※ とここまで書いていて、Mongo_Db::get_collection メソッドを用いれば任意のコレクションにアクセスできるので、直接 Mongo オブジェクトを使う必要がないことに気がつきました。まあいいやw

MongoDBをベースにしたモデルのベースクラスを作る

で、ここまでできたらもういっそ Model のベースクラスまで作ってしまえということで、できたのが以下です。

<?php
use Fuel\Core\Date;

class Model_Mongobase
{
    static protected $table_name = null;
    static protected $uniq_id = null;

    static protected $timestamp = null;

    static public function getMongo()
    {
        return \Util_MongoPlus::instance();
    }

    static public function getTable()
    {
        return static::$table_name;
    }

    static public function getSeqName()
    {
        // dummy
        return null;
    }

    static public function newId()
    {
        $mongo = static::getMongo();
        $seq = static::getSeqName();
        if (empty($seq)) {
            throw new Exception('empty sequence');
        }
        return $mongo->seqNext($seq);
    }

    // base methods
    static public function get($id)
    {
        if (static::$uniq_id === null) {
            throw new Exception('Unique key is not found');
        }

        $mongo = static::getMongo(); 
        $conds = array(
            static::$uniq_id => (int)$id,
            );
        $result = $mongo->get_where(static::getTable(), $conds);
        if (count($result) === 0) {
            throw new Exception('Item not found');
        }
        return $result[0];
    }

    static public function all($options = null)
    {
        $conds = array_val($options, 'conditions');
        $order_by = array_val($options, 'order_by');
        $use_cursor = array_val($options, 'use_cursor', false);

        $mongo = static::getMongo();
        if (!empty($conds)) {
            $mongo->where($conds);
        }
        if (!empty($order_by)) {
            $mongo->order_by($order_by);
        }
        if ($use_cursor) {
            $result = $mongo->get_cursor(static::getTable());
        } else {
            $result = $mongo->get(static::getTable());
        }
        return $result;
    }

    static public function save($data)
    {
        if (static::$uniq_id === null) {
            $result = static::saveWithNonuniq($data);
        } else {
            $result = static::saveWithUniq($data);
        }
        return $result;
    }
    static public function saveWithNonuniq($data)
    {
        // call hook
        static::beforeInsert($data);

        $mongo = static::getMongo();
        return $mongo->insert(static::getTable(), $data);
    }

    static public function saveWithUniq($data)
    {
        $mongo = static::getMongo();

        $id_name = static::$uniq_id;

        $id = array_val($data, $id_name);
        if (empty($id)) {
            $id = static::newId();
            $data[$id_name] = $id;

            static::beforeInsert($data);
            $result = $mongo->insert(static::getTable(), $data);
        } else {
            $conds = array(
                $id_name => (int)$id,
                );

            static::beforeUpdate($data);
            $result = $mongo->where($conds)->update(static::getTable(), $data);
        }
        return $result;
    }

    static public function deleteWithUniq($id)
    {
        $mongo = static::getMongo();

        if (static::$uniq_id === null) {
            $id_key = '_id';
        } else {
            $id_key = static::$uniq_id;
        }
        $conds = array(
            $id_key => (int)$id,
            );
        return $mongo->where($conds)->delete(static::getTable());
    }

    static public function deleteAll($conds)
    {
        $mongo = static::getMongo();

        return $mongo->where($conds)->delete_all(static::getTable());
    }

    static protected function beforeInsert(&$data)
    {
        $hook = array_val(static::$timestamp, 'before_insert');
        if (empty($hook)) {
            return;
        }
        $key = array_val($hook, 'key');
        static::handleTimestamp($data, $key);
    }

    static protected function beforeUpdate(&$data)
    {
        $hook = array_val(static::$timestamp, 'before_update');
        if (empty($hook)) {
            return;
        }
        $key = array_val($hook, 'key');
        static::handleTimestamp($data, $key);
    }

    static public function handleTimestamp(&$data, $key)
    {
        foreach ($key as $k) {
            $data[$k] = Date::forge()->format('mysql');
        }
    }
}

あー、いつものarray_val()を使ってますね。ここで紹介している自作の便利関数です。


使用するには、例えば以下の様な Model_Mongobase を継承したクラスを作って

<?php

class Model_Item extends Model_Mongobase
{
    static protected $table_name = 'item';
    static protected $sequence = 'item';
    static protected $uniq_id = 'id';

    static protected $timestamp = array(
        'before_insert' => array(
            'key' => array('created_at', 'updated_at'),
            ),
        'before_update' => array(
            'key' => array('updated_at'),
            ),
        );

    static public function getSeqName()
    {
        return static::$sequence;
    }
}

あとは任意のデータを格納するだけです。

<?php
  :
    Model_Item::save(array('fuga' => 'hoge'));

継承したクラスで

static protected $uniq_id = 'id';

のように $uniq_id プロパティが定義されていれば、それに対してシーケンスを適用してくれます。


いつもなら Package にまとめて github で公開したりするんですが、これに関してはちょっとどういうまとめ方が良いのか悩んでいるところもあって、とりあえず生ソースをブログで公開することにしました。


明日のアドカレは@さんです。

FuelPHPのViewの自動エスケープについて

前回のエントリ「JavaScript側にPHP変数を簡単にまるごと渡す方法 #FuelPHPAdvent2013 - Blog::koyhoge」について、PHPjson_encode()関数は標準ではエスケープ処理は行わないのでXSS脆弱性があるのではないか、という指摘をいただきました。

json_encode()のエスケープオプション

確かにPHPのマニュアルには、各種文字にエスケープ対応するオプションが存在します。

PHP: json_encode - Manual

この場合で言えば

    return sprintf($fmt, $name, json_encode($val)); 

を以下のようにエスケープオプションを追加するべきということです。

    return sprintf($fmt, $name, json_encode($val, JSON_HEX_TAG |JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP)); 

ただこのコードを書いた時に自動的にエスケープ処理がかかることを確認していたので、どこでそれが行われるかは深く調べずに、json_encodeのオプションを省いたという経緯がありました。

自動エスケープは\Fuel\Core\Viewの機能だった

その後fuelphp.jpグループで@さんに指摘されて、Parserパッケージの標準設定で 'auto_encode’ が true になっているおかげでテンプレートに渡される変数が自動でエスケープされていた事がわかりました。

fuel/packages/parser/config/parser.php

の以下の部分ですね。

<?php
:
        'View_Twig' => array(
                'auto_encode' => true,
:

この auto_encode 設定は、\Fuel\Core\View のコンストラクタに $auto_filter として渡され、結果的に\Fuel\Core\Security::clean() が呼び出されます。つまりTwig Extensionに渡される際にはすでにエスケープ済になっていたわけですね。


PHP 変数を JSON にして JavaScript に渡す仕組みは、別に FuelPHP でなくても使用できますので、その場合は XSS に注意して json_encode にオプションを随時追加して下さい。

JSON の埋め込み方の問題

他にもfuelphp.jpグループでは@さんより、HTML 要素にテキストとして JSON を書き出すよりは、要素の data-option 属性として埋め込んだ方が良いのではないかとの指摘を受けました。

  <div class="hidden">{"fuga":"hoge"}</div>

ではなく

  <div class="hidden" data-option='{"fuga":"hoge"}'></div>

とせよということですね。ふむー、これはちょっと試してみたいと思います。

Twigのクラスが古かった

元記事とその前の記事で用いた以下のクラスはすでに古く、2.0でなくなる予定だと@さんに指摘いただきました。

  • Twig_Filter_Function
  • Twig_Function_Method

これは気が付いてなかったので、Twig_SimpleFunction, Twig_SimpleFilter を使うように元記事を修正しました。

その他の反応への返事

はてブより。

id:teppeis $nameもjson_encode()もエスケープが足りないです。危険。

http://b.hatena.ne.jp/teppeis/20131207#bookmark-172246146

json_encodeについては上記に書いたとおり。$nameはテンプレートに直接記述されるので、そこに外部からの変数が渡される事態は、コード全体を見直したほうが良いレベルだと思うのですがどうでしょう?

id:thujikun JSON形式のコードをJSの変数に直接代入する方が楽な気が。。。ひとつグローバル変数使うことにはなるけども。

http://b.hatena.ne.jp/thujikun/20131208#bookmark-172246146

JavaScriptにテンプレートエンジンを通して変数展開を埋め込む方が、自分的にはあり得ないです。HTMLに埋め込み JS を直接記述することは現在は全くやっていません。

id:fakechan PHPのレガシーっぷりに驚きを隠せない。というか、こういう場合はREST APIを作って「js側から」Ajaxでアクセスすればいいのでは。Ajaxのロードが終わるまでは「ロード中...」とかかぶせて。

http://b.hatena.ne.jp/fakechan/20131208#bookmark-172246146

いやこれとPHPのレガシーは関係ないでしょ。PHPディスりたい病にかかっているようですね。何でもRESTでAjaxすれば良いやというのは、JS 側の処理を無駄に複雑にするだけではありませんか?