plenvでバージョン指定して実行できるやつを作った

Perlで書いたWebアプリケーションのデプロイにはCinnamon使っているのだけど、これはPerl 5.14以上が必要。Perl 5.10 でやらなきゃならない案件があって、plenv local 5.10.1とかしているので、Cinnamonが使えない。さあ困ったとなった。

回避策として考えられたのは、

  • PATH="$PLENV_ROOT/versions/5.18.0/bin:$PATH" cinnamon ...みたいに実行する。plenv-execのヘルプに書いてあるやり方。
    • ダーティだし忘れそう。
  • $SHELL -lc 'plenv shell 5.18.0; cinnamon ...'みたいにplenv shellでアレする。
    • 上のよりはわかりやすいけど、ハックっぽいし補完効かないし。

ということで素直に作ることにした。plenv execほぼそのままで、plenv exec-withというのを書いてみた。

github.com

せっかくなのでGitHubにアップしてみた。GitHubリポジトリ作るの初めてだってのは自分でもおどろき。

今後はもうちょっとGitHub活していきたい。いい仕事欲しいしね。

SQL::Translator の罠

今までデータベースのマイグレーション

DBIx::Migration - Seamless DB schema up- and downgrades - metacpan.org

を使っていた。スキーマを変えるためのクエリをそのまま保存すれば migrate できるので非常にお手軽。ではあったものの、変更の数があまり多くなると現在のスキーマを確認するのが面倒臭くなってきたので、別の方法を探していたところ、

papix.hatenablog.com

などを見つけた。が、 Anego も GitDDL も、ちょっと求めているものとは違う感じなので、 SQL::Translator を生で使おうかといろいろ試したのだが、これがまあ酷い品質だった。

本当はもっときっちりまとめたかったが、数が数なんで箇条書きで。なお GitHub で一応開発は続いているようで、このうちの1つ2つは直っているっぽい。ちゃんと確認はしていないが。

  • ファイルの読み込みで encode してくれないので、非 ASCII 文字は化ける。 filename() は使わず自前で読み込んで、 data() に渡せばおけ。
  • SQL::Translator::Schema::Table->options にデータを追加する方法がわかりづらい。 ListAttr という独自の Moo::Role を使っている上、 options を読み込む SQL::Translator::Producer::MySQL 側のコードもわかりづらい。
    • $table->options({ $key => $value }, { ... }, ...)の形か、追加モードなので options() を複数回実効してもいい、模様。
  • SQL::Translator::Parser::DBI::MySQL からスキーマを持って来ると、 AUTO_INCREMENT の初期値まで入るので鬱陶しいが、その初期値を SQL::Translator::Schema::Table->options から削除するのがまたやっかい。
  • DBI->connect の第4引数が固定。つまり mysql_enable_utf8 とか使えない……。 dbh を生で渡せるので、自前で用意して渡す。ただし、 SQL::Translator::Parser::DBI 側のデフォルトの第4引数に追加する形にしないと問題が出うる。
  • ほとんどのメソッドが die せず戻り値が偽であることによってエラーを表現している。ぜんぶチェックするのは大変なので、 new() か translate() あたりにぜんぶパラメータ突っ込んで、まとめて対応するのがよさそう。
  • filters() 周りの挙動が、ドキュメントと実装でかなり異なっている。
  • SQL::Translator::Filter::DefaultExtra がどうも完全に壊れている。十分に検証はできていないが。
  • SQL::Translator::Parser::MySQL でコメント周りのパースがおかしく、コメントの位置によっては落ちる。
  • これは MySQL の仕様的なところもあるが、 BOOL などはデータベースから引いてくると tinyint(1) になるがその辺を吸収してくれない。

どうにもコードが汚くて読むのが本当につらかった。これ Moo ベースなんだけど、せめて Moo なしだったならもうちょっと読みやすかったんじゃないだろうか。

あと、自分の使い方的には INSERT も見て欲しいんだけどそこは無視される。まあ仕方ないけど、どうしたもんだろう。

機能的には便利なので、これベースで簡単なスクリプト書いて DBIx::Migration の代わりにするつもり。

PHPカンファレンス関西2016に行ってきた 前編: トーク感想

先週土曜日、PHPカンファレンス関西2016に行ってきた。PHPカンファレンスは初めて。と いうか、カンファレンスとか勉強会とかに行くこと自体初めて。

