こもろぐ @tenkoma

What We Find Changes Who We Become -- Peter Morville著『アンビエント・ファインダビリティ 』

今契約しているサブスクリプションまとめ

今までまとめて把握できてない状態だった。 使わなくても良くなったATOKと、あまり使っていなかったesa.ioを解約したけど、サブスクリプション把握できてないなぁ、と思ったのでまとめてみた。

2020/09/16 現在 (過去1年の実績なので、ドル払いなどで変動もあります)

娯楽系

  • Spotify Premium(年間契約 ¥9,800) ¥9,800
  • Amazon Prime (年間契約 ¥4,900) ¥4,900
  • Netflix (スタンダード 月 ¥1,320) ¥15,840
  • Nintendo Switch Online (個人プラン 12ヶ月 ¥2,400) ¥2,400

生活ツール系

  • マネーフォワード (プレミアム ¥500) ¥6,000
  • Sleep Cycle (年間 ¥240) ¥240

開発ツール系

  • GitHub (sponsor 含む ¥1,452) ¥17,424
  • さくらのVPS ¥43,560
  • tenkoma.net ドメイン(年間 ¥1,886) ¥1,886
  • tenkoma.devドメイン (年間 ¥1,540) ¥1,540
  • JetBrains All Products Pack (3年目以降 ¥18,920) ¥18,920
  • DeepL Pro (Starter 年払い ¥9,000) ¥9,000

寄付系

  • Rebuild.fm スポンサー ¥10,332
  • Wikimedia への寄付(秘密)

その他

  • はてなブログ (はてなブログPro 2年コース¥14,400) ¥7,200

Work from homeが始まってから買ったもの

3月から1度もオフィスに行ってないです。

もとからデスク・オフィスチェアや外付けモニタはあったので、自宅で仕事出来る環境はありましたが、より快適にしたかったので半年でいろいろ買った。

EIZO FlexScan 27.0インチ ディスプレイモニター EV2785-BK

2台目のEV2785-BK。

2台あっても生産性は2倍にはならない。

右側にエディタを表示して左側にそれ以外を表示して使ってます。

Amazonで買った。

エルゴトロン MXV デュアル デスクモニターアーム

定評があるらしかったので。 机を壁に付けて使っているとアームの可動範囲がせまく、モニターが大分前面に来てしまうので調整したい。モニタと同じくAmazonで買った。

YubiKey 5Ci

Work from homeとは関係ないけど、GitHubフィッシングが話題になったときに、Amazonで買った。

吸気口フィルター

ずっと家に居るので、空気質が気になりはじめた。 空気清浄機を買おうと思ったが、24時間換気システムの吸気口フィルターを交換すれば改善できそうと思い、適合するフィルターをAmazonで買った。

買ったあとで、もっと性能の良いフィルターがあることを知った。

新フィルターTF-A-PG/PF<トレフィン>登場!! | 高品質フィルターのユニックスストア

ダイソン 空気清浄機能付 ファンヒーター Dyson Pure Hot + Cool Link HP03WS ホワイト/シルバー

結局空気清浄機をヨドバシで買ってしまった。

温風送風機能が付いているので、エアコンが故障したときのバックアップだよ(言い訳)

1日12時間稼働で1年でフィルター交換らしいが、 空気質が悪くなったら稼働するオートモードがあり、平均2時間も稼働してないので、 4年くらいは持ちそう。 アプリをインストールすると、稼働時間・空気質・湿度・温度のモニタが出来るので、むしろその機能をよく見ている。

掃除機も持ってるけど、ダイソンは使いたくなるデザインが上手いと思う。

ヨドバシ.com - ダイソン Dyson HP03WS [空気清浄機能付ファンヒーター Dyson Pure Hot+Cool Link ホワイト/シルバー] 通販【全品無料配達】

ロジクール HD プロ ウェブカム フルHD 1080p C920S

プライバシーシャッターつきなので。

ヨドバシ.comで買いました。

ヨドバシ.com - ロジクール Logicool C920s [ロジクール HD プロ ウェブカム] 通販【全品無料配達】

AKG LYRA USBマイク

配信とかはしない。

サウンドハウスで買いました。

AKG ( アーカーゲー ) >LYRA USBマイク 送料無料 | サウンドハウス

サンワサプライ エルゴノミクスフットレスト MR-FR1

足への負担が気にならなくなったので、効果あるんだろう。

