PHPコードのチューニング

はじめに

こんにちは、イメージ・マジックのもあいです。

最近はサッカーを見るときには2つのチームがどのように動こうとしているのかを観察することによって、より面白く観戦できるようになってきました。日本代表チームがアジアカップの決勝でどのような戦い方をするのかが楽しみではありますが、相手チームであるカタールにも気をつけてみるとさらに面白くなります。

今回はPHP(Symfony4)のパフォーマンスチューニングについての記事になります。

記事の前提
 OS: Ubuntu 18.04
 PHP:7.2.10
 nginx:1.14.0
 PhpStorm:2018.3.3
 ※全てのソフトウェアはaptコマンドでインストールしています

Xdebugのプロファイラ機能

PHPで関数/メソッドの処理時間を測定するのにXdebugをインストールします。

sudo apt install php-xdebug

php.iniを編集して有効にします。
/etc/php/7.2/fpm/conf.d/20-xdebug.ini は下記の通り

xdebug.remote_enable=On
xdebug.remote_autostart=On
xdebug.remote_host=192.168.33.1
xdebug.profiler_enable=On
xdebug.profiler_output_dir=/tmp
xdebug.profiler_enable_trigger = 1;
xdebug.max_nesting_level=1000
xdebug.remote_handler=dbgp
xdebug.remote_mode=req
xdebug.remote_timeout=2000

ポイントとしては、xdebug.profiler_enableをOnにするのと、xdebug.profiler_output_dirに測定結果を出力するディレクトリを指定してphp-fpmをreloadします。測定が終わったら xdebug.profiler_enable を Offにしてphp-fpmをreloadします。常にOnにするのは処理が非常に重たくなるのでおすすめできません。

プロファイラを有効にして測定したいURLを実行すると下記のようなファイルが作成されます
-rwxrwxrwx 1 moai moai 42986908  1月 17 17:09 cachegrind.out.26276
-rwxrwxrwx 1 moai moai     2644  1月 17 17:09 cachegrind.out.26276.0c892b
この2つのファイルをPhpStormをインストールしているマシンにコピーします。

PhpStormでプロファイル結果を解析

PhpStormでプロファイル結果を読み込みます。

Tool → Analyze Xdebug Profiler Snapshot…

ファイル選択ダイアログが開かれるので、ファイルサイズの大きいファイルを選択します。
デフォルトで処理時間の多い順番に表示されますが、これだけだとわかりにくいので、Call Treeタブをクリックして実行順表示にしてから処理に時間がかかっているのをドリルダウンして探します。

遅い部分を探して比較を行うと下記のような状態にすることができます。
改修前
DlvryController->jsonが19657msかかっています。ここで全体の53%の処理時間がかかってル事がわかります。ローカルの仮想マシンで測定しているとはいえ遅すぎます。
jsonが何を行っているかというと、Symfony4のEntityをjsonに変換しています。フレームワークの処理で遅くなっているのであれば手の施しようが無いような気もしますが、よくよく調べるとクライアント側で使っていない情報も返却していました。送る情報が少なくなればそれなりに速くなるのでは無いかという課程のもと、返却する情報を必要最低限の情報に絞った改修を行いました。

プロファイル結果は下記の通り。
改修後
3603msになりました。4.5倍速くなったことになります。
ここに出てくる処理時間はプロファイルを行っているため実際よりも大分遅いですし、VirtualBox上の仮想マシンで実行していますので、本番環境よりもさらに遅くなっていますが、どこで遅くなったかを把握するにはちょうど良い事になります。

最後に

プロファイルを行うことによりどこが遅いのかが一目瞭然でわかったのと、使っていないデータまで送ると遅くなる事がはっきりしました。

開発をしていると、面倒だからといってデータ取得の便利メソッドを使ってデータを取得し、そのままクライアントに返却することをやってしまうのですが、そういう事をすると最後にしっぺ返しが返ってくる良い例でした。

Seleniumテスト環境設定時の注意点

はじめに

株式会社イメージ・マジックの技術ブログ、今週の担当のsoenoです。

二週間以上前に買うと決めたものを未だに買えていません。
日常生活においてはどうでもいいことこそ、タスク管理が重要なのかもしれません。

さて、今回はSeleniumのテスト環境の設定時に気を付けることを書こうと思います。