前々から興味はあったが、なかなか機会がなかった。今回もだいぶ前から行こうと決めて なかったら行けてなかったと思う。前日もだいぶ気分落ち込んでたし。

行ってみたらとても楽しかったので行ってよかった。まあこの辺の話は後編に譲り、今回 はトークの感想。

タイムテーブルはこちら。

タイムテーブル PHPカンファレンス関西2016

スライド一覧を作ってくれてる方がいる。これは便利。

PHPカンファレンス関西2016 スライドリンク集 #phpkansai - お?意外といけるやん!

10:30-11:15 Composerを速くするために必要だったもの

基調講演。Compoerの通信周りをハックして高速化した話。前からこのプラグインのこと は知っていたが、試そうと思って試せてなかった。今の仕事でComposer使えてないのも あって……。

Compoerの使い方とか、思想とか、そういう基本的なところもきっちり抑えた上、その辺 が最後の伏線になったりしていてトークとしてレベルが高過ぎた。笑いも入ってるし。

物理的距離と糞実装に起因するレイテンシをどうするか、みたいな技術的な話の部分もも ちろん面白かったが、やはり最終部が最高だった。「問題と向き合うこと」大事。

PerlユーザとしてはCPANとかcpanmのこととかいろいろ考えながら聞いていた。cpanmが重 いのは、テストがいちばんのネックな気がするのでそのまま適用はできないだろうけど、 こっちも速くなれば・できればいいなあ。最近Perlもあまり書いてないけど。

スライドにも出てくるPRはこれ。後で試すなりして、いいねつけたい。でも先にプラグイ ンからかな……。

https://github.com/composer/composer/pull/5293

11:30-12:00 PHP開発とクラウド

今回のトークでこれだけはちょっと残念だった。ブース併設の部屋だったので、ちょっと うるさく聞き取りづらかったのがまず。

内容的にもふわふわしていた。よく考えるとタイトルからしてちょっとふわふわ感あった か。

13:00-13:30 ORMユーザー対談 〜Laravel/Doctrine/CakePHP3〜

アスペクト指向によるアプリケーション拡張」と迷ったんだが、昼食後メインホールに さっさと座ってしまったせいでけっきょくそのまま見た。

話自体は十分に面白かったんだが、濃い内容・三人の話者を30分でまとめきれるはずも なく、駆け足になってしまっていたのが残念。

Eloquent (Laravel), Doctorine (Symfony), CakePHP ORMそれぞれの特徴・差異がわ かったのはよかった。特にEloquentは個人的な好みに合いそう。それぞれのコードも今度 ちょっと読んでみたい。

それにしてもやっぱりアスペクト指向~の方見たかった気持ちが残ってしまって。後でス ライドちょっと見ても、やっぱりかなり楽しそうな内容だったっぽいし。

懇親会に参加しなかったことに次いで今回2つ目の後悔。

13:45-14:15 ビューのソースコードコンフリクトから解放される、PHPerのための次世代Webアプリケーション開発への道。

立ち見ならぬ座り見が出るほどの人気。みんな興味ある分野なんだなあ、とか思った。

タイトルにある「コンフリクト」周りを期待していたがちょっと違って、事実上Web Componentsの話だった。面白かったからいいけど。

Web Componentsさっぱりだったが、割と具体的な説明があって、Web Componentsがなんな のか、の基本的なところは理解できた気がする。ちょっと眠くて途中ちゃんと聞けてない ところもあったんだけど……w

フロントエンドはさっぱりだしあまり興味もないけど、今後手を出す必要が出てくるのは 確実だと思うんで、そのときまでにWeb Componentsくらいはちゃんと勉強しておきたい。

14:30-15:00 Laravel と DIコンテナ、コンポーネントの設計

DIとDIコンテナとその他の話。DIとDIコンテナの違い、このトーク聞いて初めて理解し た。というか今までは2つの違いにそもそも特に気を留めていなかった。

なお依存性を外部から突っ込むことの便利さを知りたければ、他人の書いていまいちな コードをテストするのがいいと思う。自分はそれで依存性の扱いに気を使うように なった。

コンストラクタでの注入も、名前ベースで持って来るようなのも、引数でやるのも、プロ パティやグローバル変数(!)でやるようなのも、大体一通り経験あるけど、どれがよくて どれがあまりよくないか、についてはそこまで深く考えたことがなかったが、このトーク を聞いた感じコンストラクタで突っ込めればそれがいちばんかなとか、名前ベースで引く のは便利だけどいろいろデメリットもありそうとか、ちょっと考えた。