ヨドバシ.comで買った。

ヨドバシ.com - サンワサプライ SANWA SUPPLY MR-FR1 [エルゴノミクスフットレスト] 通販【全品無料配達】

ロジクール Logicool MX Master 3

ヨドバシ.com - ロジクール Logicool MX2200sGR [MX Master 3 アドバンスド ワイヤレス マウス グラファイト] 通販【全品無料配達】

ヨドバシ.comで買った。

MX Master 2sにくらべると、スクロールが快適になった。

ノートPCクーラー

ヨドバシ.com - ミヨシ miyoshi NSF-03 [ノートPCクーラー スタンダードタイプ] 通販【全品無料配達】

ヨドバシ.comで買った。

7月に私物のMacbook Pro 2016モデルをバッテリー膨張で有償修理した。

底が熱くなるけど使えるか、と思っていたのを考え直して冷却台を導入。

光らなくても良いのに、と思ったが、ノートPCを載せればほぼ塞がるし、付けっぱなしで気づかないことが防げるかもなのでいいのかもしれない。

電源タップ 3m

サウンドハウスで2つ買った。

CLASSIC PRO ( クラシックプロ ) >電源タップ PDS8 3m 送料無料 | サウンドハウス

電化製品増えて足りなくなってきたので。

チェアマット

ヨドバシ.comで買った

ヨドバシ.com - ロアス LOAS RCM-007CL [チェアマット ハードフロア用] 通販【全品無料配達】

開発環境でOpcacheを有効にする

Docker Desktop for Mac の環境で動かしていたPHPアプリケーションが遅い(1リクエスト1.2〜1.4秒ほどかかっていた)。 ずっと、Docker Desktop for Macの Mount 機能は速度が出ないからだ、と思いこんでいて、最近知った Mutagenを試したけど、200ミリ秒ほどしか改善しなかった。そこでもしかしたら遅い原因は他にあるんじゃないかと思い、調査したところ、以下の2つを変更したらかなりレスポンスタイムが短くなった(300〜450ミリ秒になった)

  • Xdebugを無効にする(xdebug.so を読み込まない)
  • OPcache を有効にする

Xdebug については、リモートデバッグやプロファイラなど遅くなりそうなオプションを全部Offにしても300ミリ秒くらいは増えるので、拡張自体が重いのかも、と思っている。

開発環境でOpcacheは無効化されていたけど、開発環境向けにファイルを変更したら即時反映するよう設定出来るんじゃないかと思ってやってみたら、できそうだったので記事としてまとめる。

開発環境向けOpcache 設定

PHPスクリプトへの変更が即時反映されるための設定は以下の通り。

; Opcache を有効にする
opcache.enable=1
; PHPスクリプトのタイムスタンプをチェックして、更新されていたらオペコードキャッシュを作り直す
opcache.validate_timestamps=1
; タイムスタンプの再チェックを行う時間(秒)。0なら実行毎にタイムスタンプチェックする
opcache.revalidate_freq=0

Laravel 7.2 Hello, world アプリで測ってみた

フレームワークを使ったPHPアプリケーションでどのくらい効果があるかみるために、以下のようなサンプルアプリケーションを作ってみた。

composer create-project してHello, world ページを作っただけのLaravelアプリケーションがあるので、

git clone git@github.com:tenkoma/opcache-config-for-dev-sample.git
cd opcache-config-for-dev-sample
cp .env.example .env
docker-compose run composer composer install
docker-compose up -d
docker-compose exec php-fpm php artisan key:generate

して、http://localhost:8000/hello にアクセスしてテストできる。 初回のアクセスはキャッシュを作るので2回目以降のタイムが速くなる。 Hello, Worldのコードは以下の通り。

<?php
// routes/web.php
Route::get('hello', function () {
    return 'Hello World';
});

テスト結果は以下の通り(10回ほどリクエスト後に10回計測)

  • 1) Volumeオプション:デフォルト(consistent), opcache.enable=0: 平均 0.96s (min 0.84s, max 1.09s)
  • 2) Volumeオプション: cached, opcache.enable=0: 平均 0.44s (min 0.38s, max 0.48s)
  • 3) Volumeオプション: cached, opcache.enable=1 他上記設定を有効: 平均 0.03s (min 0.03s, max 0.04s)

(2) → (3) で 0.35s〜0.44s くらい速くなった。

