gallu’s blog

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

Slim4-Skeleton解析のためのSlim4解析のためのPHP-DIの解析

タイトルが長い(笑

いやまぁ色々と調べていてそれはそれで後で記事にするのですが。
Slim4で使われているContainerについてちょいと調べたので、備忘録かてがて。

Slim3では "pimple/pimple" が使われていたのですが、Slim4では "php-di/php-di" が使われています。
……いや「plainなinstallすると色々とアレがナニ」な感じで色々と色々なのですが、その辺はまた別に記載いたします*1

似たようなもんか……と思いきや色々と差異があったりするので、その辺を調べてみました。

一端、ざっくりとSlim4をインストールしてある、とか思ってくださいませ。
ちと別件もあるので少し余計なものが入ってますが、composer.json

{
    "require": {
        "slim/slim": "^4.4",
        "twig/twig": "^3.0",
        "php-di/php-di": "^6.0",
        "slim/psr7": "^1.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^9.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "app/"
        }
    }
}

大体こんな感じ。

で、まずは準備。

<?php
declare(strict_types=1);

// 基準になるディレクトリ(最後の / はない形式で)
define('BASEPATH', realpath(__DIR__ . '/..'));

// オートローダ
require(BASEPATH . '/vendor/autoload.php');

/*
 *
 */
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;

// Builderインスタンスの生成
$containerBuilder = new ContainerBuilder();
$container = $containerBuilder->build();
var_dump( get_class($container) );

どうもお作法として
・まずcontainerBuilderを作る
・containerBuilderからcontainerを作る
って感じらしいので、上述のように。

string(12) "DI\Container"

ってな感じで、OK。

以前の、Slim3(というかpimple)のようなやり方を一応試してみる。

//
$container['test'] = function ($c) {
    return \stdClass();
};
var_dump($container->get('test'));
Fatal error: Uncaught Error: Cannot use object of type DI\Container as array in 

一発でアウト。
ほむ。配列系のやつ*2、持ってないんだなぁ。

で、色々と調査。
どうも
・設定は「ContainerBuilderに対して行う」
・メソッド「addDefinitions()」を使う
ようなので、ちょいとコードを書き換え。

大まかには

<?php
declare(strict_types=1);

// 基準になるディレクトリ(最後の / はない形式で)
define('BASEPATH', realpath(__DIR__ . '/..'));

// オートローダ
require(BASEPATH . '/vendor/autoload.php');

/*
 *
 */
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;

// Builderインスタンスの生成
$containerBuilder = new ContainerBuilder();

// Builderに対して設定
XXXXXXXXXXXXXXXXX

// Containerインスタンスの生成
$container = $containerBuilder->build();

// 以下、getつかって色々
XXXXXXXXXXXXXXXXX

って感じになるぽ。
最後に全ソース書くので、一端、局所で説明していきます。

まず、Slimだとお馴染みsetting。

// 配列の設定
$containerBuilder->addDefinitions([
    'setting' => [
        'setting_test' => 'OK',
    ],
]);

ってやると

var_dump($container->get('setting'));

で取れます。

array(1) {
  ["setting_test"]=>
  string(2) "OK"
}

うんまぁこの辺はよし。
次。

古くからある「あらかじめDIに書いておくとどこからでも同一インスタンスが取得できる」のは、今回「Definitions」って呼称するようです。
設置の仕方は上述と一緒。

// 「Classを明示的に設定」のテスト: Definitions
class Foo {
}
//
$containerBuilder->addDefinitions([
    'testFoo' => function(ContainerInterface $c) {
        $obj = new \Foo();
        $obj->s = $c->get('setting')['setting_test'];
        return $obj;
    },
]);

ってやると

//
var_dump($container->get('testFoo'));
var_dump($container->get('testFoo'));

object(Foo)#28 (1) {
  ["s"]=>
  string(2) "OK"
}
object(Foo)#28 (1) {
  ["s"]=>
  string(2) "OK"
}

って取れまして、同じインスタンスNo持ってるので「同一のものが取れている」事がわかります。
だからまぁ「addDefinitionsで括ってやれば大体Slim3の頃と同じ事が出来る」で、基本は終了でございます。
とりあえず最低限としては「Slim3で"配列への代入"で書いていた所を、addDefinitionsメソッドで包んでやればOK」って感じぽい。


