Chonaso's Commentary

InternetやIT技術などについて知ったこと、試したこと、考えたことを書いていきます。

PHP Exceptionクラスの聖域

PHPの触れてはいけない部分に触れてしまったようです。

Throwableを直接実装したいなんて今思えば相当追い込まれていたのかもしれません。

https://www.php.net/manual/ja/class.throwable.php

注意: PHP のクラスが Throwable インターフェイスを直接実装することはできません。 そのかわりに、Exception を継承する必要があります。

PHPのfalseの闇

PHPで闇深いといわれる代表選手は == ですが、これについては使わなければいいだけですので、そこまで目くじらを立てる必要はなさそうです。

個人的に闇深いと感じたのが false の使われ方です。

規定の型 or false

PHPの関数によくある返り値が規定の型 or falseというパターン。 代表的なのがstrpos

返り値

needle が見つかった位置を、 haystack 文字列の先頭 (offset の値とは無関係) からの相対位置で返します。 文字列の開始位置は 0 であり、1 ではないことに注意しましょう。

needle が見つからない場合は FALSE を返します。

これがなかなか扱いづらいのです。

アノテーション

そういった関数のラッパー的な関数を書くときにこの流儀を引き継ぐと、アノテーションを書くときに

/**
 * @return int|boolean
 */

みたいな感じになるわけですが

といった心の叫びが漏れてしまってました。

Union Type V2では int|false みたいな書き方が許されてるようですね。


お前どっちのfalseよ!?

つまりRedisにbooleanの値が格納されていることが期待できる状況でfalseが返ってきた場合、値がセットされていないのかfalseという値が取れたのか区別がつかないわけです。

これに対してこういうコードを書きたくなるわけですが、これはgetしたときには値が消されている可能性が否定できないのでアウトです。

$value = $redis->exists('hoge') ? $redis->get('hoge') 
                                : getFromDatabase('hoge');

ワークアラウンドとして、boolean型の値を取得したい場合は一度自動シリアライズを無効にするってところでしょうか。

// デシリアライズしない
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
// redis上でbooleanのfalseが格納されていたら b:0 が返ってくる、存在しなかったら booleanのfalseが返ってくる。
$ret = $redis->get('hoge');
// シリアライズ設定戻す
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
// Redisから値が取得できている場合は手動でデシリアライズする
$value = ($ret !== false) ? unserialize($ret) : getFromDatabase('hoge');

こういった事情からこんなコードを書きたくなってしまうわけですが、

if($ret = $redis->get('hoge')){
    $value = unserialize($ret);
}else{
    logger.debug("******************");
    $value = getFromDatabase('hoge');
}

これはさすがにIDEにはやめろと言われます。

とりあえずfalseでなきゃOK的な

他にもあるかもしれませんが。

おまけ

これは true null にも言えることなんですが、大文字・小文字の区別が無いという点もなかなか闇深事案ですね。

PHPマニュアル内でも一貫性が無いという徹底ぶりです。

PHPの配列とハッシュ

これは当時の憤りから漏れ出たtweetですが、PHPあるあるの一つなんでしょうけどこれにはクラッと来ました。

実際気をつけないとハマることもあるでしょう。


こういうJSONを出力する必要があったんですが、

{ "0": 1, "1": 5, "2": 2}

こんなコードを書くと、

$json = json_encode( [ 0=>1, 1=>5, 2=>2 ] );

$json の中身はこうなります。

[1, 5, 2]

こう書くと?

$json = json_encode( [ 1=>1, 2=>5, 3=>2 ] );

$json の中身はこう。

{ "1":1, "2": 5, "3": 2 }

つまりはハッシュキーがゼロ始まりだと配列になってしまうわけですが、 この辺はPHPマニュアルには

PHP の配列は、実際には順番付けられたマップです。マップは型の一種で、 値をキーに関連付けます。 この型は、さまざまな使い道にあわせて最適化されます。 配列としてだけでなく、リスト (ベクター)、 ハッシュテーブル (マップの実装の一つ)、辞書、コレクション、スタック、 キュー等として使用することが可能です。 PHP の配列には他の PHP 配列を値として保持することができるため、 非常に簡単にツリー構造を表現することが可能です。

などと自慢気に書いてあります。

ちなみにこう書くと

$json = json_encode( [ 1=>5, 2=>2, 0=>1 ] );

$json の中身はもちろんこう。

{ "1": 5, "2": 2, "0":1 }