おわり。

⛳ PHPer Code Golf by pixiv(PHPerKaigi 2020) 上級編の回答について解説

PHPerKaigi 2020 、お疲れ様でした!! PHPer Code Golf by pixiv で賞を頂いたtenkomaです。

イベントについてのエントリーは別途書くとして、Code Golf上級編で書いたコードの解説をします。 1問目はHello, World, 2問目はFizzBuzzなので詳しい解説はしません。

個人的にはCode Golfガチ勢では全く無いのですが、賞をもらってしまったので、コードを晒す義務が発生した気がします。 ちなみに会場のネットワークの混雑等のトラブルもあり、第3ホールの開催時間は1時間20分ほどだったようです。

出題概要

PHPerKaigi 2020参加者向けに用意されたこのサイトで、問題を解く形式です。サイトは停止するかもしれないので出題文を転載します。

配列を展開せよ

あるサイトにのAPIにリクエストを送ると、結果がとても変な形式のJSONで帰ってきてしまいます。

{"foo": "1", "bar[0]": "A", "bar[1]": "B", "buz[0][0]": "00", "buz[0][1]": "01"}

あなたの仕事はこの独特なフォーマットのレスポンスを常識的に綺麗な形状の配列に直すことです。

{"foo": "1", "bar": ["A", "B"], "buz": [["00", "01"]]}

結果の配列は json_encode() で変換して出力してください。

コード入力

<?php

declare(strict_types=1);

$input = json_decode(stream_get_contents(STDIN), true);

$converter = function (array $in) {
    // ...
};

// 最終的にこの変換結果が出力されるようにしてください。
// コード内の好きな位置に関数やクラスを定義しても構いません。
echo json_encode($converter($input));

問題文は以上です。

問題についての注意点

出力後のjson は所々スペースが入ってますが、json_encode()の出力ではスペースを入れることができなかったので、レギュレーションについてゴルフ場デベロッパーさん (@tadsan) / Twitterに問い合わせして、スペースを含まないjsonで正解することができるようになりました。

とりあえず正解にたどり着く

最初に正解にたどり着いたときのコードは以下のような感じでした。

<?php

foreach (json_decode(stream_get_contents(STDIN), true) as $k => $v) {
    $a[] = "{$k}={$v}";
}
parse_str(implode('&', $a), $a);
echo json_encode($a);

この答えにたどり着くまでに可変変数や eval() などを試していたのですが、うまくパスできませんでした。 そのころから、JSONハッシュのキーがURLのクエリのキー形式として使えそうだったので、URLクエリ形式に変換して parse_url() で配列化する方法をためして、正解することができました。 $a 配列をimplode() でまとめた文字列が

foo=1&bar[0]=A&bar[1]=B&buz[0][0]=00&buz[0][1]=01

になるので、それを parse_url() で求める連想配列にできる、という感じです。

最適化1 標準入力の受け取りが長い

stream_get_contents(STDIN)

ここですね。いかにも冗長な感じがします。検索したところ、

fgets(STDIN)

で受け取れることが分かったので、短くなりました。 なお、今回のレギュレーションだと、単に文字数が短いというのが高得点のコツではないそうですが、スコアは良くなりました。

最適化2 ループをなくす

次に見たのがここです。

foreach (json_decode(fgets(STDIN), true) as $k => $v) {
    $a[] = "{$k}={$v}";
}

json_decode して foreach するとか、コード書きすぎですね。。。 ループをなくして、文字列変換ですませることができれば、かなりコードをシンプルにできそうです。

{"foo": "1", "bar[0]": "A", "bar[1]": "B", "buz[0][0]": "00", "buz[0][1]": "01"}

この入力を見ると、jsonのキーと値は ":", 各要素は "," で区切られていることが分かるので、それぞれを =,&に変換してから、周りの{}" を除去できれば、期待するURLクエリに変換できそうです。 そこで最適化したコードが以下です。

<?php
parse_str(strtr(trim(fgets(STDIN), '{}"'), ['":"' => '=', '","' => '&']), $a);
echo json_encode($a);

標準入力を trim(fgets(STDIN), '{}"') で、余計な文字列を削除して以下にします。

foo": "1", "bar[0]": "A", "bar[1]": "B", "buz[0][0]": "00", "buz[0][1]": "01

