がるの健忘録 このページをアンテナに追加 RSSフィード

2017-08-13

[]本当にメモ書き程度

多くの場合において

/etc/dovecot/conf.d/10-mail.conf

あたりにあると思われる、いわゆるメール受信側(POP3とか)の設定で。


first_valid_uid


ってのがあるのかへぇしらなんだ。

色々と所以があって、一部のふる〜〜〜〜いアカウントは500番台の後半くらいからアサインされてるのがあるので。

その辺のアカウントが、これでひっかかってきたお。


うんまぁ色々と面白いなぁ、と思ったので、メモり。

2017-08-11

[]CentOS7にBIND 9、のナレッジ

お引越しでインストールした&案外とあちこち躓いたので、めも。

…いやまぁそもそも「そろそろBINDやめようよ」とか思わないわけでもないのですが、一応。


インストールは、大体こんな感じ、がベース。

yum -y install bind bind-chroot

firewall-cmd --add-service=dns

firewall-cmd --add-service=dns --permanent

firewall-cmd --list-services

/usr/libexec/setup-named-chroot.sh /var/named/chroot on

# すげぇ念の為

systemctl stop named

systemctl disable named


で、設定を終えた後で

systemctl start named-chroot

systemctl enable named-chroot

OK


……なんだけど、設定ファイルの記述ミスその他で大量に躓いたので。

おいちゃんの脳内フラッシュメモリから(ある意味奇跡的に)こぼれていない情報を、整理もせずに羅列。


情報としては

journalctl -xe

で出てくる……って言われるんだけど、色々と「もうちょっと情報よこせ」って感じの事が多々。

日付抜くの面倒なんで日付入りで晒すと、/var/log/messagesに

Aug 11 17:51:49 localhost named[4886]: ----------------------------------------------------

Aug 11 17:51:49 localhost named[4886]: BIND 9 is maintained by Internet Systems Consortium,

Aug 11 17:51:49 localhost named[4886]: Inc. (ISC), a non-profit 501(c)(3) public-benefit

Aug 11 17:51:49 localhost named[4886]: corporation. Support and training for BIND 9 are

Aug 11 17:51:49 localhost named[4886]: available at https://www.isc.org/support

Aug 11 17:51:49 localhost named[4886]: ----------------------------------------------------

Aug 11 17:51:49 localhost named[4886]: adjusted limit on open files from 4096 to 1048576

Aug 11 17:51:49 localhost named[4886]: found 2 CPUs, using 2 worker threads

Aug 11 17:51:49 localhost named[4886]: using 2 UDP listeners per interface

Aug 11 17:51:49 localhost named[4886]: using up to 4096 sockets

Aug 11 17:51:49 localhost systemd: named-chroot.service: control process exited, code=exited status=1

Aug 11 17:51:49 localhost systemd: Failed to start Berkeley Internet Name Domain (DNS).

Aug 11 17:51:49 localhost systemd: Unit named-chroot.service entered failed state.

Aug 11 17:51:49 localhost systemd: named-chroot.service failed.

Aug 11 17:51:49 localhost systemd: Stopping Set-up/destroy chroot environment for named (DNS)...

Aug 11 17:51:49 localhost systemd: Stopped Set-up/destroy chroot environment for named (DNS).

こんな感じで出てくるのを、ヘッジした時の諸々。


とりあえず前提として

・外向けの権威サーバ立ててる

・プライマリもセカンダリも自前

chrootは「とりあえず念の為やっとく」


大本の設定ファイルは、 /etc/named.conf 。

実際には /var/named/chroot/etc/named.conf を見ているはずなんだけど、これは systemctl start named-chroot とか systemctl restart named-chroot とかすると、/etc/named.conf から持ってきてくれるぽいので。

修正などは、 /etc/named.conf にそのままぶちかます。

ちなみに、restartとかしないと反映されないので、修正されたらretartしよう。


大体同じニュアンスで。

directory "/var/named";

を前提に、zoneファイルは /var/named に置いておく。

これも実際にはchrootで以下略。

ちなみに、restart以下略。


ファイルをmvだったかcpだったかしようとしたら「same file云々」とかいうエラーメッセージが出たので、多分「内部的には完全に同一」だと思われ。

chrootのギミックなんだろうけど、一端ここは深堀せず。

そのうち余裕があったら。


んで、書式で結構色々と躓いたので、引っかかったところを列挙。

・named.confとzoneファイルで書式が違う。具体的にはコメントが。named.confは//と /**/が使えて、zoneは;がコメント。zoneで/**/は多分使えない

