gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

2a問題が解決した!!!!

素晴らしき自動的な世界~或いは「型のない」世界~ https://gallu.hatenadiary.jp/entry/20061108/p1 で書いた、こんなお話、記憶にございますでしょうか?

PHP驚愕の事実

if ('2a' == 2) {
 ここ通る
}

ここから幾星霜、涙が大河となるくらいまであちこちで色々な問題を引き起こしてきました2a問題(大げさ)。
2a問題再びw https://gallu.hatenadiary.jp/entry/20070516/p1 でも書いた通り、switch でも遺憾なくその威力を発揮してまいりました。

が!!!
https://www.php.net/manual/ja/migration80.incompatible.php

下位互換性のない変更点
PHP コア
文字列と数値の比較

(厳密でないやり方で)数値と非数値文字列を比較する場合、 数値を文字列にキャストし、文字列と比較するようになりました。

との事でさりげなく書いてあるこの内容が! 全てを! 改善しました!!!!

ってなわけで、まずソースコード

<?php
declare(strict_types=1);

var_dump( PHP_VERSION );

var_dump( 2 == '2a' );

$i = '2a';
switch ($i){
    case 2:
        echo "2\n";
        break;
    case '2a':
        echo "2a\n";
        break;
}

環境その1

string(6) "7.4.11"
bool(true)
2

まぁ毎度お困りの状態*1

環境その2

string(5) "8.0.0"
bool(false)
2a

ききました奥様?
falseですって!
2aですって!!

これですよコレ!!!

というわけで。
まぁもう少し色々と掘ってみないと、な所はありますが、とはいえ一端「2a問題が(多分大体)落ち着いてきた!!」ってことで、これは快挙なんじゃないかなぁ? と思うわけでございます。

ってなわけで記念Blog。

&余談。
PHP8.0.0ですが、PHP7.4.xのconfigureオプションで(おいちゃんの場合)問題なくコンパイルできました。
インストール、とりあえずは楽なんじゃないですかしらん???

*1:なんでメンテナンスが11か、ってぇと、12と13が「This is a bug fix release.」だから、ちょいと止めてる状態

Slim4で「404のログ」だけ消したい(&任意のエラー画面出したい)

Slim4です。
エラー周りの基本処理で。

 * @param bool $displayErrorDetails -> Should be set to false in production
 * @param bool $logErrors -> Parameter is passed to the default ErrorHandler
 * @param bool $logErrorDetails -> Display error details in error log
 * which can be replaced by a callable of your choice.
 * @param \Psr\Log\LoggerInterface $logger -> Optional PSR-3 logger to receive errors

ってなコメントもございますので、おいちゃんは大概

    $errorMiddleware = $app->addErrorMiddleware($container->get('settings')['displayErrorDetails'], true, true, $container->get('logger'));

って書いておくでございます。
ただ、これだけだと「404もエラーログに書かれるので結構ウザい」でございます。

いや「Slimのエラーを一通りログに出さない」んなら

    $errorMiddleware = $app->addErrorMiddleware($container->get('settings')['displayErrorDetails'], true, true);

でよいのですが、「404以外のエラーは念のために補足しておきたい」んですよねぇ、という乙女心*1

まぁ色々調べてみるわけなのですが。

まず「エラー時に任意の画面を出したい」場合、
https://www.slimframework.com/docs/v4/middleware/error-handling.html

// Get the default error handler and register my custom error renderer.
$errorHandler = $errorMiddleware->getDefaultErrorHandler();
$errorHandler->registerErrorRenderer('text/html', MyCustomErrorRenderer::class);

ってな記述があるのでございます。
なお上述クラスは

<?php
use Slim\Interfaces\ErrorRendererInterface;

class MyCustomErrorRenderer implements ErrorRendererInterface
{
    public function __invoke(Throwable $exception, bool $displayErrorDetails): string
    {
        return 'My awesome format';
    }
}

