「PHP」に関する日記(2)

<前 次>

メテオンもどきとPHP8.3とNeoMupl

メテオンもどき

ゲーム本体はもうとっくに動かないんだけど、微妙な警告が出ていました。
PHP8.3の動作確認中だったのですが、バージョンアップの影響かどうかは不明です。
どのみち、良い傾向とは言えないので、修正しています。

PHP8.3

というわけで、ステージング環境的な場所で、動作確認中。
表示時にエラーが起きないかどうかだけなら、クローラーを走らせればいいだけなので楽。
まあ、でたらめに走り回るので、エラー発見効率はお察しですが。

NeoMupl

コミットメッセージが紛らわしいですが、アンドゥ・リドゥ機能の実装を始めました。
GitHub Copilot を試しに使ったんですが、チャット機能で生成させると微妙で、履歴の一動作に相当するインターフェースを作ってやると、それに沿っていい感じに作ってくれました。
アンドゥ・リドゥ機能自体はエディタ系のプログラムでは定番なので、お手本となるコードも豊富なんでしょうね。
こっから先の、各動作を実際にやり直す機能は、さすがに自力で実装しようと思います。

HTMLとアンパンマンDBとPHP8.3

HTML

とりあえず主要なファイルは一通りアップ終わりました。

アンパンマンDB(システム)

また発動していないバグ修正。
話数が空欄じゃなくて「0」になっていた場合に、「第0話」とかになってしまうんですが、アンパンマンDB的には「0」も空欄扱いしたいので、空欄の処理に飛ぶようにしました。
さっきのHTMLと一緒にアップ済みです。

PHP8.3

もうそろそろPHP8.4の正式リリースも近そうですが、うちはまだ8.2だったので、ひとまず8.3まで上げることにしました。
とりあえず開発環境での動作は一部のライブラリ以外はコードの修正なしで大丈夫そう。
本番サーバーはオプションを変えるだけだからいいとして、DockerとCI環境も対応しておかないと。

アンパンマンDBとXdebug

アンパンマンDB(記事更新)

コラムからの転記。
この程度なら毎日いっぱいやってもいいのにね。

アンパンマンDB(システム)

リストをNULL検索できないバグを修正しました。
ただ、今のところ、何もない項目はNULLじゃなくて空文字列が入っているので、実質的に影響はありません。

Xdebug

Xdebug - Debugger and Profiler Tool for PHP
Xdebug導入しました。
すなわち、Xdebug使わない縛り辞めました。

もともとデバッガとしては、PHP標準のphpdbgを使ってました。
PHPUnitのカバレッジもphpdbgだったんですが、PHPUnit10以降(最新版は11)には使えなくて、phpdbgのために古いのを使う続けるのも厳しいなって思ってました。

カバレッジ計測の選択肢としては、PCOVもあったんですが、あんまり更新されてなさそうだったし、カバレッジ「だけ」というのもどうかと思ったので、デバッグ並びにカバレッジ計測の定番、Xdebugを導入した次第です。

そもそもXdebugを避けてphpdbgを使っていたのは、XdebugはPHP標準に入っていないというのと、カバレッジ計測が遅いという話を聞いたからだったのですが、実測してみると、今はXdebugのほうがphpdbgより1割ほど早く終わりました。

SearchQueryStructure

実装は特に進まず。
やっぱりエラー処理となると、やみくもに実装を始めるより、ちゃんと考えないと詰みますからね。
エラーが起きたときも、途中までのパース結果はエラー本体とともに返したい、というのは昨日言った通り。
どこでエラーが起きたかというのも返せると、修正もしやすくなりますね。
となると、字句解析の時点で、各シンボルに位置情報を埋め込む必要がありそう。
あと、空文字列を渡されたときの特別対応もあっていいかな。

SearchQueryStructure