あとは strtr(..., ['":"' => '=', '","' => '&'])でURLクエリになるように変換します。 (入力のjsonにもスペースがなかったようです) 時間制限最後の回答は以下だったかと思います。

<?php
parse_str(strtr(trim(fgets(STDIN), '{}"'), ['":"' => '=', '","' => '&']), $a);
echo json_encode($a);

今思えば、値に :, が入ってると変換できないですね。

最適化3 (時間切れですが、少し最適化)

LTの時間にやっていたのですが、 preg_replace() を使って {}" をすべて削除してから、 strtr() で1文字毎に変換(:=, ,&)するコードだと抽象構文木としてはシンプルになるので試したところ、スコアが良くなりました。

<?php
parse_str(strtr(preg_replace('/[{}"]/','',fgets(STDIN)),':,', '=&'), $a);
echo json_encode($a);

そのときのスコアは以下の通り。

  • 項目A(低いほど高評価) 105 (最適化2では107)
  • 項目B(低いほど高評価) 4 (最適化2では4)
  • 項目C(低いほど高評価) 39 (最適化2では46)
  • 項目D(低いほど高評価) 105 (最適化2では107)
  • 項目E(高いほど高評価) 8 (最適化2では10)

いまのところ、僕の最高スコアはこちらになります。

おまけ: FizzBuzz について

FizzBuzz については昔作ったプログラムを少し最適化して

<?for(;$i++<100;)echo(($i%3?'':Fizz).($i%5?'':Buzz)?:$i).'
';

として解いてました。しかし、このゴルフ場はPHP7.4、7.4の最新の文法を使ったら、もしかしたらPHPerチャレンジのボーナストークン的なものがもらえるかも!?とおもい、アロー関数を使って

<?for(;$i++<100;)echo(($f=fn($d,$w)=>$i%$d?'':$w)(3,Fizz).$f(5,Buzz)?:$i)."
";

と書いて正解になりましたが、別のトークンはもらえませんでした!

終わり。

Chrome 80 クッキーのSameSite属性対応のややこしい注意点2つ

2020年2月4日に Chrome 80 がリリースされました。 このバージョンでは既存のWebアプリケーションの振る舞いに大きな影響を与える変更として、クッキーのSameSite属性の既定値が変わるというのがあります。

ただし、振る舞いの変更は、2月18日以降に予定されています。(正確には「2月17日の週でアメリカ合衆国大統領の日を除く」)

Enforcement rollout for Chrome 80 Stable: The SameSite-by-default and SameSite=None-requires-Secure behaviors will begin rolling out to Chrome 80 Stable for an initial limited population starting the week of February 17, 2020, excluding the US President’s Day holiday on Monday. We will be closely monitoring and evaluating ecosystem impact from this initial limited phase through gradually increasing rollouts. SameSite Updates - The Chromium Projects

この仕様変更についてはChrome 80が密かに呼び寄せる地獄 ~ SameSite属性のデフォルト変更を調べてみた - Qiitaが詳しいので、この記事では説明しません。 しかし、概要を把握した上で、以下を読んでおくことをお勧めします。

1) 2分間は、SameSite=Lax のクッキーがPOST時に送信される仕様

Chrome 80 以降の振る舞いをテストするには、chrome://flags で以下の項目をEnabledにする必要があります。

  • SameSite by default cookies
  • Cookies without SameSite must be secure

さらに、仕様変更の影響を緩和させる措置として、SameSite=Lax のクッキーはトップレベルのクロスドメインPOST 時に発行から2分間は送信されるChromeの仕様に注意が必要です。(最終リクエストから2分間ではなく、クッキー発行から2分間)

トップレベルのクロスドメインリクエストとは、iframe 内のPOSTやXHRではない、通常のフォームでの画面遷移を伴うPOSTのことです。

つまり既存のアプリケーションの振る舞いに影響しないかをChromeを使って調査するときは、クッキー発行後2分待つ必要があるということです。

この一時的な介入は将来的に削除される予定ですが、アプリケーションの検証時には、無効化したいこともあると思います。その場合、以下のようにChrome起動時にオプションを指定して無効化できます。(macOSの場合) (Chrome 79では使えなかったのでChrome Canaryを使う必要がありましたが、Chrome 80では使えるようになっています。(おそらく80 Beta版でも使えたのではないでしょうか))

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --enable-features=SameSiteDefaultChecksMethodRigorously

