読者です 読者をやめる 読者になる 読者になる

composer updateでよくわからないエラーメッセージが出たが、指定したブランチがないだけだった

あるアプリケーションが依存している Git リポジトリがあったのだが、master 以外のブランチを参照することになった。ので、composer.json を更新して composer update したら、

The requested package <VENDOR>/<REPO> <BRANCH> exists as <VENDOR>/<REPO>[dev-master] but these are rejected by your constraint.

というよくわからないエラーが出た。

指定したブランチが別名で(というか master として)存在しているがあなたの制約でリジェクトされた、ってどういうこと?

いろいろ調べたがけっきょくわからず、もしやと思って依存リポジトリの方を確認したらけっきょく、参照したいブランチがリモートに push されていなかっただけだった。

master はあるけど指定されたブランチはないよ、master はあなたが指定していないから使わないよ、と言いたかったのだろうか。わからんって。

Vagrantのsynced_folderにはあまり期待してはいけない

そもそもなにがしたかったか

Vagrant + VirtualBoxで開発環境を作ろうとしていて、デフォルトのvagrantユーザで作業するのもアレなんでprovision(今回はansible_local)でユーザを作って、そのユーザのホームディレクトリをホストOSと共有したかった。

試したことと当たった問題

  1. config.vm.synced_folderowner, groupにユーザ名・グループ名を普通に指定。
    • id: magai: no such user
    • フォルダの同期→provisioningの順番なのか。
  2. provisioner側でmountする。
    • mounting failed with the error: Protocol errorとか言われる。
    • どうも、ホストOS側からVirtualBoxに指示(GUIなどから?)しないとホストOS側のパスが見えない? ちょっとここ原因が最後までわからなかった。
  3. 仕方ないので、synced_folderで、uid, gidで直接指定しつつ、provisioner側で、uid, gid指定でユーザ、グループを作成。
    • mount_optionsがおかしいのか、これも動かない。普通にvagrant:vagrantのままになっていた。

そして、解決しないままいろいろ試していて気付いたのだが、ホームディレクトリを共有するとなると、~/.ssh/なども共有することになるが、ご存知の通りこの辺パーミッションが厳密でないと動作しない。

なのにsynced_folderではディレクトリ・ファイルのパーミッションを1つ設定するだけで、細かいことはできないし、そもそもホームディレクトリを共有しても問題ばかりで駄目かなと。

あきらめた。

おまけ: 関連して引っかかった細かい問題

  1. Ansibleのmountモジュールのpathパラメータが、今回使ったバージョンのAnsibleではまだnameだった。
  2. Ansibleのsudo: yesがbecome: yesになっていた。
  3. Ansibleのuserモジュールにgidパラメータがなかった。issueは立っているので、今後修正されるかもだが、今はいちいちgidを指定してgroupを作ってから……と二度手間になる。

今後確認すること

  1. ユーザが存在する場合のみsynced_folderを設定する、ということはできないか?
  2. VirtualBoxの共有機能のパーミッション周りにはどれくらい制限があるか。

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 にするとまた別の問題が起きるので注意。すでにほかで読み込まれているときにわかりづらく問題になる。

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