・named.confは後ろに ; がないとエラーになる

zoneファイルのパーミッションに注意。上述の起動の仕方だと named ユーザ起動になるんだけど。zoneファイルのパーミッション的に「読めない」と、エラーにはならないのに情報が見れないwww

・named.conf、allow-queryを「localhost;(たしか、デフォがこれ)」にすると「外部から見れない」ので注意。anyが正解

・同じくnamed.conf、「recursion no;」は忘れずに!! たしかデフォがyesなのだよねぇ……。「外向き」にするんなら必須なんじゃなかろうか?

・named.conf。「allow-transfer { none; };」「allow-update { none; };」「allow-query-cache { none; };」も必須。デフォだった気もするんだが、明示して悪いもんでもなかろうもん

・「version "unknown";」気分w

・プライマリには「notify yes;」「also-notify { 11.22.33.44; };」。セカンダリ散らかさないんなら、optionsん中でよいと思う

セカンダリzoneの中に「type slave;」「masters { 1.2.3.4; };」で、親からとってくる感じ。「同名ファイルがあるとNG」「ディレクトリパーミッションに気をつけろ」ってあたりがポイント? ここ、すんなり通ったからナレッジが今一つ……

zoneファイルのほう。おいちゃんCNAME好きなんだけど「CNAMEで宣言するドメインMXとかに使えない」ので注意。……はまりました orz

・あ。TXT書かなきゃ(まだ書いてない……メールサービス組む時でよいかし……)


ちな、試験は

dig ドメイン名 @localhost

dig ドメイン名 @[鯖のIPアドレス]

dig よそ様のドメイン名 @[鯖のIPアドレス] # エラーになる事

で実験。


さて引き続きいくつか実験君がまっているので……この三連休はその辺でつぶれそうな予感……

2017-07-22

[][][]多言語対応のあれこれ

ふと生徒さんに質問をいただいたのもあって。

ちょうどよいきっかけになったので、せっかくなんでBlogで。


本質的には「どの言語のどの領域」でもある程度応用が利くかと思われますが。

一応、おいちゃんの記述なんで「MySQLPHPつかったWebアプリケーション」をど真ん中に据えて、ってな感じで。


まず「ユーザからの入力について多言語対応したい」は簡単で「保存するデータ及びHTMLUTF-8にしましょう」で、fin。

いやまぁUnicodeであれば大体無問題だと思われるのですが*1HTMLとかでUTF-16って、あんまり見た記憶がないんですよねぇその辺詳しい諸氏の突っ込み求む。

ひとつポイントがあるとすると「MySQL文字コードは、utf8じゃなくてutf8mb4」ってあたりなのですが、そのためにはMySQL5.5.3+というバージョンが必要なのでそれより低いバージョンの場合はバージョンをあげましょうここに慈悲はない。


お次に「プログラム的に動的な文字の出力」をどうするか、ですが。例えば「お知らせ」とか。

この辺あたりから少し面倒になるのですがおおむね

・ユーザが「表示してほしい」言語を忖度する

忖度した結果としての「選択された言語」のデータを引っ張ってくる

ってまぁ、こんな感じ。


まぁ最終的には「ユーザに選んでもらう」でよいですし、選んだ結果は「Cookieあたりに保存」しておけばよいのですが。

初手のアクセスで「きっとこの人は日本語圏の人なのではなかろうか?」を推測したいのであれば、HTTP RequestヘッダのAcecpt-Languageを見ると、比較的、ヒントがあったり。

PHPの場合

$_SERVER['HTTP_ACCEPT_LANGUAGE']

で取得可能。帰ってくる値は、例えば

"ja,en-US;q=0.7,en;q=0.3"

ってな感じなので。初手にjaがあったら「なんとなくこの人、日本語圏の人なのではないだろうか?」と推測が可能。

それ以外は適宜しらべて。en-USとかきたら英語圏だし、それ以外で斜めに調べた限りだと「de (ドイツ語)」「es (スペイン語)」「it (イタリア語)」など。

後ろの「q=0.7,en;q=0.3」にも本来的には意味があるので、興味がある諸氏は適宜しらべたし(大まかには、各言語の優先確率)。

雑に行くんなら「先頭2文字で判断」でも、当面は困らないんじゃないかなぁ切り出しておいて問題が起きたら修正すればいいんだし(雑)。


で、あとは例えば「お知らせ」なら、お知らせテーブルに「言語」とかいうカラムをつけておいて

ja: お知らせです。