旧来の機能をおおむね移植完了。
ただ、異常系に対しては、途中までの結果を返すのではなく、例外を投げたいので、もうちょっと調整が必要ですね。
できれば、専用の例外を作って、例外オブジェクトの中に途中までの結果を詰め込んで、例外をキャッチすれば失敗時も旧来の動きにできるという方向で作りたいですね。

SearchQueryStructure

やっと作り始めました。
美しい設計で作り直せるはずだと夢想して、「で、美しいって具体的には?」で止まって、長いことほったらかしになっていました。
これ、アンパンマンDBの検索機能の機能強化に使おうと思ってたんですよね。
だから、美しさを目指していつまでもスタートラインでぐるぐる回ってちゃアンパンマンDBも変われんのです。

ともかく、最初にやるべきことは旧来のコードのコピペと最初に決めたし、出力が美しければ中身が泥水でも結果は変わらんので、とりあえずほぼコピペでどうにかなる部分だけ持ってきました。
既にINodeインターフェースを用意していることからもわかるように、最終的な結果はINode実装クラスで返そうと思っています。

アンパンマンDB

アンパンマンDB(記事更新)

昨日のふくろうおじさんの流れで今度はおじいさんを追加しました。

こっちはコラムから転記組。
元気倍率アップ系ですね。
300より200のほうが見た目豪華なのがポイント。

アンパンマンDB(システム)

テストが不安定な問題、直接の原因は一応わかりました。
エラーコード0xC0000005(-1073741819)、アクセス違反です。
アクセス違反なら何でもこのエラーが出るので、このエラーコード一つで真の原因の特定はできません。
傾向として、カバレッジ計測なしだとエラーは起きず、計測ありのときだけエラーが出ることがあるので、カバレッジと何かの競合だと思われます。
落ちる部分のコードがやってることと言えば、同じPHPスクリプトを条件を変えて2回呼び出していることなので、ここで何らかのロックがかかってしまってカバレッジを邪魔しているのでは?と考えています。
完全解決までは、カバレッジ計測の頻度は減らすかなぁ。

目次

サーバーサイドでの自動生成の目次の仕組みを作って、各ページに適用中。

うちのサイトって、昔はいろんなページに自動生成の目次がありました。
ただ、それはInternet Explorer、それも古いバージョン専用だったので、もう動いてなかったんですよね。
静的HTMLファイルに労せず目次を取り付けたいという動機でJavaScriptを使ったのですが、動かなきゃ世話ないですね。
私の思想として、サイトの基本的な機能はなるべくJavaScript抜きでも動作するようにしたいというのがあり、この思想にも反する仕組みでした。
一方で、URL上は.html拡張子に見えるファイルは、現在は大部分をHTMLファイルに見せかけたPHPファイルを裏で動かしています。
詳しく言うと、ただのHTMLファイルに見せかけたPHPファイルが出力したただのHTMLファイルを最終的に表示しているのですが、とにかくサーバー側でいったん動的な処理を挟んでいるわけです。
この動的な処理の部分に目次自動生成を差し込めば、JavaScriptに頼らず、ブラウザに依存せず、それでいて労せず目次を作れるのです。

今のところ、自動生成の仕組みはできたので、今は各ページに適用するのを頑張っているところです。
先ほどまで自動化だの労せずだの言っていましたが、目次以前にまずHTMLそのものを修正しないといけないのです。
PHPを使う仕組みに切り替えた時点から、HTMLも4.01から5をすっ飛ばしてLiving Standardに切り替えているのです。

…切り替わってないのです!

PHPに切り替えた時点で、自動生成のヘッダとフッタにより、ガワだけはLiving Standardになりましたが、中身はほとんど手付かず。
4.01のままのHTMLからじゃ、Living Standard用のシステムで目次を自動生成はできないのです。
ブラウザはだましだまし動いてくれますが、ブラウザほど気を利かせてない自作システムは、だませないのです。

ということで、労せず目次を自動生成するために、現在、労しています。