Seleniumとは。

webアプリケーション向けのテスト実行ツールです。
GUIを通した操作を自動で行わせることができます。

名前にまつわる注意点

Selenium~といった名前が結構いろいろ出てきます。
細かく書いていくとかえってやこしくなるので実例は省略します。

調べた結果が何かおかしいといったときは手元のSelenium~と参照している資料のSeleniumが同じものかを確認するといいかもしれません。(バージョンなど)

selenium builder

「GUIから操作してテストを書き出せる。」というものだったらしいのですが、
今はもうないです。

同様の機能を提供するSelenium IDEというプラグインもありますが
2019年1月の時点でスクリプトの書き出し機能はいません。

Selenium IDE は書き出しはできないとはいえ、自動化時にどのように動くかや、
web Driverで使用するターゲット名の確認ができる点で有用だとおもいます。
記録と再生のみならばこれだけでも十分かもしれません。

PhantomJS

非表示で実行されるブラウザ、非表示軽量のため本などでseleniumとセットで使われているのですがこちらもメンテナンス終了とのこと。
https://groups.google.com/forum/m/#!topic/phantomjs/9aI5d-LDuNE

Headless Chromeに切り替えるか、Headless FireFoxにするか、
もしくはメンテナンス終了ということを考慮しつつphantomjsを使うか等の判断がいります。

その他

環境設定時にプラグイン間のバージョンが整う必要があります。
バージョンがおかしいというエラーが出たら解消します。

久しぶりに触ると忘れていたりするのがヘッドレスブラウザの起動
テストの前にwebDriverTypeとして指定したブラウザを起動します。

最後に

GUIのテストは日々更新されるブラウザを対象とします。
環境にもまだまだ変化がありそうです。
その時に今回は待ったことで再びはまらないように気を付けたいと思います。

WordPressのセキュリティ

こんにちは、岡野です。
最近、当社で使用しているWordPressについてセキュリティ向上を図る案件があったので紹介します。

まずOWASPに載っている以下の様な事を実施しました。

・allow_url_fopenの禁止
この設定はファイル名にURLを指定できなくする物です。

・disable_functionsで、コマンド実行系(exec()など)の関数を禁止
この設定はOSコマンドインジェクションの防御には結構有効で、後述のAppArmorと組み合わせるとより効果的です。

・WP Security Audit Logプラグインをインストールし、操作履歴を保存
これは何か問題が生じた際の調査に使用します。

また、あらかじめ指定したPHPファイル以外はWebアクセスできない様にしています。

その他、FPMからDB接続ができなくなるため現在調整中ですがAppArmorも設定予定です。

WordPressもスマホアプリの様にプラグインやテーマ毎に細かな権限設定ができればと思いますが、PHP自体にその様な機能が無いため難しいかもしれません。

大量の情報から必要なものを探す(メール編)

こんにちは。イメージマジック三浦です。
昨年、月に1~2回やっているフットサル用に、長袖のトレーニングウェアを買いました。フットサルは屋外でやることが多いのですが、肌が直接外気に触れないだけで体の冷え具合が全く違うので、寒い時期の運動にはありがたい存在です。

今回はメールの中から必要な情報を取り出す、ということをテーマにして投稿します。

【システムからの通知メール】

弊社で運用しているシステムからは、日々多くのメールが送信されてきます。
多くは定時のバッチやタスクが「異常なし」で動作したことを通知してくるメールですが、 時々異常を通知するメールが送信されることがあります。
異常を感知した場合は、内容を確認して必要な対応を取らなくてはいけません。

通知メールは1通ずつ見ていくのが大変なくらいに送信されてきますが、 異常がない時に「異常なし」と通知してもらうことも重要です。「問題なし」の通知を止めてしまうと、異常がなくて通知がないのか何らかの異常で通知できないのか判断しにくくなります。

そこで、送信されてきたメールの中からエラーを通知するメールを取り出すことを考えます。

【キーワードの選定】

ここではシステムからの通知メール本文に
「error: x」(x = 1から9の整数)
という文字列があれば、何かしらの異常があるものと判断します。
異常が1件でも10件でも、「error: 1」という文字列で検索できるということです。

【どうやって抽出するか】