ここから、追加調査。
少し気になる記述があったので試してみる……曰く「Autowiring」。
http://php-di.org/doc/autowiring.html

Autowiring is an exotic word that represents something very simple: the ability of the container to automatically create and inject dependencies.

ぐぐる先生翻訳

自動配線は、非常に単純なものを表すエキゾチックな単語です。依存関係を自動的に作成および注入するコンテナの機能です。

自動的? automatically create? ふぁ?

実験。

// 「解決出来るクラス名渡したら設定しなくてもDIしてくれるよ」テスト: Autowiring
class Test {
}
var_dump($container->get('Test'));
var_dump($container->get('Test'));
object(Test)#37 (0) {
}
object(Test)#37 (0) {
}

………取れてる………しかも(ある意味)ちゃんと「シングルトン(同一インスタンス)」で。
……いいのかどうか、悩む挙動だなぁ。

なお試してませんが

$containerBuilder->useAutowiring(false);

で、この挙動はoffに出来るぽい。
……幾分色々と思案される所ではあるような気がする……なんとなく、おいちゃん的には(=業務的には)offっておいたほうが安全な気がせんでもない。

お次。
……どうも「コンストラクタの時、型指定をすると"その型のインスタンスをgetして取ってきてくれる"」とかいう、絶妙に微妙な機能があるっぽい*3

とりあえず、まずは「無指定の時」の実験。

class Test2 {
    public function __construct($o) {
        $this->o = $o;
        var_dump( get_clas($o) );
    }
}
var_dump($container->get('Test2'));
var_dump($container->get('Test2'));
echo "\n";
Fatal error: Uncaught DI\Definition\Exception\InvalidDefinition: Entry "Test2" cannot be resolved: Parameter $o of __construct() has no value defined or guessable

をや?
「has no value defined or guessable」?
あぁそうかここ別に「なにかが必ず渡ってくる」って訳でもないから、か。

では改めて「型を指定して」実験。

class Test3 {
    public function __construct(Test $o) {
        $this->o = $o;
    }
}
var_dump($container->get('Test3'));
var_dump($container->get('Test3'));
echo "\n";
object(Test3)#36 (1) {
  ["o"]=>
  object(Test)#37 (0) {
  }
}
object(Test3)#36 (1) {
  ["o"]=>
  object(Test)#37 (0) {
  }
}

確かに入ってるなぁ。しかもちゃんと「Container経由」のインスタンスが。

……あれ? ってことは?

//
$containerBuilder->addDefinitions([
    'testFooTest' => function(Test $c) {
        $obj = new \Foo();
        $obj->s = $c;
        return $obj;
    },
]);

してから

var_dump($container->get('testFooTest'));
var_dump($container->get('testFooTest'));
echo "\n";
object(Foo)#43 (1) {
  ["s"]=>
  object(Test)#40 (0) {
  }
}
object(Foo)#43 (1) {
  ["s"]=>
  object(Test)#40 (0) {
  }
}

予想通りではある……便利とも言えるけど、気をつけないと割と諸刃っぽいなぁ。

でもまぁせっかくなんでもうちょっと突っ込み。
暗黙のクラスは「自作のクラスじゃなくて、PHPが元々持っているクラス」でも行けるのか?

class Test4 {
    public function __construct(\ArrayObject $o) {
        $this->o = $o;
    }
}
var_dump($container->get('Test4'));
echo "\n";
Fatal error: Uncaught DI\Definition\Exception\InvalidDefinition: Entry "Test4" cannot be resolved: Entry "ArrayObject" cannot be resolved: The parameter "input" of __construct() has no type defined or guessable. It has a default value, but the default value can't be read through Reflection because it is a PHP internal class.

ほむ。
「it is a PHP internal class.」
なのか。んじゃこれは駄目だな。

……「クラス名じゃないけど、Containerに指定しているkey名」だと、どうだろう?

class Test5 {
    public function __construct(testFoo $o) {
        $this->o = $o;
    }
}
var_dump($container->get('Test5')); // Fatal error: Uncaught ReflectionException: Class testFoo does not exist in /
echo "\n";
Fatal error: Uncaught ReflectionException: Class testFoo does not exist in ...