Starting in Canary version 80.0.3975.0, the Lax+POST temporary mitigation can be disabled for testing purposes using the new flag --enable-features=SameSiteDefaultChecksMethodRigorously to allow testing of sites and services in the eventual end state of the feature where the mitigation has been removed.

2) SameSite=None が一部の古いブラウザで機能しない問題

SameSite=Lax だと動作しなくなる場合、SameSite=None; Secure に変更することで振る舞いを維持する、という解決策があります。(SameSite=None; Secure を送出する場合、CSRFのリスクを考慮する必要があります。)

Google Developers Japan: 新しい Cookie 設定 SameSite=None; Secure の準備を始めましょう

特定のバージョンの Chrome、Safari、UC Browser など、一部のブラウザは None 値を意図しない方法で処理する可能性があります。その場合、デベロッパーはそのようなクライアント向けに例外処理をコーディングする必要があります。これには、古いバージョンの Chrome が提供している Android の WebView も含まれます。既知の互換性のないクライアントの一覧はこちらです。

と書かれていて、SameSite=None のクッキーを削除したり、SameSite=Strict とするブラウザがあるようです。そのようなブラウザを判定するためのコードがSameSite=None: Known Incompatible Clients - The Chromium Projectsで紹介されています。それによれば

  • Chrome 51〜66
  • Android版UC Browser バージョン12.13.2以前
  • MacOS 10.14 のSafari とSafari組み込みブラウザ、iOS 12のすべてのブラウザ

に問題があるとのことです。macOS 10.14 と iOS 12 の影響が大きそうです。

まとめ

最初にSameSiteの話を詳しく調査したとき、2分間はSameSite=Lax+POSTでクッキー送信されるというのを見逃していて後日知って焦りました。共有するために記事を書きました。

CSRF対策として既定がSameSite=Laxに変わることは歓迎しますが、既存アプリをサポートする立場だと注意点がありますね。

CHROMEのAVAIL BACKPACKを買った

https://www.chromeindustries.jp/smartphone/detail.html?id=000000000632

2014年にcote&ciel のリュックを買って使ってましたが、底が破れたり、肩のベルトがどんどんずれて背負い心地が悪くかったので買い換えようと。 以前CHROMEのメッセンジャーバッグを使っててよかったのでまたCHROMEに。

肩紐が全くズレないし、背中が暑くならないのでウォーキングが快適です。 ショップでは15インチノートPCが入るかは書いてありませんでしたが、 Macbook Pro 15インチがちょうど入る大きさでした。 電車で立つ時に、前に抱えるのも楽。 良い買い物をしたと思います。

PHPカンファレンス福岡2019 マネクラからの挑戦状 Webサイトコードゴルフ スコア60230(非公認)の解説

PHPカンファレンス福岡2019内企画 マネクラからの挑戦状、開催時間内にはほとんどやってませんでしたが、後日やってみて、スコア60230を出せたので解説してみます。開催時間外なので非公認スコアです。

(記事公開後、 @m3m0r7)さんと相談しながら縮めた結果、60300→60230へとさらに70短縮できました。

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

スコア60230(PHPファイル(480文字)+画像ファイル)

まずは入念にレギュレーションをチェック

プログラム一式をマネージドクラウドインスタンスにデプロイ後、オンライン上に用意されたDiffツールを使って、ブラウザレンダリング結果が一致しているかチェックします。合格したら、make check コマンドでスコアを計測する、という流れです。また以下のレギュレーションが存在します。

  • PHPで作成されたサイト https://phpconfuk-codegolf-php.lolipop.io/ (正解サイト)のソースコードをお渡しします(このリポジトリです)。
  • サイトの 表示を一切変えずに Webサイトを構成する全コード全ファイルの合計ファイルサイズを小さくして、あなたのロリポップ!マネージドクラウドPHPプロジェクト にデプロイしてください。
  • 最も小さいファイルサイズになった人が優勝です
  • コンテナ内の /var/www/html 内にWebサイトを表示するのに必要な全てのコード、およびファイルを設置してください
    • /var/www 等への設置は禁止です
  • Makefile内の check タスク内のコマンドの変更は禁止です

レギュレーション

プログラムがやっていること

エントリーポイントとなる index.php を見てみましょう。