en: It is news

it:saluti

とかって感じでデータを用意して出力すれば、それでOKな感じ。どっちかってぇと「各言語のコンテンツ」用意するのが面倒だよねぇ、的な。

でもまぁそれはコンテンツ用意する人の問題なので、サイトの骨格であるシステム作成のおいちゃん的にはいったん気にしないw


さて割と一番大きな本題「HTMLなどの静的*2なファイルの文字」をどうするか、ですが。

大枠として2種類あって、かつその2種類にはそれぞれ亜種がいくつか存在します、ので、それぞれ、ある程度(もしくは簡単に触りだけ)説明をしていきたいかなぁ、ってのが、本文章の趣旨。


大まかには

テンプレートを切り替える

・出力文字を切り替える

の2種類。

それぞれ、少しかみ砕いて。


テンプレートを切り替える

いやまぁそのまんまなのですが。

例えば「ログイン」Pageがあるとして、ボタンに「ログイン」とか日本語で書かれると、英語圏の人は多分いろいろと困るです。

……いや日本のサイトで「login」って書いてあっても困らない気がビシバシとするのですが、その辺は置いといて。


てっとり早いのは「英語圏用のテンプレート」と「日本語圏用のテンプレート」とを別々に用意して出力を切り替える、って方法がありまして。

対応言語数が少ない&ページ数が少ない&更新頻度が低い(更新そのものが少ない)のであれば、割と手っ取り早い解決策だと思われます。


方法としては

・言語ごとにディレクトリを分ける

・言語ごとに拡張子を分ける

って方法がありまして。


例えば(Smartyチックに)login.tpl、ってテンプレートがあるとしますと。

ディレクトリで分ける」パターンであれば、

templates/ja/login.tpl

templates/en/login.tpl

って風に入れて。

拡張子で分けるのであれば、(Smarty的に「本当の意味での拡張子」はいじると面倒なんで)

templates/login.ja.tpl

templates/login.en.tpl

って感じにすると、切り分けられます。


上述のような切り替えの作業は、どこか一か所にまとめておくといろいろと楽ですよね。

うちのフレームワーク(MagicWeapon)であれば、viewクラスのmake_template_filename()メソッドを上書きして、って感じかなぁ。

まぁ大体の*3コードであれば、どこかしら「テンプレートのファイル名を取得する」的な一点があると思うので、そこを「キュッ」と絞めると、いけると思います。


出力文字を切り替える

対応言語数が多い&ページ数が多い&更新頻度が高いのであれば、「出力文字による切り替え」を想定したほうが楽かもしれません……初手面倒ですが。

端的には

・各言語用の翻訳ファイルを用意して

プログラムを通して文字列を変換する

となります。


これにも方法がいくつかあって、大まかには

サーバサイドで自力実装

クライアントサイドで自力実装

・「gettext」ってのが割とあちこちの言語でライブラリとして存在するので、それを使う

のいずれか、になります。


共通があるのでgettextがよさそうなもんですが…それなりに使い方とかお作法とか癖とかがあるので。

PHPの場合は「インストール」も必要ですしねぇ。

前提条件や癖やそのあたりが「呑み込めそう」なら、gettextを使ってみるとよいんじゃないかなぁ、と思います。

xgettextとか面白いんだけどなぁ……「テンプレートエンジンを使う」前提だと、幾分、ハードルが上がったり諸々が以下略。


自力で作る場合は、おおむね

・辞書のフォーマットを決める

・辞書ファイルを必要言語数だけ作る

テンプレートに辞書ファイルをぶつけて出力する

といった感じ。


んと……ざっぱに、例。

例えば辞書ファイルを「コード: 翻訳文」とします。

日本語と英語を用意してみませう。


辞書.ja

login_button: ログイン

login_text: こちらからログインしてください。

password_reminder_text: パスワードがわからない場合はこのボタンを押してください。

password_reminder_button: パスワードリマインダ


辞書.en

login_button: login

login_text: Please log in from here.

password_reminder_text: Please press this button if you do not know the password.

password_reminder_button: Password reminder

*4


いろいろと面倒なんでいったんSmarty前提。

まずPHP本体側で、「言語にそった辞書」を渡します。

辞書は、key=コード、value=翻訳文、のhash配列方式で情報があると仮定

// 辞書の選択
if (日本語圏なら) {
  $辞書配列 = 日本語の辞書;
} else if (英語圏なら) {
  $辞書配列 = 英語の辞書;
} else {
    // 例外でもぶん投げるかねぇ
}