正規表現による検索が使えるメーラーの場合、 上記の規則性をカバーできる正規表現で条件を設定すれば簡単です。 しかし、検索に正規表現を使えないメーラーの場合は悩みます。
※普段の業務ではgmailを利用していますが、gmailは正規表現が使えません。

【実際にやったこと】

gmailでは、必要な分だけOR条件をつなげる方法が使えました。
“error: 1” OR “error: 2” OR … (9までつなげる)
検索条件のテキストを別ファイルに保存しておいて、gmailフィルタのラベル添付機能と組み合わせると、使いやすい状態になりました。

【まとめ】

今回は、大量の情報から必要な情報を探すという観点で書きました。
目の前にある情報をどう整理するかという観点と、どういう情報があれば整理しやすいかという観点の両方を忘れないようにしたいと思います。

axiosで渡したパラメータをSymfonyで受け取る方法まとめ

はじめに

あっという間に年の瀬で、もうすぐ入社から10か月の廣田です。

入社時よりは多少知識はつきましたが、他の記事を読むとハイレベルだな~、まだまだだな~と感じ入ります。

とはいえ難しいことは語れないので(汗)、今回は毎回調べてしまうaxiosとSymfonyのやりとり(SymfonyのRequestクラスの使い方)について書こうと思います。

開発本部で使っているjavascriptフレームワーク、Vue.jsでサーバーとやり取りする際にaxiosを利用していますが、その際のaxiosの書き方やSymfonyでパラメータを受け取る時の方法が、いつもなんだっけ?となるので備忘がてらまとめました。


Routeにplaceholderを設定しているとき

SymfonyでRouteを以下の例のように設定している場合、GET/POST送信されたパラメータをコントローラの引数として取得できます。

単純な値を1、2個渡すだけならこの方法を使ったほうが良いと思います。(URLの見た目もスッキリします。)
▼javascript側
const url = Routing.generate('app_find_prdct', {id: this.id});
const res = await axios.get(url);
if (!res.data.success) {
   EventBus.$emit('simple-modal_alert', 'エラー', res.data.errorMessages, callback);
   return;
}
//うまくいったときの処理
・・・
  ▼PHP側
  /**
     * @Route("/findProduct/{id}", name="app_find_prdct", options={"expose"=true}, methods={"GET"})
     * @param ProductRepository $pRepo
     * @param int|null $id
     * @return JsonResponse
     */
    public function findProduct(ProductRepository $pRepo, ?int $id ): JsonResponse
    {
        $p = $pRepo->findById( $id );
        if( !$p ){
            return $this->json(['success' => false]);
        }
        return $this->json([
            'success'  => true,
            'product' => $p
        ]);
    }
Routeでプレースホルダーに{id}とすることでコントローラの引数$idでaxiosが渡したパラメータを取得しています。

SymfonyのRequestクラスを使う

プレースホルダーの指定をしないときはSymfonyのRequestクラスを使います。

use Symfony\Component\HttpFoundation\Request;
public funtion someFunction(Request $request) {
     ...............
//GETの時
$id = $request->query->get('id');
//POSTの時
$id = $request->get('id');

//JSON形式でPOSTされた値を取得する時など
$content = $request->getContent();
['id' => $id] = json_decode($content, true);

}
axiosで渡すパラメータの値がJSON形式の時や、パラメータが複数の時はRequestクラスのgetContentを使うと、POST/GETされたそのままのデータにアクセスできます。
さらに、その値をPHPのjson_decodeを利用して連想配列形式で取得したりできるので便利です。

※参考:Symfony Routing
    generate_url_javascript
    HttpFoundation Component

さいごに

初歩的なところですが、当然のように使っていたので文章にまとめてみると理解が深まったような気がします。
記事を書く順番が来るとネタを決めるまで悩みますが、振り返る良い機会になりますね^^

冬休みまで指折り数える毎日ですが、それまで体調管理に気を付けて頑張りましょう!
ではでは。

DLL+EXEからDLLの使い方を解析する[前編]

はじめに

イメージ・マジックの安藤です。
先日から季節が急速に真冬の様相を呈していますね。風邪やインフルエンザが流行りだす時期なので皆様におかれましても十分ご用心ください。ちなみに、私は去年の大晦日から今年の三が日にかけてインフルエンザを発症するという何とも言えない新年スタートを切っているので気を付けます。
今回は題名の通りDLLの解析をしたという話です。