うんまぁそうかPHPのparseでエラーになるかそりゃそうだ。


というわけで
・とりあえず「Slim3の頃と同じような感じで使いたい」なら「ContainerBuilderのインスタンスにaddDefinitions()してからbuild()メソッドで$containerインスタンスを作る」
・containerのget、或いはaddDefinitions()に渡すfunction()の引数、またはコンストラクタに「自作クラスのクラス名」を渡すと、それを自動的に取ってきてくれる
ってのがわかった感じ。

一応「Slim3からの移植」にはさほどの差し障りがないなぁ、というのと「ちょっと便利が過ぎて少し微妙」な機能があるので、使いどころかなぁ、的な印象でございます。

最後に、今回のコードの大体の全景を。

<?php
declare(strict_types=1);

// 基準になるディレクトリ(最後の / はない形式で)
define('BASEPATH', realpath(__DIR__ . '/..'));

// オートローダ
require(BASEPATH . '/vendor/autoload.php');

/*
 *
 */
use DI\ContainerBuilder;
use Psr\Container\ContainerInterface;

// Builderインスタンスの生成
$containerBuilder = new ContainerBuilder();

// 配列の設定
$containerBuilder->addDefinitions([
    'setting' => [
        'setting_test' => 'OK',
    ],
]);

// 「Classを明示的に設定」のテスト: Definitions
class Foo {
}
//
$containerBuilder->addDefinitions([
    'testFoo' => function(ContainerInterface $c) {
        $obj = new \Foo();
        $obj->s = $c->get('setting')['setting_test'];
        return $obj;
    },
    // タイプヒンティングで「別のクラス」を指定したら、(解決できるなら)それを解決する
    'testFooHoge' => function(Test $t) {
        $obj = new \Foo();
        $obj->o = $t;
        return $obj;
    },
]);
//
$containerBuilder->addDefinitions([
    'testFooTest' => function(Test $c) {
        $obj = new \Foo();
        $obj->s = $c;
        return $obj;
    },
]);

// Containerインスタンスの生成
$container = $containerBuilder->build();

//
var_dump($container->get('setting'));
echo "\n";

//
var_dump($container->get('testFoo'));
var_dump($container->get('testFoo'));
echo "\n";

var_dump($container->get('testFooHoge'));
var_dump($container->get('testFooHoge'));
echo "\n";


// 「解決出来るクラス名渡したら設定しなくてもDIしてくれるよ」テスト: Autowiring
class Test {
}
var_dump($container->get('Test'));
var_dump($container->get('Test'));
echo "\n";

/*
// Fatal error: Uncaught DI\Definition\Exception\InvalidDefinition: Entry "Test2" cannot be resolved: Parameter $o of __construct() has no value defined or guessable
class Test2 {
    public function __construct($o) {
        $this->o = $o;
        var_dump( get_clas($o) );
    }
}
var_dump($container->get('Test2'));
var_dump($container->get('Test2'));
echo "\n";

*/

class Test3 {
    public function __construct(Test $o) {
        $this->o = $o;
    }
}
var_dump($container->get('Test3'));
var_dump($container->get('Test3'));
echo "\n";


//
var_dump($container->get('testFooTest'));
var_dump($container->get('testFooTest'));
echo "\n";

// これは駄目なのか:It has a default value, but the default value can't be read through Reflection because it is a PHP internal class.
class Test4 {
    public function __construct(\ArrayObject $o) {
        $this->o = $o;
    }
}
//var_dump($container->get('Test4')); // Fatal error: Uncaught DI\Definition\Exception\InvalidDefinition: Entry "Test4" cannot be resolved: Entry "ArrayObject" cannot be resolved: The parameter "input" of __construct() has no type defined or guessable. It has a default value, but the default value can't be read through Reflection because it is a PHP internal class.
//echo "\n";

// 流石にこれは駄目か
/*
class Test5 {
    public function __construct(testFoo $o) {
        $this->o = $o;
    }
}
var_dump($container->get('Test5')); // Fatal error: Uncaught ReflectionException: Class testFoo does not exist in /
echo "\n";
*/

*1:使うんだから composer.json に書いておいて欲しい orz