// 辞書をアサイン
$smarty_obj->assign('dic', $辞書配列);

んで、Smartyでは、例えばこんな風に記述します。

{$div.login_text}
<form ...>


<button>{$dic.login_button}</button>
</form>

こんな風にしてテンプレートに「一切出力用の自然言語を書かずに」辞書ファイルに追い出すと、まぁいろいろとできたり出来たりします。ざっくりとは。


結論

がっつりと多言語対応って、割と案件数的にも少ない気がするので。

調べると、案外とネットの情報も少ないんですよねぇ……なので、書いてみた。


なんかほかにも手法ありそうなのですが、まずは「こんなのもあるよ〜」的に。

突っ込みとかあったら突っ込んでくださいませ > 諸氏

*1UTF-7? なにそれ美味しいの?

*2:政敵って変換するFEPについてどう思う?

*3:まっとうな

*4:英訳への突っ込みはこれを禁止するwww

2017-07-13

[]いわゆる__callと__getマジックメソッド

PHPで言うところのマジックメソッドPythonの世界では「特殊メソッド」って呼称するようですねぇ、とかいう細かいナレッジをぶっこみつつ。

ちなみにPythonは3でございます。「2とかありえない」と、複数名のPythonist(Pythonista?)に言われたので、割と素直に従っておりまする。


ちょいと扱いたいので調べてみたのですが……案外に厄介だったので、いくつか試行錯誤。

まずそもそもとして「存在しないメソッド/プロパティをcallした時に呼ばれる特殊メソッド」が、__getattr__一つなので、まずその辺で「メソッドcallなのかプロパティcallなのか」を把握しなきゃいけないという以下略。

いやまぁそもそもとして「publicなプロパティとかありえないからプロパティにアクセスしようとした愚か者に百年の呪いあれ」とか言いきってもよさそうな気がわりとビシバシとしてはいるのですがいやマジで。


ただまぁ一応技術者として「できない」よりは「使わないけどできるけど使ったものに災いあれって事で使わない」ほうがよろしいかなぁ、と思うので「どうしても無理ならあきらめる」くらいの緩い温度感で、さくり、と。


とりあえずどっかググって書いた初手。

幾分「いろいろやってる感がある」のが、若干もにょりつつ、まず「なんとかはなるんだよなぁ」というあたりを確保。


#
import inspect

#
class hoge(object):

  def dummy(self, *args):
    print('__call: {}({})'.format(self.name, args))
    return "hogehoge"

  def __getattr__(self, name):
    #print("{}".format(name))

    # ここがもうちょっと綺麗だとうれしいんだろうなぁ……
    frame = inspect.getframeinfo(inspect.currentframe().f_back)
    code = ''.join([line for line in frame.code_context])
    code = ''.join(code.split()) + ' '  # remove all spaces and append sentinel
    s = code[code.index(name) + len(name)]
    #print("::{}".format(s))
    #
    if s == '(':
      self.name = name
      return self.dummy
    else:
      print("error")

h = hoge()

#
s = h.test(1,2,3)
print("return is {}".format(s))
s = h.test2()
print("return is {}".format(s))

s = h.foo
print("return is {}".format(s))

少しだけ試行錯誤してみて、コード量を落ち着けてみた……つもりのコード


import inspect

class X(object):
  def __getattr__(self, key):
    class MethodHook:
      def __init__(self, key):
        self.key = key
      def __call__(self, *args, **kwargs):
        print('__call: "{}", {}, {}'.format(self.key, args, kwargs))

    frame = inspect.getframeinfo(inspect.currentframe().f_back)
    code = ''.join([line for line in frame.code_context])
    code = ''.join(code.split()) + ' '  # remove all spaces and append sentinel
    s = code[code.index(key) + len(key)]
    if s == '(':
      return MethodHook(key)
    else:
      print('__get: "{}"'.format(key))
      return "placeholder"

#
x = X()
x.undef_method(1, 2, 3, a1="Hello", a2="World")
x.undef_method2()

#
y = x.hoge
print("y={}".format(y))

色々調べてみるともうちょっとすっきり……なんだけど、プロパティが取れない………


class A:
    def __init__(self):
        self.testProperty = 'text of testProperty'

    def __getattr__(self, name):
        def function(*args):
            #print("You tried to call a method named: %s" % name)
            print('__call: {}({})'.format(name, args))
            print(self.testProperty)
        return function

a = A()
a.test()
a.test2(1,2,3)
a.test2([1,2,3])
val = a.hoge
print ("hoge is {}".format(val))