SearchQueryStructure

mifumi323/SearchQueryStructure: 検索構文とかに使われるAND/OR/括弧などの構文解析をするやつ
新しいライブラリとして作り始めました。
「検索クエリ」ってよく言ってるよなってことで、Phrase→Queryに、解析(parse)の逆に構築(build)もできていいかもなって思って、両方に共通する構造化データから、Parser→Structureに名前を変更しました。
実装はまだ始めていませんが、最初の段階はSearchPhraseParserからコピーしてきて結果だけ何かしらのクラスを作って当てはめればいいかなって思ってます。
オプションで柔軟にとか、ビルドできるようにとかは最初の一区切りまでできてから。

SearchPhraseParser

新バージョンか別バージョンか、どうするか決めかねているんですが、データの返し方を大幅に変えようかと思っています。
現状だと、全部連想配列で返しているんですが、今どきのPHPだと、型をしっかりつけたほうがいいんですよね。
まあ、それを想定しての現状の関数名ParseToArrayで、単純にParseToObject関数を生やすのも一つの手なんですが、もう一つやりたいことがあって、そっちが問題なんですよね。
現在、パーサーはParserクラスで、実際にパースするParseToArray関数は、static関数です。
パーサーオブジェクトを作らずに呼び出す形なので、パーサーオブジェクトにオプションを持たせてデフォルト以外の動作をさせるとか、クラスを継承して使用を拡張するみたいなことが一切できません。
だから、オブジェクトを作る前提の別クラスを新たに用意する、というのも考えたのですが、クラス名はParserが完璧すぎて別の名前使いたくないし…。
で、そこまでやると、使い方が根本的に変わってしまうので、そもそも同じライブラリである必要があるのか?って疑問もわいてくるのです。
しかし、別ライブラリとして作るなら、いったいどんな新しい名前にするの?って話にもなるわけで。
サーチに使うと限らないのでサーチの部分変えるか?
検索語の組み合わせを取り扱うのでフレーズという単語を使ったけど、もっとぴったりな単語はあるか?
パーサーの部分は今のところ完璧なので名前から外す気はない。

アンパンマンDBとTgwsMarkとPackagist

アンパンマンDB(記事更新)

正式な曲目が公開されたので反映しました。
『森は枯れても2024』が新録ですね。
映画も森を舞台とした話なので、実際に使われる可能性が高そうです。

TgwsMark

名前空間とか調整して、再公開しました。
これでもうばっちりだと思ったのですが、試しに自分のプロジェクトに入れようとしたら、うまくいきませんでした。
他の人のライブラリとかと見比べても決定的に足りないものはなさそうに見えるけど…。
急ぐ用事ではないし、ちょっと後日考えてみよう。

Packagist(Mifuminator、SearchPhraseParser、TwitterLib)

他のライブラリも名前空間変更したのですが、どうにも微妙な感じです。
一応、メジャーバージョンを上げておいたので、バージョン指定で導入している人には悪影響ないとは思うのですが。

アンパンマンDBとPHP-CS-Fixer

アンパンマンDB(システム)

リストの現行のストリクトクエリのテストを作成。
新記法にする前に、内部処理を変える必要があって、旧記法でも同じ内部処理を使う必要があるので、内部処理を変えても旧記法が通用することを確認するためのテストが必要なのです。

PHP-CS-Fixer(Today I Learned)

PHP-CS-Fixer/PHP-CS-Fixer: A tool to automatically fix PHP Coding Standards issuesには、ドキュメントコメントでもないくせにドキュメントコメントぶっているただのコメントを本当のただのコメントに戻す機能があります。
/** ただのコメント *//* ただのコメント */

しかし、/** @var ADBValueList $adb_value */みたいに、ローカル変数の型を指定しているコメントまで、ただのコメント扱いされてしまいます。
Intelephenseでは、ちゃんとローカル変数のドキュメントコメントも認識してくれるので、PHP-CS-Fixerにぶち壊されると困るのです。