こんな風に実装する感じでございます。

さて。
とりあえずgetDefaultErrorHandler()てのがあるので、軽くgrepります*2
"vendor/slim/slim/Slim/Middleware/ErrorMiddleware.php"

    public function getDefaultErrorHandler()
    {
        if ($this->defaultErrorHandler === null) {
            $this->defaultErrorHandler = new ErrorHandler(
                $this->callableResolver,
                $this->responseFactory,
                $this->logger
            );
        }

        return $this->callableResolver->resolve($this->defaultErrorHandler);
    }

getがあるんならsetもあるんじゃなかろうか。

    public function setDefaultErrorHandler($handler): self
    {
        $this->defaultErrorHandler = $handler;
        return $this;
    }

DefaultがあるんならDefault無しもあるんじゃなかろうか。

    public function setErrorHandler($typeOrTypes, $handler, bool $handleSubclasses = false): self
    {
        if (is_array($typeOrTypes)) {
            foreach ($typeOrTypes as $type) {
                $this->addErrorHandler($type, $handler, $handleSubclasses);
            }
        } else {
            $this->addErrorHandler($typeOrTypes, $handler, $handleSubclasses);
        }

        return $this;
    }

ここからちゃんとコードを確認してもよいのですが……面倒なんでvar_dumpで調査します。
つまり
・setErrorHandler()の先頭に var_dump($typeOrTypes); exit; を仕込んで
・404をわざと発生させます
まぁ予想通り Slim\Exception\HttpNotFoundException でございます(Slim4、この辺は例外のクラスで判別するので)。

ってことは

    public function getErrorHandler(string $type)
    {
        if (isset($this->handlers[$type])) {
            return $this->callableResolver->resolve($this->handlers[$type]);
        } elseif (isset($this->subClassHandlers[$type])) {
            return $this->callableResolver->resolve($this->subClassHandlers[$type]);
        } else {
            foreach ($this->subClassHandlers as $class => $handler) {
                if (is_subclass_of($type, $class)) {
                    return $this->callableResolver->resolve($handler);
                }
            }
        }

        return $this->getDefaultErrorHandler();
    }

これも大体予想通り。

なので、ざっくりと実装してみます。

    $errorMiddleware->setErrorHandler(\Slim\Exception\HttpNotFoundException::class, function($request, $e) use($app){
        $response = $app->getResponseFactory()->createResponse(404);
        $response->getBody()->write('めっからないよん??');
        return $response;
    });

確認……うん、OK。

というわけで、備忘録を兼ねて、メモ。

*1:なにゆえに乙女?

*2:ぐれぷります、とかお読みいただければ幸いです

あえての「WAF(FW)使わない」PHPサイトの開発(副題: でなきゃ定期的にバージョン上げろ)

微妙暴言のお時間でございます*1
いやまぁ昨今、サイト作るのにWAF/FW(Web Application Framework で Framework の略な)を使わない、って選択肢も稀かなぁと思っております。
……いやまぁ「うちのFWはSmartyです」とか言われた頃が(うっすらした殺意と共に)懐かしいのですが、昨今はそーゆー事もなく、Laravelとかチョイスされる事は多いんじゃないかと思います*2

でまぁ、別にFWがLaravelでもCakePHPでもSlimでもSymfonyでもYiiでもPhalconでもZend FrameworkでもFuelPHPでもCodeIgniterでもBEAR.Sundayでも ちいたん でもKonahaでもEthnaでもMojaviでもPiece Frameworkでもいいのですが*3 *4
割と気になるのが「そのFWのサポート寿命」と「メジャーバージョンアップにおけるハードルの高さ」。

勿論理想としては「FWのバージョンアップに合わせてこまめに調整とリファクタをしてちゃんとついていく」事で、それが出来るんならどのFW使ってもよいかと思うんですよ……次点は「FWのサポートが終了する時」で、その時に「どうするの?」ってあたりがちゃんとしているんなら。