*2:ArrayAccess インタフェース

*3:「暗黙」ではないからそこまでNGだとは思わないけど、「よくわからずに使う」と色々と面倒そうなので、あんまりよい匂いを感じない

PHPで「型宣言された引数の型の名前」を知る方法

元々は、Laravelで興味深い機能*1を見かけたのが初手で。
その後、(近々公開しますが)Slim4のContainerでも似たような事をやっていたので、ちぃと思い切って調査をしてみました。

端的には
・関数(メソッド)宣言で引数に型宣言をしている時に、「なんのクラスを宣言しているのか?」を知る方法
です。

んと……色々と手間を省くためにざっくりしたコードで恐縮ですが。

class Hoge {
    public function t(\Exception $e, \ArrayObject $ar, string $s, int $i) {
    }
}

こんなクラスがあった時に
・t()の、第一引数には\Exception、第二引数には\ArrayObject、第三引数にはstring、第四引数にはintが型指定されている
事を知りたい時があるわけですよ……多分、フレームワーク作ってるとかじゃないとあんまりなさそうなシチュエーションですが、フレームワークとかの「汎用系ルーチン」作ってると、こーゆーの、稀にあります。

で、調べてみたのですが……わかれば割と簡単。
Reflectionを使ってたんですねぇ(少なくとも Slim4のContainerであるPHP-DIでは。Laravelが使ってるのか「別の方法なのか」は知らにゃい)。

ざっくり、コードを書いてみました。

<?php

// 調査用クラス
class Hoge {
    public function t(\Exception $e, \ArrayObject $ar, string $s, int $i) {
    }
}
// 調査用関数
function t(\Exception $e, \ArrayObject $ar, string $s, int $i) {
}

/*
//
$ref = new \ReflectionFunction('tt');
//$ref = new \ReflectionMethod('Hoge', 't');

//
$params = $ref->getParameters();
//
foreach($params as $p) {
    echo $p->getType() , "\n";
}
*/

// 一行にまとめてみるとこんなん
foreach(( new \ReflectionMethod('Hoge', 't'))->getParameters() as $p) {
    echo $p->getType() , "\n";
}