そこで使うのが以下のオプション。

Rule phpdoc_to_comment - PHP Coding Standards Fixer
ドキュメントコメントをただのコメントに戻す処理の制御ができます。
今回の件なら、ignored_tagsオプションにvarを入れれば、@varのドキュメントコメントだけは見逃してくれるようになります。

PHPUnit

PHPUnit自体の機能とは特に関係ないんですが、うちのサイトの自動テストでは、HTMLの表示とかのテスト(結合テストに近いところ)もある程度やっていて、出力結果の保存とかもやっているんですね。
テストやるたびに、たとえ何の変更もなくて何の問題もなくても、何百ページ分ものHTMLファイルが出来上がります。
たとえ何の変更もなくて何の問題もなくても、改めて毎回テストをやること自体は、回帰テストといって、とても有意義なのですが、回帰テストは正しいか正しくないかだけわかれば充分なので、何が出力されたかまで細かく残す必然性はありません。
とはいえ、たとえ何の変更もなくても問題が起きることがあるから、回帰テストが必要なわけで、回帰テストに落ちたときだけは、何がおかしくなったのかを残す必要があります。
なので、普段邪魔になるからと言って、一律で保存をやめるなんてことはしないほうがいいでしょう。
だからといって、テストに落ちたときだけ保存するという器用なこともなかなか難しいわけで。
ということで、出力結果を破棄するオプションを作って、それをON/OFFすることで、保存するかどうか切り替えるようにしてみました。

アンパンマンDB(システム)

ストリクトクエリの件、区切り子じゃないコロンをどうするかでちょっと悩んでます。
str_getcsvは区切りとして扱わない区切り子を考慮してくれますが、そのためには「"」など囲み記号が追加で必要になるんですよね。
単なるCSV(ここではCharacter Separated Values)であればそれでいいんですが、アンパンマンDBの検索構文では、ルーズクエリ・ストリクトクエリ両方に共通する文法で、スペースや「"」を含む単語は「"」で囲むという仕様があって、クオートの仕様がぶつかってしまいます。
一応ぶつかったところで、どちらにもエスケープ処理はちゃんとあるので、「"」で囲んだものを「"」でエスケープして「"」でさらに囲むみたいなことをすれば、不具合なく取り扱えるはずですが、ちょっとさすがにややこしいかなと。
ストリクトクエリの囲み記号を「"」以外にするか、そもそもstr_getcsvを使わずに完全に独自仕様にするか…。

あと、ストリクトクエリをパースするのとは逆に、ストリクトクエリを構築する機能もアンパンマンDBにはあるんですが、str_getcsvの逆をやる関数が実はPHPには今ないんですよね。
CSVの構築にはfputcsvがありますが、これはファイルに出力されてしまいますし、末尾に余計な改行も追加されます。
多分どうにかする方法はあるとは思うのですが、そこまでしてPHPの標準関数使うべきかな…と思ったり。

やはり独自仕様にする…かなぁ。

アンパンマンDB(システム)

昔のアンパンマンDB(アンパンマンDB3)では、編集画面でプレビューすると、どの項目が編集されたか、編集内容を編集前と編集後で比較表示できるようになっていました。
しかし、今のバージョン(アンパンマンDB4)になって、項目の取り扱い方が大きく変わったため、比較表示機能は失われてしまいました。


復活させました。
今回、編集内容の表示機能を復活させるに当たって、以前より見やすくしてみました。
編集前後を並べるのではなく、diffコマンドみたいに差分のあった行を色分けして強調表示しています。
欲を言うならもっとdiffコマンドに寄せて、追加行に+、削除行に-をつけたりするとかあってもよかったかもしれないですね。