ただ、割と見かけるのが
・FWのバージョンアップはしない(マイナーとメンテナンスは上げるけどメジャーは上げない、とか、メンテナンスは上げるんだけどマイナーメジャーは上げない、とかも含む)
・FWが「新しいバージョンのPHP」に対応していないからPHPのバージョンを上げない
ってスタンス。

いや別に「3ヶ月で終了するサービス」ならよいんですよ?
ただ、それが「年単位でビジネス上の計画も立っている」ような、それなりに末永いサイト(になるといいなぁ)である場合、ちょっとそれは「どうなんだろう???」と。

ちなみに最近「ローンチのタイミングですでに"Security fix期限が切れている"バージョンのFW*5をお使いになりやがってらっしゃる」ケースとかを以下検閲削除。
……じゃぁそれが「珍しい話か?」と問われると「割と見かける」んですよねぇ……… 勘弁してくれ orz

そーゆーのを数見てしまうと。
ふと「FW使わないで作ったほうが、PHPのバージョンはコンスタントに上げる事が条件だけど、より安全に回せるんじゃなかろうか?」とか思う事があったりするのですよ。
……いやまぁ「PHPのバージョンをコンスタントに上げる」事自体のハードルが高いんだろうけど……多分……高いかなぁ?……おいちゃんのやり方だとあんまり高くないっていうかマジでコンスタントに上げてるけど困った事ないし……そのうちその辺のノウハウ書こうかしらん? おいといて。
下手にFWに「振り回されて」「FW都合で"バージョン上げられません」って言うくらいなら「plainなPHPで開発してみたら?」とか思う事はまぁ、あったりするわけでございます。
まぁ「FWがやってくれていること」を全部自前、なので、相応のハイスペックスキルが求められますけどねぇ……*6 *7

とまぁ色々と考えたりするので。
ナニが言いたいかっていうと「運用までちゃんと想定して開発しようず、選択しようず」って話なんだけど、その辺、割と浸透してないような気がするんですよねぇ……という、「気付いて欲しい」ネタ。

ちな、Laravelは「LTS単位で乗り換えるとよいのかしらん?」って思ってたら、Laravelに熟達している複数人から「やめとけ。コンスタントにこまめに上げるのが一番楽」って言われたので、そーゆーもんらしいす。
おいちゃんは最近、Slimがお気に入り(自前のFW、どうしましょうかねぇ???)。

Slim、3から4へは「まぁまぁ書き換えがいる」んだけど「coreロジックは多分ほとんどいらない」ので……ニーズがあるんなら「Slim 3から4への移植方法」とか、どこかで書いてもよいのかも、って感じでございます。

……って話が、もうちょっと活発に行われるとよいんですけどねぇ。
PHPカンファレンスとかでしゃべれば?」ごもっともでございます orz *8

*1:いつものこと、って気がせんでもないんだが、まぁ

*2:個人的には嫌いですが、まぁ

*3:順不同

*4:色々微妙だったり懐かしかったりするんだが気にしちゃいけない

*5:えぇLaravelでございましたともさ

*6:古参だと割といけそうな気もするが

*7:……思い返すに「絶対無理だろう」と思われるメンツも相応にいらっしゃるので以下検閲削除

*8:なんとか参加は出来そうですが、ちょい前まで、参加できるかも微妙だったのですよ orz

心房細動 で 心臓カテーテルアブレーション やってきました

