Movatterモバイル変換


[0]ホーム

URL:


pixiv inside [archive]

pixiv insideは移転しました! ≫https://inside.pixiv.blog/

この広告は、90日以上更新していないブログに表示しています。

PHP初心者が仕事で躓いた4つの罠

この記事をはてなブックマークに追加

こちらはピクシブ株式会社 Advent Calendar 2015の2日目の記事です。

こんにちは。Vimエンジニアのkana です。

さて、皆さんもご存知の通り、WebサービスのpixivにはPHPが使用されています:

f:id:ka-nacht:20151202201442p:plain

PHPについては様々な噂を聞き及んでいた為、これまでPHPとは関わらないように注意して過ごしてきましたが、pixiv.netの開発ではPHPを避けて通ることは出来ません。

仕方なくPHPを使うことになる訳ですが、実際に使ってみると……これが予想していた以上に様々な方向から毎日新鮮な驚きを届けてくれます。今回は実際に遭遇したPHP初心者が躓くポイントを幾つか紹介しようと思います。

switch の中でcontinue したらswitch の直後に飛ぶ

大量のデータをループでぶん回して処理するのはよくある話です。その中で特定の種類のデータについては処理をスキップすることもよくある話です。例えば以下の様なコードを書いたとしましょう:

for ($i = 0; $i< 5; $i++) {  switch ($i) {    case 2:    case 4:      continue;  }  print"$i\n";}//==> 0//    1//    2//    3//    4

あれれ……2と4はスキップされると思ったのに全部print されてしまってます。不思議に思いつつcontinue のリファレンスを確認すると:

Note: In PHP theswitch statement is considered a looping structure for the purposes ofcontinue.

どうしてそうなった。

一応continue 2;switch の外のループを回すことは出来るのですが、continue 2; という字面が意味不明ですし、PHPに詳しい先輩エンジニアから「PHPのswitch は危ない」とも言われたので、これ以降switch を使うのは止めました。

クロージャーで使う外側の変数を明示しないと駄目

私はScheme大好き人間なので、データ列の処理は空気を吸う様にmap して filter するのですが、pixiv内部のソースコードにはarray_map 等を使わず愚直にforeach で処理している箇所が散見されます:

$xs = [1 => 'aaa', 2 => 'bbb', 3 => 'ccc', 4 => 'ddd'];$ids = [1, 2, 3, 4];$ys = [];foreach ($ids as $id) {  $ys[] = $xs[$id];}print implode(', ', $ys) . "\n";//==> aaa, bbb, ccc, ddd

こういう箇所は「来た時よりも美しく」の精神で無意識のうちにリファクタリングしていまいます:

$xs = [1 => 'aaa', 2 => 'bbb', 3 => 'ccc', 4 => 'ddd'];$ids = [1, 2, 3, 4];$ys = array_map(function ($id) {return $xs[$id];}, $ids);print implode(', ', $ys) . "\n";//==> , , ,

おやおや、不思議な結果になってます。困惑しつつAnonymous functionsのリファレンスを確認すると:

Closures may also inherit variables from the parent scope. Any such variables must be passed to theuse language construct.

ふぁーーーーー何だそれーーーーーー!

なるほど、そりゃあ愚直にforeach で皆コードを書いてる訳だ……

未初期化の変数を配列として使うと配列になる

一人で開発している時も勿論ですが、大人数で開発しているとなおさら万人が分かり易いようにコードを書くことになります。ところが極稀に以下の様なコードに遭遇します。

$illust_id = 123;$options['size'] = 'small';   //<== ???$options['include_metadata'] = true;$illust = fetch_illust($illust_id, $options);

う、うん?$options はどこからも初期化されておらず、それをいきなり配列として使っています。どういうことだと思いつつ変数の基本に関するリファレンスを確認すると:

It is not necessary to initialize variables in PHP however it is a very goodpractice. Uninitialized variables have a default value of their typedepending on the context in which they are used - booleans default to FALSE,integers and floats default to zero, strings (e.g. used inecho) are set asan empty string and arrays become to an empty array.

お、おう……そうか……

この仕様を知っていれば便利に使える事があるかも知れませんが、単なる書き忘れと区別が付かないことが殆どなので、こういう箇所は見つけ次第撲滅してます。

ユーザー定義関数の名前の大文字小文字は区別されない

pixivのコードベースは巨大なので、気付かないうちにどこからも使われなくなった関数がどうしても増えていきがちです。という訳でここ最近のpixivでは「未使用関数削除祭り」と称してコードの整理を行っています。機械的に抽出した未使用関数の数々に対してざっくり担当エンジニアを決め、各自半日程度の時間を取って本当に不要な関数かどうかを確認・削除しています。

抽出対象はgrepして定義行しか引っかからなかった関数なので、基本的にはただ定義を削除するだけで済む作業です。とはいえ万が一の可能性も無くはないので、念の為に関連するコードも確認します。私も自分の担当分について一通り確認した上であれこれ削除していきました。

ところがある関数を削除したものを本番環境へデプロイした途端に毎秒のペースでエラーが発生するようになりました。一旦revertしてから原因の調査を始めるのですが、「grepしても引っかからない関数なのに削除したらエラーが出る」ということは実際にはどこか呼んでいる箇所が存在するということになります。

まさか……と思いつつユーザー定義関数のリファレンスを確認すると:

Note: Function names are case-insensitive, though it is usually good form to call functions as they appear in their declaration.

ウッ。

恐る恐る実際のソースコードを確認すると……

$ grep -i getSomethingByUID.../foo.php: function getSomethingByUID(...).../bar.php: $something = getSomethingByUId(...);

なんでそこd 小文字にした!

(そっと修正してデプロイし直しておきました)

まとめ

上記の4点以外にも色々とPHPで躓いた話はあるはずなのですが、毎日驚きの連続で記憶から飛んでしまいました。PHP凄い。

次回はsaturday06 さんによる凄い趣味の話です。お楽しみに!

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

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

[8]ページ先頭

©2009-2025 Movatter.jp