あと、Laravelほんとやりたい。仕事でな

趣味でちょっとくらい触っても、やっぱり理解できないと思うんだよね。それに単純にや る気も出ないし。

15:15-15:45 PHPerに知ってほしいDB設計の話

この時間帯は最初にタイムテーブル見たときは特に決めてなかったと思うが、カンファレ ンス直前くらいから酷いDB設計に酷い目に遭わされてたせいで当日は即決。

MySQLのインデックス一つしか使わないとか、EXPLAINとか、基本的な内容なのかと思った ら意外と突っ込んだ話に展開して楽しかった。MySQLの進化とか、PostgreSQLの強さと か、どうせすぐには無理だろうけどいつかは味わいたい。

ちょうどSQLアンチパターン読み始めたところで、その関係ネタもあってよかった。

RDBの知識は長持ちする、みたいなところも考えさせられた。それじゃなくても経験値足 りてないんで、なにか一つくらい得意分野持たないととずっと思っているので。得意分野 にするならセキュリティかなと思ってたけど、RDBも楽しいかも。

16:00-16:45 LT

ここは一部だけ。

PHP7で脱!モッキングフレームワーク

PHPのコア機能で手軽に? モックオブジェクトを作ろうみたいな話。PHP 5でもリフレク ションとかあるけど、PHP 7の匿名クラスとか使うともっと手軽っぽい。いいな。

しかしPHP 5.3から抜け出すのに何年かかるという状況なので、PHP 7なんていつになった ら使えるやら……。

SwiftMailer アップデート

本題とは関係ないけどMailCatcherの話が出てきて、お、っとなった。アレなんか好きな んだよ。

PHPで作成されているOSSのソフトウェアとライセンスについて

これまた本題とは関係ないけど、スライドの長さは考えないと駄目だなー、と。ネタ的な 意味では面白かったが、ちゃんと本題を聞きたかった。

github.ioでブログを公開しよう

PHPみたいな言語は学生さんとかには人気なさそうと思ってたけど、そうでもないんだな と。

いろいろ初めてなのに、なんかライブコーディングみたいなことまで始めちゃってほんと すごい。

でもいちばんすごいのはじっさいにいいコード書いてることだよね。GitHubでちょっと見 てみたけど、普通にかなり書けてる。

こういうの見る度に今いる自分みたいな半端プログラマや、そんな自分以下のプログラマ とか、もう5年もすれば絶滅するんじゃないかと思うけど、じっさいそうでもないんだろ うな。そこ永遠の謎。

ロングポーリング(Comet)の話

興味あるネタなのでもうちょっと詳しく聞きたかった。というか記憶が曖昧だしスライド もないみたいなんであれなんだけど、これも最後までしゃべりきれなかったんだっけ?

Cometとか自分が職業プログラマになる前の話だし、ほんと懐しさ。

まとめ?

全体的に想像以上だった。また行きたい。

どうでもいいけどブログが軽く半年振りだ……。

Composer の autoloader のハッシュ値はなに?

vendor/autoload.php にある、 ComposerAutoloaderInitXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX の XXX... のことね。

手抜きでサーバ側に Composer 入れず autoloader だけ入れたいなと思ったんだけど、なにか環境ベースの値だったらいやなので調べた。

<?php
...
$suffix = $config->get('autoloader-suffix') ?: md5(uniqid('', true));

composer/AutoloadGenerator.php at master · composer/composer · GitHub

らしい。

uniqid の第2引数指定してるのが気になるけど、まあ多分安全じゃないかな。

しかしほんとブログ長いこと書いていなかった。書きたいネタ自体はいろいろあったんだけど。少しリハビリしなきゃな……。

PHPではグローバル変数は閉じ込めることができる。そして、関連して引っかかったこと

前提として、PHPでは変数には2つのスコープしかない。グローバルと、各関数(メソッド)ごとのものだ。ブロックスコープがないのが不便とはいえ、グローバル変数を使わなければ割と平和だ。

しかしレガシーコードを扱う場合など、仕方なくグローバル変数を使わざるをえない場合はある。今回、あるファイルに並んだグローバル変数が設定として使われている例があった。

ぜんぶ書き換えられるなら書き換えたいところだったが、なかなかそうも行かない。仕方ないので、せめて新しいコードからはラッパクラス経由でアクセスすることにした。

以下のようにすればベストだったと思われる。