DLLとは

多分この説明要らないんじゃないかなぁと思いつつ書いておきます。
DLLとは、プログラムのある機能を再利用できるようにプログラム本体と切り分けたライブラリのことを指します。Windowsならば.dll、Linux系では.soという拡張子が使われます。
なお、今回の話はWindowsの.dllで、一般的な、Cで記述されたDLLという話です。

コンパイルを通すのに必要なもの

DLLの利用方法には暗黙的リンクと明示的リンクが存在します。今回は完全に個人的にDLLを使いたいだけなので暗黙的リンクでコンパイルするものとします。
以下が必要になります。

  • インポートライブラリ(.lib)
  • ヘッダファイル(.h)
更にメタ的に言うとこうなります。

  • エクスポートされている関数名
  • 関数の引数と型

初期状態

DLLがどんな状態で使われていたのかを少しだけ書いておきます。
DLLとDLLの機能をラップしたEXEがあり、EXEに引数を渡して実行してあげるとDLLの機能が使えるという構成になっていました。最終的にはこのEXEを介さずにDLLの機能を呼び出すのがゴールです。

インポートライブラリを用意する

第一の関門として、インポートライブラリが存在していません。ただ、これに関してはVisual Studioを使えばDLLから同等のものを用意することができます。
まず、以下のコマンドを実行します。

dumpbin /exports hoge.dll


C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools>dumpbin /exports VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\c1.dll
Microsoft (R) COFF/PE Dumper Version 14.16.27024.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64\c1.dll

File Type: DLL

  Section contains the following exports for c1.dll

    00000000 characteristics
    FFFFFFFF time date stamp
        0.00 version
           1 ordinal base
           3 number of functions
           3 number of names

    ordinal hint RVA      name

          1    0 000DDDB0 AbortCompilerPass
          2    1 000DDE50 CloseTypeServerPDB
          3    2 000055FC InvokeCompilerPassW

  Summary

       32000 .data
       17000 .pdata
       F3000 .rdata
        7000 .reloc
        1000 .rsrc
      138000 .text

このコマンドでDLLからエクスポートされている関数の一覧がテーブル上に表示されます。MSVCに存在する適当なDLLを表示してみます。 ここでordinal~nameの列に注目すると関数名が分かります。この関数名を以下のように羅列し、拡張子を.defで保存します。これはモジュール定義ファイルと呼ばれるものです。

EXPORTS
AbortCompilerPass
CloseTypeServerPDB
InvokeCompilerPassW

作成したモジュール定義ファイルは以下のコマンドでインポートライブラリに変換できます。なお、64bitアーキテクチャを想定しています。

lib /DEF:hoge.def /MACHINE:X64 /out:hoge.lib

ヘッダファイルを用意する

ヘッダファイルでは以下のように書くことでDLLの関数を宣言できます。

#define DLLImport __declspec(dllimport)

#ifdef __cplusplus
extern "C"{
#endif

    DLLImport void funcName1();
    DLLImport int funcName2(int arg);

#ifdef __cplusplus
}
#endif


以上を見るとわかりますが、関数の戻り値と引数がわからないとDLLの機能は使えないことが分かります。

以降、今回のケースで関数定義をどう解析したかは次回の私の番で書いていきたいと思います。

「ブラックマーク」は縁の下の力持ち

はじめに

矢野です。小石川オフィスに引っ越してきて、1か月あまりが経ちました。小石川や、それに近い西片・本郷界隈は起伏が激しくて入り組んだ道が多く、引っ越し直後はランチの後にちょっと散歩するとすぐに迷子になっていたものです。それでもだんだんと脳内の地図が整理されてきて、かなり歩きやすくなってきました。

ブラックマークって何?

ちょっとググってみると、「ドリフト走行などで路面にできるタイヤの黒い跡」とか「汚点」とか、今年公開のそういうアクション映画があるとか、色々出てきます。ですが、私たちが扱っているブラックマークとは、「印刷用紙の裏側の黒い印」のことです。

ブラックマーク

ブラックマークの例。用紙を横切るような帯状のものや、紙面の中央に四角く描かれたものなど。