もうちょい試行錯誤して、概ね一段落……なんだけど、戻り値の型が固定になるなぁ……


class Magic(object):
    def __getattr__(self, name):
        print("__getattr__: {}".format(name))
        class Callable(int):
            def __call__(self, *arguments):
                print("call {}: {}".format(name, arguments))
        return Callable(100)

m = Magic()
m.test()
m.test(20)
m.test2(1,2,3,4,5)
print(m.x)

まぁ「プロパティだしなぁ」ってんで、雑な解決策を考えてみたら通ったので、まぁこの辺かなぁ一旦、的な。


class Magic(object):
    def __getattr__(self, name):
        print("__getattr__: {}".format(name))
        class CallableI(int):
            def __call__(self, *arguments):
                print("call {}: {}".format(name, arguments))
        class CallableS(str):
            pass
        if (name == 'test_s'):
            return CallableS('test')
        # else
        return CallableI(100)

m = Magic()
m.test()
m.test(20)
m.test2(1,2,3,4,5)
print(m.x)
print(m.test_s)

# ところで、存在しないプロパティでも受け取るんだねぇ……
m.hoge = 999
print(m.hoge)

とりあえず初手で欲しかった「__call」はまぁまぁな感じで実装ができたので。

__get、ぶっちゃけると一番よくやるのは「攻性防御用 http://d.hatena.ne.jp/gallu/20140119/p1 」なのでw

適当に(第一種)ホワイトリストで「許可されているメソッド名」以外は「例外ぶん投げる」とかで処理が終わりそうでもあるので、あんまり問題なさそうな気も、わりとむんむんwww

(ゲーム作成初期で、時々「実用で」__get使いますが)


まぁとりあえず当初の目的は達成したので、備忘録的に、めも。

「こうやったらもっとエレガントぢゃ!!」とかいう突っ込みがあったら歓迎いたしますので、是非。

2017-06-18

[]簡単な「メッセージキュー」の仕組みを作ろうかなぁ……

んと…いわゆる

バラバラタスクを不定期に詰め込んで

・定期的なバッチで「積みあがった」タスクをまりもりとこなす

ってのは、例えばAWSなんかだとSQSとかで提供されている機能なので、まぁある程度のニーズはあろうかと思われるのです。


ただ一方で「AWS使ってないときは?」とかってなったりいろいろとあるので。

微妙に習作的なニュアンスも込みで、作ってみようかなぁ、と。


目標としては、以下の通り。

・「複数の処理バッチ」が動いても安全:障害耐性がある程度ある

 →「複数台のマシン」で動かしても問題ないようにする

・可能な限り「どんな処理でも乗る」ように、マージンは広めにとっておく

・シンプルにする

・常駐は、してもいいけど基本は「常駐させずに定期的にcronとかで呼ばれる」


こんな感じかなぁ、と。

突っ込みなどありましたらお待ちしております。


SQL部分

色々と考えていたのですが。

「バッチに渡すべきデータ」は、内部的な処理のみなので「serialize()で作った文字列」でよかんべではないかなぁ、と。

個人的には「配列を格納して配列を受け取る」事を推奨したいところなんだけど、まぁ「インスタンス突っ込むんならそれはそれで自己責任のもとによい」のではないかなぁ、と。


その辺を前提にすると。

最低限のDDLとしてはおそらく

CREATE TABEL message_queue (
    message_queue_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '一意のID:ソート条件にも使う',
    data blob NOT NULL COMMENT '引き渡すデータ:中はなんでもよいような感じで(サイズが足りないことが懸念されるならでっかく)',
    status TINYINT UNSIGNED DEFAULT 0 COMMENT '処理ステータス。 0:未処理, 1:処理中',
    processing_at DATETIME COMMENT '処理開始時刻。statusが0の時はNULLが想定されている',
    error_count TINYINT UNSIGNED DEFAULT 0 COMMENT '処理時のエラー回数(10回くらいを最大に考えてるのでTINYINT)',
    create_at DATETIME NOT NULL COMMENT 'このキューの作成時刻',
    PRIMARY KEY (`message_queue_id`)
)CHARACTER SET 'utf8mb4', ENGINE=InnoDB, COMMENT='1レコードが「1つのキュー」を意味するテーブル';

こんな感じ。


INSERTはたぶん、何も考えずに「INSERT文一発」で終了。

まぁエラー補足はしたほうがよいと思うので「うまくINSERTできなかった」らエラー処理、くらいかなぁ。