これを作るに当たって使ったライブラリは、編集データの文字列化に使うsymfony/yamlと、できた文字列の差分表示に使うsebastian/diffの2つ。
差分表示のほうははJavaScriptでやっちゃおうかなとか思ってたんですが、しっくりくるものがなかったので、結局どっちもPHPでやりました。

symfony/yamlは、構造化されたデータを複数行のわかりやすくdiffの取りやすい文字列に変換(シリアライズ)してくれます。
diffを取りやすいというのがポイントで、JSONだと配列の要素の後ろ(または前)にコンマがつくかつかないかが要素の順序で変わったりして、実質変化のない行まで差分になってしまうのですが、YAML形式だとそういうのが起こりにくくなっています。
ただし、構造化されていないただの文字列を渡すと、JSON形式の一行の文字列にまとめられてしまうので、この場合だけはシリアライズ処理をせずにそのままdiffに渡します。

sebastian/diffは、おそらくPHPUnitにも使われている、行単位の差分検出ライブラリです。
シンプルに使えば、一発でいい感じのdiff文字列を出してくれるのですが、HTMLページに表示するにはdiff文字列ではちょっと都合が悪いので、配列形式のデータを一度出してもらって、そこから自力でHTMLを作るようにしています。
差分行の色は、GitHubと同じ色にしました。

PHP8.4(Today I Learned)

【PHP8.4】メソッド引数のデフォルトnullがついに禁止される #PHP - Qiita
function foo(int $x = null)みたいな、null許容指定していないのにデフォルト値null入れてたらnull許容(ここでは?int扱い)になるっていう気持ち悪い挙動が今後なくなるみたいです。
で、うちの場合、PHP-CSFixerで@Symfonyの設定を入れていて、これがデフォルト値nullを入れていたら方の左に付いているnull許容指定の?を自動で消しちゃうんですね。
見た目気持ち悪いけど@Symfonyの推奨ならいいかーって思って消されるままにしていたんですが、今後そういうのは通用しなくなっていくので、対応を入れました。
先程の記事にも書いてあるのですが、nullable_type_declaration_for_default_null_valueオプションで、逆に?が足りないところに自動でどんどん付けてくれるようになります。

GitHub Actions

動くようになりました。
Composerのバリデーションは神経質すぎるところがあるので実行しないことにして、本当にやりたい自動テストだけにしました。
phpdbg使えなかったけど、普通にPHPUnitを動かすだけでカバレッジは取れました。
アーティファクトがばらばらのファイルじゃなくてZIPアーカイブしかなさそうなので、HTML出力したカバレッジレポートを見て回ることはできないけど、まあ手元でZIP展開すれば見られるのでこれはまあOK。
あとは…そう、もういらなくなったCircleCIとの連携を切るだけ。

Docker DesktopとBluesky

Docker Desktop

なんかドメイン追加したらコンテナが立ち上がらなくなったんですが、設定ファイル内のコマンド順序を入れ替えたらなぜか動くようになりました。
カレントディレクトリがおかしい系のエラーみたいだったから、コマンド内部でひっそり変わってるのかもと思ってコマンド順序を入れ替えたのですが、ディレクトリの確認はしてないので、実際のところ本当にこれでよかったのかは謎です。
致命的ではないもののPHPのエラーも起きているようだし、もうちょっとしっかり設定を見直してみてもいいかもしれません。

Bluesky

とりあえず、自分のアカウントへのリンクをうちのサイトのトップページに追加しておきました。

Docker Desktop

dockerのphp8にGD ライブラリをインストール #PHP - Qiita
このへんを参考に画像ライブラリ入れてみました。
これでとりあえず画像は表示されるようになりました。
文字列の表示とかツイート文字数計測とか、なぜかうまくいかない部分がまだ残っていますが、ひとまず目立つ部分はいい感じに動くようになりました。

Docker Desktop

.htaccessの調整を進めたのと、PHPの画像関連の拡張の有効化。
ただ、まだローカル開発環境で画像は表示できてないです。
何が足りないんだ?

<前 次>