これは誰がうれしいんでしょうねぇ…

json_encodeに配列をつっこんでも0始まりのハッシュで出す方法があった気がするんですが失念したので思い出したら追記します...

PHP浦島太郎

2017年夏頃から2019年初頭まであるゲームのサーバサイド担当としてPHP業に勤しんでいました。

PHPに最初に触ったのは学生時代で、その頃のPHPに対する認識は

print "Content-type: text/html\n\n";

を自動でつけてくれるPerlエイリアス(しかもCGIより速い)というイメージでした。

新卒時はPHP5でPHP4っぽいコードを書くプロジェクトにいたこともありましたが、

ここ10年ほどのPHPとの関わり方は

  • SSIをPHPで置き換えましたみたいなページの保守
  • PHPからOracleを使うために手動でPHPをビルド(しかもWebアプリじゃなくPHPで書かれた業務バッチで使う)
  • Java&Tomcatで運用してますっつってるのになぜかPHPで納品されたものを使わなきゃならなくなってQuercus(Java上でPHPスクリプトを動かすやつ)を組み込む

などコーディングとはあまり関係ないものがほとんどでした。

そんなPHP力ほぼゼロの状態で無慈悲にも即戦力としてPHPプロジェクトにブチ込まれたわけですが、とりあえずPHP公式ページを読んでみると、

「俺の知ってるPHPと違う…」 「一瞬Javaかと思ったけど$が付いてる」

といった状態。 どうも自分の知識はPHPの3か4くらいで止まってたようです。

PHP3系→4系の変更インパクトも割と大きかったですが、PHP4系→5系・PHP5系→PHP7系もなかなか変わってますね。

Wikipediaの既述の変遷も興味深く、影響を受けた言語にはJavaが追加され、型付けは「弱い動的型付け」から「強い動的型付け」へと変化しています。 いったいPHPはどこに向かってるんでしょうね。

新規開発ということもあり、PHPのバージョンは7系だったのでよかったです。 アプリのローンチギリギリまでバージョンは追従させてました(たぶん)。 逆に今PHP5で書けって言われたら発狂するかもしれませんね。

ってことで、久々のPHPでは色々と悶絶させられたのでその辺のネタをちょくちょく書いていこうかと思います。

近況など(2019年末)

一昨年・昨年に引き続き、異動先のグループ会社で働いています。 昨年書いたときは別の会社のスマホゲームを作っていましたが、2019年2月いっぱいで開発チームから抜けて自社の仕事をしています。

ちなみにそのスマホゲームはすでにサービス終了しています。

B2C Webサービスの開発運用に携わっていた時にもサービス終了を迎えたものがありますが、今回は初期開発から参画したものでしたので残念な気持ちはより大きいです。

これについての公式な振り返り内容を聞いているわけではないですが月々の売上や運営状況はフォローしていましたので自分でも思うところは色々あります。

(当たり障りのないところで)得た教訓としてはスマホゲームは何をKPIとし、日々どのような運営をし、何を禁じ手としなければならないのかがうっすら分かったというところでしょうか。

そういった経験も踏まえつつ、自社(正確には親会社)向けのKPIデータ集約システムの提案~開発を行ない年末に稼働を始めました。年明けから本格運用となります。


今年は初めて海外出張を経験しまして、プライベート含めて初めて日本国外に出ました。 (そういえば10数年前に転職して以来泊りがけの出張ってこれが初めてでした)

まぁ、ビデオ会議システムの性能が飛躍的に向上しているので個人的にはリモートでもいいのでは?と思わないこともないのですがやはり直接顔を合わせることもたまには必要でしょうか。

基本的に上司に帯同だったので困ることはほぼありませんでしたが、時差には結構ハマりました。 なぜ多くのSNSやチャット、メッセンジャー類の投稿日時が「〇〇分前」などの相対表示なのかというのを今更ながら理解しました。


新しい試みとしてはゲームサーバの性能評価をいくつか実施させていただきました。

昨今スマホゲーがリリース直後に高負荷でサービス停止・メンテナンス入りに追い込まれるケースが散見されますが、その多くはやはり負荷試験不足に起因していると個人的には考えています。

最低でもβテストを実施していればかなりの部分が事前にわかったのでは?という気もしますが、βテストの準備自体相当のマンパワーを要するためなかなか実施できないという事情もわかりますので悩ましいところです。

システムを安定的に運用する責任は運営チームではなくエンジニアにあります。 昨年開発に携わったゲームもβテスト後から正式リリースまでの間は私はほぼパフォーマンス改善に時間を費やしていました。