コアとなるロジックは、

  • クエリストリングのキーと値の組み合わせを tokens テーブルに保存。
  • 値降順(第一ソート順), キー降順(第二ソート順)でとりだし、3番目の値の文字列を取得し逆さにする
  • HTMLテンプレートに文字列を埋め込んで表示

という感じでした。

vendors ディレクトリ、データベースアクセスを取り除く

初期のスコアは400万を超えていますが、その大部分が vendors ディレクトリであることがわかります。 プログラムを把握した上であらためてレギュレーションを確認してみると、データベースに関してはなんら記述がないので、データベースへ保存するコードを除去できます。 よって、受け取ったクエリストリング配列を処理して、レンダリングに必要な文字列を返すコードと、その他のHTMLを全てindex.php に入れれば、大分スコアが減らせそうです。そうして出来たコードにインデント・スペースを補完したコードが以下。

<?
    $a=$_GET;
    krsort($a);
    arsort($a);
    echo strrev(array_values($a)[2])
?>

最初のforeach で配列を整形してる部分をなくせないかな、と思いましたが思いつかなかったです。 ユーザー定義のソート関数ではキーと値のソートルールが同じだったので、$i = $a[1] != $b[1];$itrueなら値、false ならキーでソートされます。

コードゴルフなのに、そこそこ読みやすいコードになってしまいましたね。

short open tag

short open tagは将来的に廃止されそう(PHP: rfc:deprecate_php_short_tags)ですが、PHP 7.3.2ではまだ動作しますし、マネクラの環境でもデフォルトで使えます。タグとforeach キーワードの間にスペースが無くても動きます。

不要なファイルを削除する

これで、 vendors も削除できますし、 model/token.php も不要になりました。表示に必要なファイルは、 index.php, mc.png の2つになったので、改変不可な make check ファイルを含む Makefile を入れて、3ファイル以外の全てのファイルを削除しましょう。 .env, .htaccess が無くてもDiffチェッカーはパスします。

HTMLドキュメントを短くする

残るはHTMLを短くするのみです。PNGファイルは、名前を変える以外の変更はしていません。

無駄なCSSを取り除く。

lolipop-mc-codegolf-challenge/home.liquid at master · pepabo/lolipop-mc-codegolf-challenge · GitHub

CSS定義でファイルサイズがかなり大きくなってます。 まず、HTMLにはクラス属性が1個も定義されてなかったので、セレクタにクラスを含むものは安全に削除できます。 使ってないタグを含むセレクタの定義も削れます。 結果以下が残りました。

body {
    height: 100vh;
    font-family: Helvetica;
    letter-spacing: .04em
}
table {
    height: 100%
}
* {
    border: none;
    padding: 0;
    margin: 0;
    font-weight: 400;
    color: #465560;
    text-align: center
}
td {
    height: 50%;
    width: 5%
}

font-family は、レンダリングに使われている物だけをのこしました。画面に表示されている文字列要素が h2 だけだったので、 * に統合することができました。

表示に影響しないタグを削除していく

<html><header><meta><body> などを削ってもチェッカーにパスしました。

表示に影響しない閉じタグを削除していく

一箇所 </table> が残りましたが、それ以外は削除できます。 </style> については、ドキュメントの最後に移動すると、削除できます。 ところどころにある &nbsp; も不要です。

属性値を"で囲まない

はい。業務で真似してはいけない。

DOCTYPE宣言

<!DOCTYPEhtml>

宣言内のスペース不要でした。

pngファイル名をmc.pngm

5文字縮まりましたね。拡張子なんていらなかった。

まとめ

これで解説は終わりです。 全般的に業務で役に立たない知識です。

没テクニック

テーブルレイアウトをやめてdivにする

挑戦しましたが、Diffが出てしまうので断念。

PNGファイルサイズを縮める

PNGファイルからアルファチャンネルを削除すれば、スコアを縮められるかと思ってGIMPでやってみましたが、Diffチェッカーに引っかかって失敗しました。サイズは8,000ほど縮まりそうだったので残念です。

レギュレーションを守った上で最高のスコアは

なんと0になります。スコアチェッカーのコマンドは以下の通りですが、 *.git/*, Makefile, .env が除外されているため、.git/ ディレクトリ以下に index.php, mc.png を移動して、シンボリックリンクを作れば、レギュレーションを完全に守った上で0が達成できます。 チェッカーで除外するのではなく、デプロイ時に除外すればこの穴はできないので、運営側で意図した穴なのかな、と思います。

@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'