生産管理に使用する小さなラベルや完成品をお届けするための配送伝票などのシールの台紙となる剥離紙の裏側に印刷されている黒い印や帯のことです。シールとして貼る際に捨てられてしまうので、お客様の目に触れることはありません。いわば縁の下の力持ちですね。

何のために使うの?

私たちが取り扱うラベルプリンターには印字ヘッドの近くに光学式のセンサーが内蔵されていて、それで台紙の裏側をスキャンできます。ブラックマークを検知するまでは用紙を送り出して、検知したところで位置合わせ。そこから印刷を始めて、1枚分印刷できたら台紙ごとカットする(これもプリンターに内蔵されたカッターがやってくれます)。1枚ごとの長さが決まっているロール式のラベルを正確に取り扱うための、重要なマークです。

と、ある程度取り扱えるようになってから言うのは簡単ですが、プリンターと用紙がそれぞれ専用の組み合わせでない場合は、用紙の向き、サイズ、光学センサーの位置、検知の方式(透過式? 反射式?)、マークからの相対的な位置の設定などなど、いろいろな苦闘がありました。用紙のフィードがいつまでも止まらなかったり、おかしなところでちょん切られたり……。

形のないソフトウェアの世界と、プリンターやRFIDリーダーなどのハードウェア、印刷メディアなどの間を行ったり来たりしながら、私たちは開発の仕事をしています。

Doctrine2でMySQLのレプリケーションを設定する

はじめに

こんにちは、イメージ・マジックのもあいです。
弊社のシステムで、工場の生産を管理する生産管理システムがあるのですが、このシステムのデータベースはレプリケーションを行っており、読み取り専用のスレーブが存在します。
Symfony 4.1で生産管理のWeb開発を行っているのですが、レプリケーションの設定で調査を行った結果を記載したいと思います。

Doctrine2について

Symfony 4.1で使用するORMなのですが、Symfony専用というわけではなく、汎用的に使えるORMです。Doctrineの意味を調べてみるとちょっとお堅い感じの意味が出てきたのにはびっくりしました。

マスター/スレーブ構成とは

MySQLでは大分昔のバージョンから使えますが、一つのマスターDBと複数のスレーブDBからなるシステムで、スレーブは基本的には書き込みができません。スレーブDBに参照クエリを集めることにより、マスターDBの負荷を分散させることができるため、システムのスループット向上が見込めます。

Doctrine2の設定

doctrine.ymlにSlave設定を追加するだけで、現在のバージョンでは使用することが可能です。
slavesの配下にslaveのDB設定を追加すれば、スレーブを増やすこともできます。
現在のDoctrine2(2.5)ではこれだけです。

        connections:
            default:
                driver: 'pdo_mysql'
                charset: utf8mb4
                default_table_options:
                    charset: utf8mb4
                    collate: utf8mb4_general_ci
                    row_format: dynamic
                dbname: 'dbname'
                user: 'db_user_name'
                password: 'db_user_password'
                host: 'db_master_host'
                port: 3306
                slaves:
                    slave1:
                        charset: utf8mb4
                        dbname: 'dbname'
                        user: 'db_user_name'
                        password: 'db_user_password'
                        host: 'db_slave_host'
                        port: 3306

これだけで、selectはスレーブ、update/insertはマスターを参照するようになります。

セレクタの詳細度の概要と(とデバッガ?)の話

はじめに

株式会社イメージ・マジックの技術ブログ、今週の担当のsoenoです。

オフィスが移転し二週間がたちました。

文京区は神田に比べて子供の姿をよく見ます。

少なくとも神田でベビーカーを見た記憶がないのでしみじみと地区による差を感じています。(さすがに記憶にないだけですれ違ったりはしていたのだと思いますが。)

さて、今回はcssのセレクタの詳細度の概要(とデバッガ?)について書きます。

CSSが反映されないとき。

cssを触りだすと、反映してほしい指定が来ないことがあります。

(赤くなってほしいところが赤くならない等々。)

大体そういう時は

・その指定を反映させるための条件が足りていない。

・よその指定が反映したい指定を打ち消している。

あたりだったりします。

そんなときはCSSがimportantまみれになる前に要素の指定の方法を見直していきます。

状況の確認方法(chrome ディベロッパーツール)