性能評価といえばシナリオやテスト条件をもらってツール実行するだけ、と思われる方もいらっしゃるかと思います。

自分達が行なった性能評価は実際にゲームをプレイし、ソースコードを読み、どの箇所あるいはどんなシナリオでどのような条件でテストするかといったコード修正以外のほとんどをカバーする内容でした。時にはゲームサーバのパフォーマンスチューニングも提案しています。

明確に「××が遅い」原因を調査するわけではなく「特段遅い部分があるという認識が無い」中での評価は暗中模索な部分があります。

これに対してゲーム開発から得たゲーム特有の負荷ポイント知識・長年のB2Cシステム開発経験から培われたWebシステムのチューニング知識、ゲームそのものに対する理解が非常にモノをいいました。 スマホゲームの開発スケジュールは往々にしてタイトであるためこれらを短納期が求められるのはなかなかシビれます。

性能評価を行なったゲームは負荷問題が発生することなく運用されているようです。


昨年はみっちりPHPでしたが、今年は公私ともにAWS屋さんでした。 自社のシステム保守や新規システムの構築、上述の負荷試験(EC2インスタンスの量がハンパない!)などはすべてAWSを使用していました。

とにかくサービスや設定項目が非常に多岐にわたるため多くを習得するのは大変ですが、やはりAWSスタックで構築する早さは目を見張るものがありますね。

今のところAWSを使っていれば許される、という状況ですが、今後はコストやリスクマネジメントなどの観点からも他のIaaS/PaaS/SaaSにも手を出していかないとならないですね。


気づけば異動から2年経過したので再異動ロックも解除されていました。 今のところ(正式な)オファーがあるわけではないので気にしていませんでしたが、中年サラリーマンとしては自分がどういう立場に置かれるかというのは若干気になるところではあります。

立場で思い出しましたが、肩書が「開発部部長」になってました。 ただ今のところあまり部長らしい仕事はしていないです。 (給料も変わってない😇)

立場が仕事を作るのか資質が立場を作るのか、来年はその辺が見えるかもしれません。

ORA-02287

たまによく踏む。

ORA-02287
ここでは順序番号は使用できません。
sequence number not allowed here
-- ORA-02287になる
insert into HOGE values ( (select SEQ_HOGE.nextval from dual),SYSDATE)
-- OK
insert into HOGE select SEQ_HOGE.nextval,SYSDATE from dual

シーケンスはサブクエリで呼んじゃだめだってさ。

RundeckのSlack通知テンプレート更新

Rundeckはシンプルなジョブコントローラーですがプラグインによる機能拡張が可能です。

Rundeckのジョブ実行に関する通知をSlackへ飛ばすには公式GitHubにあるslack-incoming-webhook-pluginというプラグインを使用します。

インストール自体はJARファイルを設置するだけなので簡単ですが、通知内容のカスタマイズができません。

通知の内容自体は汎用性が高いのでそのままでも使えるのですが、メンションを付けたいなどの要求にはテンプレートをカスタマイズする必要があります。

このプラグインでの通知内容はFreeMarkerのテンプレートで定義されており、かつては特定の場所にテンプレートファイルを置くことで通知内容をカスタマイズできるようになっていたのですが、あるバージョンから外部テンプレート参照の機能がオミットされています。


というわけで、少々強引ではありますがJARパッケージ内にあるテンプレートファイルを上書きすることで通知内容をカスタマイズすることができました。

JARパッケージを直接変更するため、実施の際には各自の責任において実施してください。

  • 動作環境
    • CentOS7
    • rundeck-3.1.2
    • slack-incoming-webhook-plugin v1.2.5
# 作業用ディレクトリ作成
sudo -u rundeck mkdir /etc/rundeck/templates

# JARファイルからテンプレートファイルを抽出
cd /etc/rundeck/templates
sudo -u rundeck jar -xvf /var/lib/rundeck/libext/slack-incoming-webhook-plugin-X.X.X.jar templates/slack-incoming-message.ftl

# テンプレートを編集
sudo -u rundeck vi slack-incoming-message.ftl

# JARファイル内のテンプレートファイルを上書き
sudo -u rundeck jar -uf /var/lib/rundeck/libext/slack-incoming-webhook-plugin-X.X.X.jar -C /etc/rundeck templates/slack-incoming-message.ftl

Rundeckdの再起動は不要で即反映となります。