いやまぁタイトルそのまんまなのですが。なんかの参考にでもなればなぁ、と思いまして(笑

端的には
・大体言われている通りの事が多かった
・でも「あんまりネットでは書いてない」知見もあった
ので、その辺を織り交ぜながら。

術前
ネットに書いてある事とそんなに変わらないかなぁ、と。
なお、除毛はおいちゃんがかかった病院だと「自分でやる」「後で看護師さんがチェックする(で、おいちゃんの場合はやってもらいました)」ってくらいかなぁ。
入院したのは、手術前日でした。
やったのは、点滴用のルートを取るくらい、と、除毛。
手術の説明をうけました……まぁ一般的な「こんな手術します」「一応こんな危険性があります」などを一通り。
ただまぁ「手術の失敗の確率」もあるんだけど、失敗しても病院が「全力でリカバリしてくれる」だろうから、「リカバリまで含めて全部アウトな確率」とか考えた時に「手術、やっぱり怖いですぅ」とかいう選択肢になり得るはずもなく、スムーズに会話は終了。

手術そのもの
全身麻酔……というか正確には「しんこんすい」とか言っていたような気がするんですが、ググっても出てこない orz
説明としては「全身麻酔は麻酔科の先生がへばりつくけど、今回はそこまでではない、けど、がっちし意識はlostする」的な説明をいただいてました。
他にも色々説明はいただいていたのですが、ネットに載ってる話が多いので割愛。
んで、「今からお薬入れます」の一言で、そりゃもぉがっつりとほぼ一瞬で意識を失ってました(笑
なお、手術時間は(おいちゃんの心房細動の場合)2~3時間程度だろう、ってお話で、実際、2時間程度で終わっていたようです。

手術直後
気付いたら病室のベッドでした。「足を動かすな」って話だけ覚えていたので、しっかりと「動かさないように」気をつけていたのを覚えてます。
あと、点滴に一緒に「眠くなる薬」入れられてたようで(これは看護師さんに確認済)、ほぼまる1日、寝てました(笑
手術は朝だったので、13:30くらいには昼食をいただきました。ネットにある通り「串刺しのおかず+おむすび」でした(笑
朝食ってないから腹減ってるし食った……のですが、眠くなる薬があるからなのか胃があんまり動いて無くて、夜になっても胃に昼食が残ってる状態だったので、がっついたのはちょっと失敗だったかもしれない(苦笑
小のほうは尿道カテーテルでございました……違和感が半端ない(笑
なお、心臓カテーテルは「太もも(おいちゃんは右太もも)から3本」「首から1本」だったそうです。なんで、太ももと首に絆創膏(的な、もうちょっと医療用のやつ)。
あと、太ももは一針、縫っていました。

たしか6時間後くらいに
・傷口の確認
尿道カテーテル抜く
・看護師さんに付き添ってもらって僅かに歩行
ってな感じだったかと思います。なお「とはいえあんまり動くな」とか言われてました。

なお夜も串刺し飯でした(笑

翌日
ここ、ネットではあんまり見かけなかったのですが。
・心臓というか肺というかあたりに微妙な違和感なんだか圧迫感なんだか(+僅かに咳)
・発熱。おいちゃんは37.4~37.6度くらいを推移
ってな感じでした。
医師に聞くと「発熱は3~4割くらいの確率である。とりあえずこれくらいなら大丈夫」との事でした。
まぁ(朝に採血からの)血液検査、心電図とレントゲンとってその結果を見つつ、でしたが。
……ぶっちゃけ「このタイミングでコロナ?」とか、幾分、ドキドキではありました。
で……足の一針を抜くのですが、結構いたかった(苦笑
あれ、麻酔とか出来ないモンなんかねぇ???

翌々日
予定だと退院……で、予定通り退院。
発熱は36.8~37.1くらいで推移。
言われた辺りとしては
・一週間は入浴禁止(シャワーは可)
・一ヶ月は「激しい運動禁止」。激しいの定義が「脈拍110を超える」なので、ウォーキングを超えると結構アウト臭(笑
・ストレスは大変によろしくないのでとにかく無理すんな
あたりが印象的だったかなぁ、と(笑

でまぁ数日。
・思った以上に心肺機能が弱ってる
・発熱はじわりと続いてる(多分、ゆっくり治癒が進んでる)
ってあたりが個人的にはポイントかなぁ、と。
おいちゃんは「寝ると色々治る」体質のようなので、「ちょっと頑張り目にウォーキング、寝る」を1日に2~3セット繰り返してます。

で……「退院したら普通の生活に戻れます」とか書いてある所が多いように記憶をしているのですが「身体にそこそこダメージが残ってるので、しばらくは安静に平穏におとなしくしている」必要があるなぁ、と思いました。
まぁ「ハツ焼いてる」しな(笑
退院後、1週間くらいは「休み、ないし半休」くらいは出来るようにしておいたほうがよいと思います*1

あと、書きたかったのがお金の話。
「高額療養費制度がないとほんのりと死ねます」www

うちの病院の場合で、3割負担で、手術だけで60万弱www
高額療養費制度の上限突破しますが、高額療養費制度の上限で大体止まるので(+1%があるから、ちょっとだけはみ出る)。
いくらになるかは収入次第ではありますが*2、申請しておいたほうがよいんじゃないかなぁ、と。
申請は、市区町村(だったと思う……都道府県単位ではなかったような記憶がかすかに)単位でも違うんでしょうが、うちの場合は
・役所に行ってもいいし
・郵便で送ってもらってもよいし
って感じで。おいちゃんは郵送でお願いしましたが、速やかにいただけました。必要書類だけ用意しておこう。

まぁ、満額支払ってもたしか後で返ってきたような記憶があるのですが。


とまぁ。
術前術中は大体「ネットにある通り」なのですが、術後のお話が「いくつか、あんまり載ってなかった」のと、お金の話が思ったより重要だなぁと思ったので(笑
とりあえず「治療行為とはいえ、一時的には身体にダメージが入るから、しばらくはおとなしくしてような!!」というお話でございました。

……うん、頑張って出来るだけ休憩するようにするお orz

2020/11/10追記
「発熱」と「動機っぽいの」と「胸部の圧迫感っぽいの」が大体おちついた。
手術からだと5日、退院してからだと3日経過。
まぁ冷静に考えたら「早い」ような気もするんだけど、これくらいはかかるねぇ癒えるのに。

*1:思います……思います orz

*2:……無言

お仕事コードの書き方(の一部)

おいちゃん、経験的に、割合とPHPが長くなってきております。
んで、PHPでコード書くとき、割と色々と「丁寧に」書く癖があって。
特に昔は割と嫌がられたりしてたもんだけど、おかげでバージョンアップしても「平気の平左で」受け入れられるんだよなぁ、とかふと思い出しまして。
……なんて事を考えるとお仕事コードの書き方で「おいちゃんが気にしている事」があって、その辺を書いておくとなんか役に立つ事もあるのかなぁ? と思い、雑文を散らかしてみようか、と。

端的には
・キワを攻めない
に尽きるのですが……かみ砕いて。

例えばif文の評価式は最終的にbooleanが欲しいものでございます。が、関数やらメソッドやらの戻り値が、空文字だったり数値0だったりNULLだったりする事もございます。
とはいえその辺は暗黙の変換があるので

if (null !== 関数())

とか書かなくても

if (! 関数())

と書けば「nullがreturnされてきたからbooleanだとfalseだからそれの否定だから」で簡単に処理を書く事ができます。

複数の条件式があっても、括弧で囲わなくても演算子の優先順位があるから、優先順位をちゃんと理解していれば括弧なんて不要です。

型とか気にしなくても、例えば「文字が欲しい」ところに数値0書いたってよしなに解釈してくれます。

その他諸々。
言語仕様を「熟知していれば」こその、「一見不思議なんだけど、言語仕様を熟知していれば理解できる」書き方ってのは、色々とございます。

おいちゃん、上述を一通り「大変にお好まない」んですねぇ。
関数の戻り値云々で、多分これはそこそこ引っかかっている人もいたんだろうなぁと思われるstrpos()。
正直、あの問題おいちゃんは「1度も引っかかった事がない」んですが、マニュアルにも書かれているくらいなんで「結構引っかかった人もいるんだろうなぁ」とか、しみじみ。

っつかif文とかwhile文とかの条件式を書く所はちゃんと「条件式」で。
かつ、==ではなくて===で「型を意識して厳密に」。
かつ昨今なら「declare(strict_types=1);」付けて。
この辺を死守しておくと、少々ナニカがあってもあんまり困りません。

括弧はちゃんと付ける。
「これくらいは演算子の優先順位があるから」と思っても、異なる演算子があるんなら「このくくりと」「このくくりと」がわかるように、しっかりと丁寧に書く。
省略しない。

……若干悩むのが「インクリメントとかの前置と後置」ですが。
まぁPHPの界隈だと「そのテクニック使うな」って言われる事が多いですかねぇ。なので使わないようにしてます。

「ンなこと言ったってその辺の挙動、そうそう変わらないよ」ってよく言われるんですが。
【PHP8.0】非厳密な比較演算子`==`の挙動が今さら変更になる https://qiita.com/rana_kualu/items/82cc8295d2102d14b88a
【PHP8.0】演算子.と+の優先順位が変わる https://qiita.com/rana_kualu/items/db7ae541016bd0b02122
諸行無常。一切のものは流転し変化し、変わるものでございます。

まぁ関数やメソッドの戻り値は「falseかしら? NULLかしら?」って調べるのが若干面倒かもしれませんが。
括弧付けるくらい、そんなに手間ですかしらん?
って思ってるです正直。

こーゆー思想で作ってるので。
(最近、Slimにどっぷりハマって放置してますが)自前のフレームワーク、PHP5からPHP7に対応させるとき、ほぼ修正いらずでした(一桁行くらいだったかし、修正したの)。
逆にこの辺が雑なコードをPHP5からPHP7に移植させるとき、驚くほど手間がかかりました。

いやまぁ「いつどこにコストをかけるのか?」って話なんだろうとは思うのですが。
どうせなら「その言語にちょっとくらい不慣れな人」が読んでもすんなり読めるような、丁寧なコード書いてたほうが色々とメリットも多いんじゃ無かろうか? と思うんですが、どうなんですかねぇ?

駄目なUUIDv4実装

「実装によってはUUID4が重複しうる」って話を聞いて「じゃぁ駄目な実装を書いてみよう」と思いました(笑
珍しく「徹頭徹尾、非実用」です(笑

<?php
mt_srand(time());
printf("%08x-%04x-4%03x-%04x-%012x\n"
    , mt_rand(0, 0xffffffff)
    , mt_rand(0, 0xffff)
    , mt_rand(0, 0xfff)
    , mt_rand(0, 0x3fff) + 0x8000
    , mt_rand(0, 0xffffffffffff)
    );

これだと、同じ秒で(別マシンだろうがなんだろうが)同タイミングで作成すると、ちゃんと(?)重複します(笑
いやまぁ「普通に考えて、最低限 random_bytes() だろ」とか思うのですが、そこはほら、実験という名のお遊びなのでwww

なお

mt_srand(time());

がキモになりますので、絶対に省略しないでください。省略されると、ちゃんと(?)重複しません(笑

うんなんか書いたら満足したwww

魔力と魔法、魔術

世界には不思議な力が満ちていた。
先人はそれを「摩訶不思議な力」と呼称し、それが転じて「魔力」と呼称された。

「極度にとがった天賦の才」を持ったもののみが扱い得る魔力であったが、徐々にその法則の端々が判明していった。
「魔力を扱うための法則」、魔法である。

理論としての魔法を追うように、魔法を扱う術(すべ)についての試行錯誤と考察、実践もまたノウハウが蓄積されていった。
「魔法を扱う術(すべ)」、魔術である。

かくして。

あるものは魔法を研究して新たなる法則を発見し。
あるものは魔術を修練して極めて強大な術を行使するようになった。