で、バッチ処理用に「キューを取得する」方法。

現状は、以下を想定している感じ。

BEGIN;
SELECT * FROM message_queue WHERE status=0 AND error_count < 10 ORDER BY message_queue_id LIMIT 0,1 FOR UPDATE;
if (SELECTがempty) {
    COMMIT; // ROLLBACKとどっちがよろしかんべか?
    プログラム終了;
}
UPDATE message_queue SET status=1, processing_at=now() WHERE message_queue_id=[上で取得したID];
COMMIT;


処理本体


if (処理でエラー発生) {
    UPDATE message_queue SET error_count=error_count+1, status=0 WHERE message_queue_id=[上で取得したID];
} else {
    DELETE message_queue WHERE message_queue_id=[上で取得したID];
    (DELETEできなかったらテキストログにでも書き出しておく)
}

これが基本。

トランザクションの中でがっつりロックが発生するんだけど「UPDATEしたらすぐに手放す」から、多少の競合が発生しても、重たいロックにはならんのではなかろうかなぁ、と。多分。


レコードは物理削除。

論理削除はお好まない感じなので。


あとは、定期的にガベコレ的な処理として

SELECT * FROM message_queue WHERE status=1 AND processing_at < 現在時刻から1時間前

とかって感じで捕捉しておくと。

ざっくりした予想だけど「上述のSQLに当てはまるような処理はたぶん"バッチが処理中に落ちてる"と思われる」ので。

「改めて別プロセスで処理(ざっくり系)」でもいいし「管理画面等にアラートを上げる(丁寧系)」でもいいし、お好みで。


同じくガベコレ的処理として

SELECT * FROM message_queue WHERE error_count >= 10;

があって、これは「何度も(今回のケースだと10回)投げたけど毎度毎度、処理がエラーになるよ?」なので、こいつは管理画面にアラート、じゃないかなぁ。


とまぁ、こんな風に作ったらあらかたいけるような気がしてる。

なんとなく。


プログラム部分

こちらで組む側はクラス作って。

仮に、クラス名を「mw_message_queue」と仮定して。


タスクを突っ込む時は

mw_message_queue::enqueue(mixed データ, PDO $dbh=NULL);

でよいかなぁ、と。

テーブル名とかその他諸々の可変になりうる変数は「メソッドで切り出し」しておいて「変えたかったら継承して子クラス作ってそっち呼んで」くらいの感じで。

第二引数についても「基本はいる」んだけど「継承した子クラスでget_dbhを実装していれば省略できる」風に作ろうかなぁ、と。

処理的には

static pub func enqueue(mixed $data, PDO $dbh=NULL) {
    //
    if (NULL === $dbh) {
        $dbh = static::get_dbh();
    }
    // チェック
    if (NULL === $dbh) {
        // 適当にエラー吐いて終了
        exit;
    }
    // 続く

}
// 親で実装してる空っぽなメソッド
static protected func get_dbh() {
    return NULL;
}

的なのをイメージ。


タスクの処理のほうは、考えているのが

mw_message_queue::dequeue(callable $callback, PDO $dbh=NULL);

これくらい。

細かい「1バッチで処理する回数の上限」とか「(1マシン内での)多重起動の制限個数」とかはメソッドで切り出しておいて以下略。


callableが意外と丁寧な処理が必要そうなので、ここはいろいろ想定。

関数引数戻り値については「bool 関数(unserialize()されたデータ);」ってフォーマットを想定……DBの項目でほかに欲しい情報とかあるかしらん???


処理的には

文字列なら、可変関数としてcall

・Closureクラスインスタンス(無名関数)なら、そのままcall

配列の場合

 →[0]がインスタンスなら、->でcall

 →[0]が文字の場合

  →[0]のクラスの[1]のメソッドがstaticなら、::でcall

  →[0]のクラスの[1]のメソッドがstaticでないなら、->でcall

で、おかしな値は一通り「エラーをゲロって終了」と。

この辺でなんとかなるはずなんだけどなぁ……たぶんこの実装は、MagicWeaponに吸収するwww


で、ガベコレ系についてもおそらくは

// 「仕掛中のまま止まってる」子の処理
mw_message_queue::gc_processing(callable $callback, PDO $dbh=NULL);
// 「エラーで止まってる」子の処理
mw_message_queue::gc_error(callable $callback, PDO $dbh=NULL);

でよいかなぁ、と。


ざっくりした設計諸々。

突っ込みがあったらよろです。

なかったら、たぶんそのうち、実装しますw