PHPカンファレンス福岡2019 マネクラからの挑戦状 Webサイトコードゴルフをやってみた

標準

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で圧縮するようにしました。

PHP: Bzip2 – Manual

bzip2コマンドで圧縮したファイルを伸張するのは bzdecompressfile_get_contents で簡単にできます。

bzdecompress(file_get_contents('v.bz2'));

DB依存を排除

ここではたと気付きます。この表示ロジックにDBは必要ないんじゃねと。
元々のロジックは以下のようなものでした。

  1. tokensテーブルの全レコードを削除
  2. クエリストリングとして入力されたキーと値をtokensテーブルに格納
  3. 値降順、キー降順でソートしてDBからレコードを全件取得
  4. 取得したレコードから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 に与えます。

Null 合体演算子

画像ファイルを最適化

上記の最適化でコードが減り、画像ファイルのサイズがかなりのウェイトを占めるようになったので画像の最適化をしてみました。

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などで晒してもらえると嬉しいです。

コメントを残す

Page optimized by WP Minify WordPress Plugin