Thinreports の PHP版を利用している人への注意喚起(レイアウト編集できなくなるかも)
普段、PHPを使って作られたアプリケーションを運用しています。
数年前に、帳票出力のレイアウトをビジュアルで定義可能なPDF出力ツールを探していた時、Ruby用のPDF生成ツールである Thinreportsを見付けました。
- Thinreports
デザインは、Thinreportsから提供されているChromeアプリで編集可能なところが良いです。Thinreports のPHP版があったので、これを使うことにしました。
ただし、PHP版には注意点があって、0.8系のChromeアプリで編集したテンプレートにしか対応していません。0.9系でテンプレートファイルの形式が変わったようで、PHP版は追随していないような状況です。仕方なく古い0.8系のChromeアプリを、無理矢理使って編集することで、thinreports-php を使うことにしました。
数年後
久しぶりに、新しい帳票のテンプレートファイルを編集するために、Chromeアプリを開こうとしたところ、下のダイアログが表示されました。
古いバージョンのChromeアプリをWindowsデバイスで使用できるのは、2022年12月までです。新しいバージョンが利用可能かどうかは次の手順で確認できます。
なんということでしょう。帳票のデザイン編集がビジュアルで定義できなくなるとかなり困ります。しかも猶予が無い。
Thinreports と Ruby と PHP の関係
Ruby版とPHP版が関連してそれぞれの位置関係が結構複雑なので、図にしてみました。
対策を見付けるまでの紆余曲折
本家のThinreportsがどうなっているかというと、レイアウト定義のビジュアル編集ツールが、Chromeアプリではなく、 thinreports-basic-editorという実行ファイル形式で配布されるようになっており(詳しく調べてないですが更に thinreports-section-editorというエディタも出ているようです)、Chromeアプリの問題とは無関係でした。
現状も運用中のPHPアプリケーションで、数多くの帳票の定義を、Chromeアプリである Thinreports Editor 0.8 を使って定義してるので、なるべくThinreport以外に移行するという解決策は避けたい。実行ファイルで提供されるthinreports-basic-editorで編集できるようになる形に収めたいです。
thinreports-basic-editorで編集するということは、0.9系以降のテンプレートファイルが出力されることになるので、まずは thinreports-php が、0.9系以降のテンプレートファイルの形式に対応した物が公開されていないかを調べてみました。
9.0系のテンプレートファイル形式対応版のthinreports-php
既に対応された方が居ました。
tweetしていたら、上記リポジトリオーナーの@nullx2さんから情報を頂きました。
一年前くらいの時点のeditorの機能は概ね対応しました。ただ、IVSの都合でフォントの強制置換が組み込まれており、一般的な用途でそのまま利用できるかは怪しい感じですね。
— ぬるぬる (@nullx2) October 20, 2022
実は、thinreports-phpに support-new-schema というブランチがあるのでそこをベースに改修してみることも検討しましたが、
ブランチが当時のままなので、新スキーマに対応し始めたくらいの内容ですね。
私もこれに期待して(ろくに調査せず)採用&開発を始めて痛い目を見ました。 旧様式でも良かったのですが、0.9以前だとListレイアウトが使えなかったので改修し、GNUの義理を一応果たして公開した次第です。 — ぬるぬる (@nullx2) October 20, 2022
新しいthinreports-basic-editorを使いたいなら、rubyを使えばいいじゃない
新しいthinreports-basic-editorは、Thinreports(本家Ruby版)のための帳票レイアウト編集ツールです。なので、PHPの世界で閉じて帳票を作ることを諦めて、本家Ruby版を使ってPDFを生成する事を検討しはじめました。具体的には、入力情報はPHP側で準備して、どうにかしてRuby側でPDFを生成して、その結果のPDFファイルをPHP側に戻す、という形です。
PHPのアプリケーションから、PDF生成部分を切り出す方法はいくつか考えられます。
- PHP実行中のサーバーにrubyをインストールして、外部コマンド呼び出しでThinreportsでPDF生成する。(PHPのサーバーの環境を複雑化させたくない)
- ↑の問題を解決するために、rubyのThinreportsでPDF生成するコマンドをシングルバイナリ化して、それをPHP実行中のサーバーに配置して、外部コマンド呼び出しでPDFを生成する。(rubyをインストールする必要がない)
- Amazon Lambda等で、rubyのThinreportsでPDF生成する。(ファイルはS3でやりとりする方法を取る必要がある。余計なオーバーヘッドが発生する)
それぞれメリット/デメリットは思い付くけど、rubyのThinreportsでPDF生成するコマンドをシングルバイナリ化する方法ができれば、それが良さそうという現状の感想でした。(実現できるのか確信は無い)
rubyのThinreportsでPDF生成するコマンドを用意する
まずは、rubyのThinreportsでPDF生成するコマンドが存在していてくれないかと希望を込めて探しはじめました。
一瞬、求めているものがある!と思ったのですが、現状では、0.8系のテンプレートファイルを、0.9系以降のテンプレートファイルに変換するコマンドが実装されているのみでした。
- Implement
thinreports generate --json /path/to/data.json /path/to/result.pdf
command
PDF生成機能は、Planのところに記載されていましたが、実装はされていないという状態です。
フォークして、最低限のPDF生成機能を追加してみました
rubyスクリプトを書くのは、ほとんど初めてのような状態でしたが、元のコードがコンパクトですっきりしていたので、最低限やりたい事(PDF生成機能の追加)は、簡単でした。PDF生成と言ってもThinreportsの機能を呼び出すだけなので。
可能であれば、Pull Requestも送ってみたいです。
RubyのCLIのシングルバイナリ化
PHPサーバの環境を汚さずに、PDF生成コマンドを導入するために、シングルバイナリ化をしたいと思い、ここに書き切れない程の試行錯誤をした結果、意外と簡単にできました。
Linux環境でシングルバイナイ化するツールは、ruby-packerを使いました。しかし、dockerのUbuntu環境でrubycの実行がうまくできなくて苦労しました。何度試してもopenssl関係のエラーが発生する。一つクリアしても次のopenssl関連のエラーが出るという状況です。openssl関連で最後まで苦戦してたのですが、dockerhubで公開されている https://hub.docker.com/r/mattipaksula/rubyc を使うとPDF生成機能を追加したthinreports-cliのシングルバイナリ化に成功しました。
PHP側の外部コマンド呼び出し部の作成
これは特に問題なく実装できて、PHPアプリケーションから、シングルバイナリ化したPDF生成機能を呼び出して、帳票をダウンロードできることが確認できました。
まとめ
今回問題を見付けてから解決するまでに、いくつかの大きめの課題をクリアしなければならず、紆余曲折したので、経緯を記録してみました。その結果書いたことが無い程の長文になってしまいました。(しばらく校正します。疲れたので公開)
実は、まだこのソリューションを本番環境に適応した訳ではないので、実運用で問題等があれば、追記していく予定です。
2022/10/27 追記
シングルバイナリ化したPDF生成機能を使って、業務で使用する背景画像が含まれる帳票を出力したところ、約10秒かかることがわかりました。サーバ環境の単純さを求めて苦労してシングルバイナリ化したのですが、このままでは利用に難があるので、シングルバイナリ以外の場合のPDF生成時間を調査しました。高速化を期待して、ruby3 と、JITを有効にしたruby3 も含めて比較してみました。
- シングルバイナリ版(ruby2.4) 9.9秒
- インストール版(ruby2.4) 6.7秒
- インストール版(ruby3.1.2) 7.4秒
- インストール版(ruby3.1.2 MJIT) 5.9秒
- インストール版(ruby3.1.2 YJIT) 4.7秒
シングルバイナイ版から比べて、ruby3.1.2 のYJIT版は、約半分の時間でPDF生成できるようになりました。PHPのサーバーにrubyを入れることによる環境の複雑化を懸念していましたが、おとなしくPHPのサーバーにrubyを入れることにしようと思います。YJITのパワーが予想以上にすごかった。