PHPカンファレンス福岡2019で「マネクラからの挑戦状」というイベントがありました、現地ではマシンを持っていってなかったためやってませんでしたが、おうちでやってみました。
マネクラからの挑戦状 Webサイトコードゴルフ pepabo/lolipop-mc-codegolf-challenge
私の戦闘力スコアは、58655です。開催後なので非公認スコアですね。↓↓↓↓↓↓↓↓↓↓↓
phpconfuk2019-codegolf https://github.com/pepabo/lolipop-mc-codegolf-challenge
コードゴルフのルール
ロリポップ!マネージクラウド上に、指定のソースコードから生成されるWebサイトを設置して、それのレンダリング結果を変えずにコードを修正していき、公開ディレクトリ内のファイルサイズを小さくしていくというルールです。詳しいレギュレーションはソースコードのREADMEに書いてあってこんな感じ
## レギュレーション
- PHPで作成されたサイト https://phpconfuk-codegolf-php.lolipop.io/ (正解サイト)のソースコードをお渡しします(このリポジトリです)。
- サイトの **表示を一切変えずに** Webサイトを構成する全コード全ファイルの合計ファイルサイズを小さくして、あなたの[ロリポップ!マネージドクラウド](https://mc.lolipop.jp/)の `PHPプロジェクト` にデプロイしてください。
- 最も小さいファイルサイズになった人が優勝です
- コンテナ内の `/var/www/html` 内にWebサイトを表示するのに必要な全てのコード、およびファイルを設置してください
- `/var/www` 等への設置は禁止です
- Makefile内の `check` タスク内のコマンドの変更は禁止です
-
pepabo/lolipop-mc-codegolf-challenge#レギュレーション
どんなふうにすすめたか
私はこんなふうにコードを小さくしていきました。
- とりあえずそのままデプロイして表示確認
- チェックスクリプトを確認
- コードを追って表示に使っているファイルを確認
- composer依存を排除
- テンプレートを圧縮
- DB依存を排除
- 画像ファイルを最適化
- HTMLから不要CSSスタイルを除去
- HTMLを短縮化
チェックスクリプトを確認
まずはスコア算出はどのようにしているのか確認します。スコア算出に使用されるコマンド make check
で実行されるのはこんなコマンドでした。
check:
@ssh -p ${SSH_PORT} ${SSH_USER}@${SSH_HOST} 'find /var/www/html -type f -not -iwholename "*/.git/*" -not -name "Makefile" -not -name ".env" | xargs cat | wc -c'
サーバー上の /var/www/html
ディレクトリから、.git/
Makefile
.env
を除外してそのファイルサイズ合計を求めています。
※ここで.git/
に表示ファイル全部入れればいいじゃないって気付いた勘のいいお子様はキライだよ
コードを追って表示に使っているファイルを確認
表示開始ファイルである index.php
から順に読んでいって使用しているファイルを確認しました。
初期状態で表示に使用しているファイルは
- .htaccess
- index.php
- vendor/* (依存composer ライブラリ一式)
- model/token.php
- view/home.liquid
- mc.png
ということがわかりました。
それ以外で /var/ww/html
にデプロイされているファイルは不要なので make deploy
コマンドを修正してデプロイ時に削除するようにします。
.htaccessファイルについても中身はdisplay_errorsをonにしているだけなので表示には不要と判断しました。
インストール手順で、composer.phar
をカレントディレクトリに配置する手順が書いてあり、composer.pharもデプロイされていたのでこれだけで結構減らせますね。
composer依存を排除
composer.jsonを見るとこのスクリプトは以下のライブラリに依存していることがわかりました。
"morris/lessql": "^0.4.1",
"vlucas/phpdotenv": "^3.4",
"league/route": "^4.2",
"zendframework/zend-diactoros": "^2.1",
"zendframework/zend-httphandlerrunner": "^1.1",
"liquid/liquid": "^1.4"
それぞれ、以下のようなライブラリです。
vlucas/phpdotenv
.envファイルのパースleague/route
,zendframework/zend-diactoros
,zendframework/zend-httphandlerrunner
HTTP/ルーティング関連ライブラリmorris/lessql
SQLライブラリliquid/liquid
テンプレートエンジン
morris/lessql
は素のPDOを利用した書き方に変更して依存を外します。
liquid/liquid
もテンプレートファイル(view/home.liquid)の {{ reversed }}
という文字列を置き換えているだけなので、プレーンなPHPで置換可能です。
リクエスト周りで使用している league/route
, zendframework/zend-diactoros
, zendframework/zend-httphandlerrunner
は、レギュレーションのチェッカーからのリクエストを見たところ、単純にGETリクエストが送られて来ていただけのようでしたので、依存を排除しました。
vlucas/phpdotenv
は、.envファイルをパースして環境変数に入れてくれるものですが、ロリポップ!マネージクラウドのコントロールパネルにはインスタンスに環境変数を設定できる機能があったので、そちらから環境変数を指定するようにして、依存を排除しました。
テンプレートを圧縮
表示テンプレートである view/home.liquid
を見ると結構なファイルサイズと無駄なCSSスタイルが仕込まれていることがわかりました。
CSSスタイルを除外していくのは時間がかかると判断し、テンプレートを圧縮した状態でアップし、実行時に展開して使用すればいいと考えました。
マネージクラウドのインスタンス上で、php -m
コマンドを叩いて使える拡張モジュールを確認したところbzip2拡張が使えるようだったのでbzip2で圧縮するようにしました。
bzip2コマンドで圧縮したファイルを伸張するのは bzdecompress
と file_get_contents
で簡単にできます。
bzdecompress(file_get_contents('v.bz2'));
DB依存を排除
ここではたと気付きます。この表示ロジックにDBは必要ないんじゃねと。
元々のロジックは以下のようなものでした。
- tokensテーブルの全レコードを削除
- クエリストリングとして入力されたキーと値をtokensテーブルに格納
- 値降順、キー降順でソートしてDBからレコードを全件取得
- 取得したレコードから3番目の値があればその値を逆から表示する、3番目の値がなければ空文字を表示する
要は、クエリストリングをキーと値でソートして、3番目の値を返せばよいのです。
というわけでこんな感じのコードになりました。
$p=$_GET;
krsort($p);
arsort($p);
$v=array_values($p);
strrev($v[2] ?? '');
もとのSQLのソートがvalue DESC, key DESC
でしたので、キー→値の順にソートを掛けることで値のソート順が優先されるようにします。
krsort
が与えられた連想配列をキーの逆順で並べ替える関数、arsort
が与えられた連想配列を値の逆順で並べ替える関数ですね。
array_values
で値だけの配列を取り出して、最後にPHP7から使えるnull合体演算子 ??
で、クエリストリングに2つ以下の値しか与えられなかった場合の対処をしてから、文字列を反転する関数 strrev
に与えます。
画像ファイルを最適化
上記の最適化でコードが減り、画像ファイルのサイズがかなりのウェイトを占めるようになったので画像の最適化をしてみました。
ImageAlpha — image minifier (like JPEG with transparency!) とかを利用して画像を最適化しようとしましたが、アルファチャンネルを無くしたり色数を減らすとチェックに通らなくなったため画像の修正は断念しました。
それでも、なんやらかんやらで 59750 → 57990 に減らせました。
HTMLから不要CSSスタイルを除去
テンプレートファイルをみると明らかに使用していないスタイルが記述されていたので適用されているスタイルだけにクリーンアップしました。
HTMLを短縮化
html
とかhead
とかbody
とか属性の"
などブラウザが補完してくれるタグを取っていきます。どんどん壊れていくHTMLを見てソウルジェムが黒く濁っていく気分を味わえました。
まとめ
以上のような感じでコードを半日ちょっと掛けて最適化した結果が↓になります。
このチャレンジをやってみて、わりとモダンなコードをどんどんクソコードプレーンなPHPコードにしていく感じが溜まりませんでした。(汚れきった大人になった感)
また、ロリポップ!マネージクラウドを使うのは初めてだったので、これを通してコントロールパネルの感じや実行の感じを体感できてよかったです。よい問題をありがとうございました。
参考に @tenkomaさんの解答例もどうぞ PHPカンファレンス福岡2019 マネクラからの挑戦状 Webサイトコードゴルフ スコア60230(非公認)の解説 – Engineer as a Lifestyle @tenkoma
他の人の解答例ももっと見たいので、やった方はTwitterなどで晒してもらえると嬉しいです。