説明は省略(笑
質問があったら、コメントにでも書いて貰えれば返答できるかと思われます。

いやわかってしまえば「あぁそりゃそうか」なんですが。
Reflection、ざっくり斜めには見てるけど、あんまり「がっぷり四つに組んで」までは見てないからなぁ。
今度、腰を据えて丁寧にじっくりとガッツリと見てみるのも面白いかもしんない。

以上、ちと興味深くて「割とず~っとうっすらと気になってた」機能の調査でした。

*1:調べたら、FormRequestと言うそうです

エアレーションとバイオビーズ

……なんかすっかりアクアリウム色(笑

1/28
エアレーションとバイオビーズ対策

エアレーションは
・水作 水心 SSPP-7S
が手に入ったので、

・堅くならないシリコンチューブ 2m
・調整弁
・逆流防止弁(298円)
・吸盤(90円くらいだったか?)
・ADA ジョイントグラス JG-001
・いぶき セラミックエアストーン 10φ丸 #100
をアクアフォレストさんで購入。

ついでに、ダイソーで(ほぼなんとなく)
・4つ入りで100円の吸盤
を購入。

セッティングは概ね好調。ただ、アクアフォレストさんでかった吸盤、エアストーンの真上に付けるとあっさりと外れる……のでダイソーのにしてみるとまぁこれが強力(笑
吸盤の径が違うしなぁ。
アクアフォレストさんで買った吸盤は水面付近にして、全体的に一段落。
……まぁエアポンプが思いっきり直置きなのであんまりヨクナイぽいんだけど。明日にでも、百均でなんか適当な台を購入予定。

調整弁は、想定してなかったんだけどお店の人に言われて買ってみて……大正解。
多分、今、3割くらいに絞ってます。それでも結構、水が飛ぶので。ガラスの蓋はあるんだけど隙間があるので、ティッシュ置いてます。多分、いい感じ。


バイオビーズは………大失敗 orz
AT-20の真ん中にプラ板の仕切り付けて……って改造で開いた背後に入れたんだけど「全部、水槽にバイオビーズが漏れ出ました」 orz
善くも悪くも、思ったより隙間があちこちあったらしい(今回の件としては悪い)。

色々悩んだのですが。
とりあえず https://shibutani4488.site/aquarium/81/ こちらにあるような感じで
・真ん中のプラ板の仕切りは外す
・全体をカゴで包む
をベースに、今ある カスタムイン50を生かしつつ、バイオビーズをなんとか「外掛けフィルター内にとどめる」工作を頑張ってみませう。

………さて、どうやってバイオビーズ拾おうかなぁ………


で、そろそろパイロットフィッシュが視野にあるので水質検査。
Ph 6.4
亜硝酸 多分0
硝酸 25くらい
と……なんか思った以上に安定してる?

これなら、(バイオビーズ問題が片付けば)週末くらにはパイロットフィッシュを召喚できそう、かも。

経過観察

1/27
超備忘録。

いち。
水槽の縁に微妙に「消えない泡」。汚れが処理仕切れてないみたい。まぁある程度もくろみ通りなので、生体のいない(水草だけの)水槽に、今日もきょうとて、少量の「アカヒレ用のエサ」を投入。
明日くらいまでかなぁ。水曜日は何も入れず、木曜日の水質の数値が異様なモノでなければ、予定通り今週末にパイロットフィッシュ・アカヒレさんを召喚しませう。
数値がアウトな感じだったら、(水替えする、ではなくて)もうしばらく、水草だけで回しておく予定。

に。
AT-20で、どうも「少し高い目の位置から水を落とす」ほうがエアレーション的効果が望めそうなので。
超手っ取り早く「外掛けフィルターの下に台を置いて少しだけ持ち上げる」作戦。うちの20cmキューブの水槽で、95mm~100mmくらいの高さがよいぽい。
百均で100mm程度の高さの木の箱を購入。心持ち、5mm程度高めではあるけど、いい感じ。

さん。
やっぱり外掛けフィルター AT-20のお話。
バイオビーズをふよつかせる空間が微妙に狭い……中間のフィルタが膨らんで、前面にはカスタムイン入れてるんだけど、後部の「バイオビーズ予定空間」が狭すぎる……ので、割と躊躇なく、あっさりと改造。
やったのは
・正規のバイオパックを外す
プラ板で仕切りを作る
くらい。

バイオパックの活性炭、もったいないから使おう……かと思ったんだけど案外にでかいので水槽に微妙に入れにくく、速攻で「お疲れ様でした」。
プラ板、厚さ0.3mmとかで「えっれぇペラッペラ」で不安をもちながら。とりあえずバイオパックの、白いフィルタ外した骨格に大体添うようにカットしてみる。……一発で入る。
底に半分だけ切れ目をいれて水が通りやすいように加工。
多分「プラ板だけ」だと上手く止まらないんだけど、カスタムイン入れると丁度よい押さえになるので、いい感じ。
ただ、ちょいと不安なんで、プラ板をもう1枚切り出して重ねる……この手の薄いプラスチック、ぬれてると大変によく張り付くので、楽(笑
高さを軽く調節して終了。所要時間5分程度(笑

これで、明日到着のバイオビーズが楽しみってぇもんである。

追記
マツモが底面から抜けた(苦笑
ある意味丁度よいので、そのままふわふわと漂わせっぱなしにしてます現在。

ハイグロフィラ ポリスペルマが、5本中、1本だけ抜けちゃいましたなぁ。再度刺そうと思ったんだけど、なんかへにゃっとなってて刺せない。
これもとりあえず漂わせておきませう……なんか元気がないので、しばらく様子見、かな。

ボトルアクアリウム

妄想、かつ、メモ。

気にしてるのが「いろんなサイクルが回しにくいなぁ」って思ったんだけど、ふと、アイデアが浮かんできてしまったので、とりあえずメモ。
後で推敲しませう。

ボトルは「パンドルチェの瓶」。直径8cm、高さ15cmの円柱、なので、多分、お水は700mlも入らないと思われる、を、前提に。

底面は適度になにか。なんとなし「吸着系のソイル」が良さそうな気はしている。
立ち上げ時に「第一水槽でしばらく泳がせたバイオビーズ(か、フィルターのバイオビーズを何割か拝借)」を入れてバクテリアを移植 & お水も第一水槽から移動。

メインは水草
ウィローモス
・マツモ
(・アナカリス)
あたりかなぁ。

で。「第一水槽のメンテナンス」の時に
・ボトルから1/3~1/2くらい水を抜く
・第一水槽の水をボトルに入れる
を追加する事で「第一水槽の汚れた(=栄養豊富な)水」を定期的に追加。

で、藻がでたりしたら、第一水槽のミナミヌマエビくんを1~2匹。水合わせも
・適当なタッパに、第一水槽からミナミヌマエビくんを移動させる
・ゆっくりと水を合わせる
・そのままボトルにぶち込む
で、(ボトルアクアリウムはヒーター入れてないから)温度も大体合わせられるから、移動させやすいだろうし。

光は「第一水槽の横」で、第一水槽の光をそのまま拝借(必要なら後でライト買えばよいし)。

これだと
・機材がそんなにいらない
水草の領域を増やせる
・ボトルアクアリウムでいくつか気になってた所が(とりあえず)解決する
ので。第一水槽が立ち上がった後、であれば、まぁまぁいけるんじゃなかろうか?

後で見直して推敲しよう。
どのみち、第一水槽がちゃんと立ち上がった以降のお話、だし。

………これはまぁ「元々あったものを使う」ので、増えるにはカウントしない、ってことで、一つ(笑

アクアリウム始めました(笑

当初は「ボトルアクアリウム」の予定だったんだけどなぁ……なおいちゃんの、主にメモ書き(笑

1/21に、アクアリウムの先輩(笑)から「ウィローモスならちょうど増えすぎて刈らないとなんで、刈ったやつ送りましょうか?」との申し出があり、ほいほいと好意に甘える(笑

一発目のお買い物は1/22。
本当は、アスウォット株式会社さん( http://aswot.com/baghis/ )の「パンドルチェ」の空き瓶でボトルアクアリウムを作ろうか、と思ったのですが。
空きスペースはかって(最大で20cm*30cm)、お買い物に行って思案している内に方針変更がありまして。

・コトブキ レグラスR-200(200×200×200mmサイズ、水容量7L)
・テトラ オート ワンタッチフィルター AT-20
・コトブキ フラットLED SS(20~32㎝水槽、全光束190lm)
・プリセットオートAR30(25.5~27.0℃ ±1.5℃:4W)
・アクシータイマー(定格入出力容量:1500Wまで)
・水作 プロホース エクストラ Sサイズ

あたりを購入。
底は「サンディブライト」の、粒の粗めなほうを購入。
あとは「スドー 水草のソフトおもり 巻き型」「流木」を、水草用に購入。カルキ抜きは「水替えプラス」、あと一応「BICOM スターターキット」を購入。

一式、アクアフォレスト新宿店さん( http://aquaforest.tokyo/ )にお世話になりました(ってかそれ以降の買い物も大分とお世話になってます)。

で、送って貰える水草
南米ウィローモス
オレゴンリバーモス
マツモ
アマゾンフロッグピット
との事。色々調べて見て、ワクテカを募らせつつ、まずは水を回し始める。スターターキットも投入……ちょっと早まったかなぁ、とか思いつつ。


翌日
1/23 に、色々調べて、生体濾過系のブツが足りないなぁ、と思って
マツダ ストレーナースポンジ ミニ 細目
・パワーハウス (AAJ9Q) カスタムイン 50 ソフトタイプ
を追加で購入。あと、アンモニアがないなぁ、と思ったので、アクアフォレストさんが「ソイル少量」ってのを売ってたのでそれも購入……そういえばどの種類なんだろう? 今度聞いてみよう。

で、水草が届いたので早速レイアウト。
レイアウトには
・絹糸(木綿糸ってあったけど100均にあったのが木綿糸だった)
・入れて貰ったビニタイ
・買ったソフトおもり
を使って。ソイルは、砂利の上にうっすらと蒔きました。ソイルがくずれたらプロホースで吸い上げればいいかなぁ、とか思ってますが、ど~なんでしょう??

あとは
・小型のあみ
・でっかいスポイト(水合わせ用)
をアクアフォレストさんで購入。

100均で
・プラスチックのパッド(作業用)
・二リットルの麦茶とか作る容器(水替え用)
・500mlの計量カップ(水を注ぐとき用)
・鏡(側面において、光を反射させたら具合がよいかなぁ、と)
を購入。
ピンセット代わりは菜箸。いや便利便利。これでいいかなぁ、って思うくらいに(笑

水槽がちょいと白濁してる……けど、水草だけだしあんまり気にしない。


その翌日
1/24 に、「もう一つくらい水草が欲しいかなぁ」と思って「簡単そうで」「根っこをはってくれる子(底面の環境がよくなると聞いた)」ってことで
・ハイグロフィラ ポリスペルマ
を選んでいただいて購入。
すげぇ刺しやすい(笑
あと、水質チェックした方がよいよ、と言われて、「テトラ 6in1」も購入。
チェックするまでもなく白濁が消えた……思ったより早いなぁ。こーゆーものらしい。


1/25
パイロットフィッシュの投入時期に悩みつつ、一瞬「………アンモニア水買うか?」とか悩んだ経由、「パイロットフィッシュに与える餌を少量入れる」に落ち着いたので。
アカヒレ飼うつもりなので、アクアフォレストさんでアカヒレに与えてるって言われて
・お魚ぱくぱく
を購入。
15粒くらい投入(明日からもうちょっと控え目予定)、ふと「今の水を一回はかろう」と思ってはかってみると「亜硝酸 1mg」「硝酸 25~50mgの間」くらい………あれ? アンモニアの数値を図りたい所ではあるんだけど、なんか思ったより落ち着いてる???

とはいえまぁそこは「こってこてが好きなおいちゃん」なので。
色々調べて悩んだ挙げ句(結構色々、ろくでもないことを考えた経由)。とりあえず「バイオビーズ」なるものが比較的よろしそうなので、購入予定。さっき、チャームさん( https://www.shopping-charm.jp )でぽちった。
ついでに
水草メンテ用はさみ
・(安かったから)ピンセット
・具合の良さそうなスポイト(GEX ジェックス メダカ元気 スポイト)
を購入。
……なんか調べると、値段も色々あったり、モノによってはヨドバシさんで扱ってたりもするから、そろそろ「落ち着いて調べてから購入」とかしてもよいかも、とか思ってみたり。

 

とりあえず今後の予定としては
・1月末か2月頭に、パイロットフィッシュとしてアカヒレを一匹、召喚
・2月半ばに、アカヒレをもう一匹、召喚
・3月に入ったら、本命のミナミヌマエビを、5~10匹、召喚
する予定でございます。

いにしえ(かもしれない)技法:bit演算変

bit演算を使ったプログラミング、割と「知られてないのでは?」という感じがあったので。
ここでは少し抽象的な部分が混ざってしまうのだけど、まぁなんかの一助になるかなぁ、くらいの感じで、めも。

大まかには
・処理が「A」「B」「A+B」みたいな感じの時の処理の共通化
・いわゆるフラグ的に on/off 出来るステータスがあるようなもの
の時に便利だったりします。

前提

まず、2進数はよいよね?
2進数は、各桁が「bit」って言い方をします。
んで、1だと「立ってる」、0だと「寝てる/立ってない/落ちてる」とかって言い方をする事が、まぁ。
1の「立ってる」は割と共通な気がするなぁ。0はあちこち方言がありそうだけど。

んで。論理演算のANDは「あるbitが、立っているか寝ているか」を判定するのに割と便利につかえます。

例。
変数 $x*1の1桁目(2^0)が立っているか寝ているかを判定する場合は

if (0 !== ($x & 0b0001)) {
    // 立ってる
} else {
    // 寝てる
}

って判定が出来ます。まぁもうちょっと省略して

if ($x & 0b0001) {
    // 立ってる
} else {
    // 寝てる
}

って書く事が多いように思いますが。

同じように、2桁目(2^1)が立っているかどうかは

if ($x & 0b0010) {

3桁目(2^2)なら

if ($x & 0b0100) {

で判定が出来ます。
とりあえずわかりやすく二進数表記で書いてますが、実際には「1、2、4」といった、十進数で書いている事もちょいちょい。

これ自体は、2進数とbit演算を知っていれば「知識としては知っている」内容だと思うのですが、これを上手く使うと、プログラムが楽に出来ます。
いやどちらかというと「いにしえから伝わる技法」だと思うのですが、割と昨今でも普通に使う事が多々あるので。

ちなみに、こーゆーのを「マスクビット」とか「ビットマスク値」とかいう言い方をする事があるので、知っておくと、ググる時に楽……かも。

処理が「A」「B」「A+B」みたいな感じの時の処理の共通化

まぁ今回の元ネタがこっちなので、先にこっちで(笑
入ってくるパラメタによって「Aのみ」「Bのみ」「AとBの処理」をやる場合。
一番ざっくりと書くと

if (パラメタ==A) {
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
} else if (パラメタ==B) {
    パラメタBの処理;
    パラメタBの処理;
} else if (パラメタ==A+B) {
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
    パラメタBの処理;
    パラメタBの処理;
}

となるか、と思います。

で、少し共通化すると

function func_A() {
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
}
function func_B() {
    パラメタBの処理;
    パラメタBの処理;
}

//
if (パラメタ==A) {
    func_A();
} else if (パラメタ==B) {
    func_B();
} else if (パラメタ==A+B) {
    func_A();
    func_B();
}

ってなるか、と思うのですが。

これを、bit演算を使うと

$type = 0;
if (パラメタ==A) {
    $type = 1; // 0b0001
} else if (パラメタ==B) {
    $type = 2; // 0b0010
} else if (パラメタ==A+B) {
    $type = 3; // 0b0011
}
//
if ($type & 1) { // 0b0001
    パラメタAの処理;
    パラメタAの処理;
    パラメタAの処理;
}
if ($type & 2) { // 0b0010
    パラメタBの処理;
    パラメタBの処理;
}

って書く事ができます。

いやまぁ関数(メソッド)に切り出してもよいんですが。
「微妙に切り出しにくい(面倒な)処理」とかで、かつ「ここでしか使わない」ようなロジックの場合、こんな風に書くと
・重複した記述がいらない
・(慣れてれば)見やすい
ので、すっきり書けますよ、的な感じになります。

いわゆるフラグ的に on/off 出来るステータスがあるようなもの

この辺は、PHPの関数でも割とおなじみなように思うのですが。
ふと思い出しやすい所で、 file_put_contents( https://www.php.net/manual/ja/function.file-put-contents.php ) を例に。

第三引数のflagsが、まさにこのやり方をやっています。

<?php

var_dump(FILE_USE_INCLUDE_PATH);
var_dump(FILE_APPEND);
var_dump(LOCK_EX);

int(1)
int(8)
int(2)

4がいませんが、まぁ2進数にすると
0b0001
0b1000
0b0010
で、ちょうどそれぞれ「1つだけbitが立っている」値になっています。

なので、例えば「FILE_APPENDかつLOCK_EXにしたい」場合は、論理和(or)を使って「 FILE_APPEND | LOCK_EX 」とやると、$flgには10(0b1010)が渡るので。
あとは、プログラムで

if ($flg & 0b1000) {
    // FILE_APPEND用の処理
}
if ($flg & 0b0010) {
    // LOCK_EX用の処理
}

って書き方をするから便利なんですね。

……と、そういえば error_reporting( https://www.php.net/manual/ja/function.error-reporting.php )もこれをがっつり使ってましたねぇ。
こっちは https://www.php.net/manual/ja/errorfunc.constants.php で(とりあえず現在のバージョンの時の)「各定数の数値」まで書いてあるので、見てみるとよいか、と。
値を2進数にしてみるとわかるのですが、いずれも「1箇所だけ1、残りは0になってる2進数」になっているかと思います。
だから、error_reportingの値は、主にor(|)をつかって「複数指定」が出来るんですね*2

この辺、多分今でも(そして多分、今後も)なにげに出てくると思われるので、知っていると便利かなぁ、と。

*1:一応、PHPerだからねぇおいちゃんw

*2: 「bit反転(~)+and(&)による一部bit(主にNotice)寝かせ」は、邪悪な行為なのでおいちゃんのコズムではリアリティにより否定されます