<?php
# legacy-config.php

$xxx = 'OK';
<?php
# lib/MyApp/Config.php

namespace MyApp;

class Config
{
    public function get_xxx() {
        require 'legacy-config.php';

        return $xxx;
    }
}

require は、実行されたコード上のその位置でそのまま実行されたようにふるまう(おそらく)。そのため、 $xxx は、 get_xxx() のスコープになる。

こうすればグローバル変数のはずのものを、関数のスコープに閉じ込めることができて安全である。が、そのことを知らずによくない実装をしてしまっていた。

<?php
# lib/MyApp/Config.php

namespace MyApp;

require 'legacy-config.php';

class Config
{
    public function get_xxx() {
        global $xxx;

        return $xxx;
    }
}

こちらの実装でも、グローバル変数が漏れてはいるものの、動作自体は変わらない。

そのためしばらくは問題なかったのだが、あるとき設定の取得ができなくなった。調べてみると、 get_xxx() がなにも返していない。

原因はオートローダを導入したことだった。オートローダを導入するとなぜグローバル変数が消えるのか。さらに調べて、最初の「グローバル変数を関数に閉じ込めることができる」仕様を知った。

PHPのオートローダの実装は、 spl_autoload_register() という組み込み関数によって実現される。

クラスが必要とされた際に、この spl_autoload_register() の第1引数に設定した関数がコールバックされて、そこで呼び出すという形だ。たとえば最小限(かつ適当)なオートローダの実装はこんな感じになる。

<?php

spl_autoload_register(function ($class) {
    require strtr($class, '\\', '/') . '.php';
});

new \NS\Cls(); # NS/Cls.php が自動的に require される

見ての通り、 require は関数内で行われている。そのため、

  • オートローダで読み込まれたコード内のあらゆるグローバル変数は関数スコープになる。
  • さらに、オートローダで読み込まれたコード内で読み込まれたコードも関数スコープになる。

対策としては、今回のような場合は最初のコードのように、そもそも関数(メソッド)内で require すればよい。

なお require_once にするとまた別の問題が起きるので注意。すでにほかで読み込まれているときにわかりづらく問題になる。

また、どうしてもグローバル変数グローバル変数そのままで必要な場合は、そこだけ手動で読み込めばいいだろう。

Microsoft アカウントの登録がいろいろ微妙

Visual Studioのライセンス認証のために必要となったので登録しようと思ったが、いろいろ微妙だった。

URLがlive.comのまま。

ホーム|Microsoft アカウント から登録しようとしたのだが、「旧 Windows Live ID」とかあるのにlive.comのままで、バグってるのかと思った。

登録時に既存のメールアドレスで登録するか、Hotmailの新規アカウントを作って登録するかの選択肢があるが、違いがわからない。

以下を参考にした感じ、後者だと電話番号がいるように見えたが、じっさいにそうなのかは不明。

なぜかというと、じっさいに既存のメールアドレスで登録しようとしてみても電話番号フォームがあり、しかし入力は不要だったからだ。

ほんと謎。意味不明。

パスワードが16文字まで。

それ自体がもう言うことなしだが、さらに、16文字以上突っ込んだときのエラーが「17字以上は駄目です」な感じで無駄にわかりづらいという。


まあ目的を達するのに問題があるってほどじゃなかったけど、これだけリソースのある会社がこの程度のことしかできてないの見ると、残念な気持ちになる。

Firebugなど各種開発ツールで、input:textのvalueの変化が取れない

<!DOCTYPE html>
<html>
  <head>
    <script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
    <script>
jQuery(function ($) {
    $('input:text').val('ok');
    $('input:hidden').val('ok');
});
    </script>
  </head>
  <body>
    <form>
      <input type="text" name="text" value="">
      <input type="hidden" name="hidden" value="">
      <input type="submit">
    </form>
  </body>
</html>

こんな感じでJavaScriptからvalueを設定したとき、input:hiddenの変化はFirebugの方から取得できるのだが、なぜかinput:textの方が駄目だった。

f:id:xxmagai:20150720140843p:plain

この状態でsubmitするとクエリはちゃんと、 text=ok&hidden=ok となる。Firebugの表示だけおかしいのだろう。

なお、Firebug以外でも、IE, Firefox, Chromeの各開発ツールでも同じようになった。HTMLの仕様によるものだろうか。

知らずに引っかからなくていいところで引っかかりそうになった。危ない。