選択中の要素にどんな指定がされているかは下記の手順で確認できます。

・調べたい要素を右クリック。
・出てきたメニューから検証を選択。
・ディベロッパーツールが要素を指定した状態で開きます。
(選択中の要素はelements内をクリックするなどで変更可能。)
・右のエリアのstyleの項目に表示されているのがその要素についているcssです。

※手順の記載の都合でchromeで書きましたが大体のブラウザで同様のことができます。

ディベロッパーツールで要素を選択しているのにかけたはずの指定が見つからないときはcssのリンクや指定で間違いがあるかもしれません。スペルの間違いや、ファイルのパス等そのほかの部分が違う可能性の方が高そうです。

指定した項目に打消し線(文字にかぶさる横線)が入っていて反映されないという場合。おそらく詳細度で何とかできる問題です。

重複した指定と詳細度の関係

cssではそれぞれ数値が割り振られており、それを合算した結果でcssで使われる指定が決まります。

割り振られている値を詳細度と呼びます。

ですので設定したい項目が何かに打ち消されている場合は打ち消している項目よりも反映させたい項目が詳細度が高くなるように指定すれば解決します。

厳密な指定がいる場合は詳細度の数値、計算方法について調べてみてください。

計算するサイトや、詳しいサイトありますのでいろいろ見てみるのがいいと思います。

また、厳密な計算が必要なほど込み入っているのであればスコープや設計を見直たほうが早いかもしれません。

要素の詳細度について

さて、以下詳細度が高い順に記述すると

1.important

記述例

css

.btn-red{
  background:red !important;
}

2.htmlのタグに直接書かれるスタイル

記述例

html

<div style="background:red"></div>;

3.idにあてられたスタイル

記述例

html

<div id="red-btn"></div>;

css

#red-btn{
   background:red !important;
}

4.クラスへあてられたスタイル

記述例

html

<div class="red-btn"></div>;

css

.red-btn{
  background:red !important;
}

5.その他

疑似要素などありますがこの辺りの衝突はあまり見ないのと、大体他のクラスにつけて記述することが多いと思いますので省略します。

尚、同じ詳細度の指定が複数個所にあった場合は後に読み込まれる方が優先されます。

一応優先度の反映のされ方をざっくり書くと

優先度が高いものが反映されますので、importantが赤といえば他が何を言っても赤です。

importantがいないときはタグに書かれたスタイルが優先されます。

importantの指定もタグの指定もなければidの指定が優先されます。

そのすべてがいないときはclass、classさえいないときはその他疑似要素等への指定が使われます。

ここまで把握したら反映させたい項目を邪魔している指定を見てみます。

詳細度が反映させたい要素より高いはずです。

自身の詳細度を高める、相手方の詳細度が不必要に高いときは下げるなどして調整すれば正しく反映されます。

終わり

詳細度についていろいろ書きましたが、厳密に調整しないといけない状況はなかなか大変なので設計で何とかできるようになりたいです。

補足(Chromeディベロッパーツール)

ディベロッパーツールのstyleではスタイルの左に表示されるチェックボックスのオンオフでスタイルの着脱ができます。
衝突している指定についてブラウザ上で相手側の指定をオフにして試してみることができます。
同じスタイルの指定が複数個所にされていて反映されない場合はディベロッパーツールであらかじめみることができます。

オリジナルプリントのシステム構成

こんにちは、岡野です。
今日は当社サービス「オリジナルプリント」のシステム説明をします。
近々サーバリプレースする予定で下の様なシステム構成です。

 

オリジナルプリントのシステム構成

オリジナルプリントのシステム構成

 

特徴として、OSは全てUbuntu18、DBはMariaDB、Nginx/PHP/ImageMagickはほぼ最新版です。またimage/batch/DBサーバにはNVMeのディスクを搭載しています。

ベンチマークを取ったメモが今見当たらないのですが、ImageMagickは色深度を16bitから8bitへ変えて確か3割速くなりました。また、ImageMagick用の一時領域としてNVMeのディスクを割り当てた結果、巨大な画像処理がSSDの時と比べ数倍速くなりました。

まだproxyサーバに静的ファイルを置くなどの対応ができていないのですが、今後も一層の高速化/安定化を目標に改善していきたいと思います。