Closed18

[PHP7.4] ginq の デバッグ

sogaohsogaoh

どこでおちるか追跡

なお、PHPUnit 9.4.4


    public static function toArray($it)
    {
        $num = count($it);
        $cur = 0;

        var_dump('>>> in for ');
        $acc = array();
        foreach ($it as $k => $v) {
            ++$cur;
            var_dump($cur);
            $acc[$k] = $v;
        }
        var_dump('<<<  out for ');
        return $acc;
    }


ErrorException: count(): Parameter must be an array or an object that implements Countable

/work/vendor/ginq/ginq/src/Ginq/Util/IteratorUtil.php:116
/work/vendor/ginq/ginq/src/Ginq/Ginq.php:156

$it が Countable じゃないようだ

sogaohsogaoh

- $num = count($it);
+ $num = is_countable($it) ? count($it) : $it->count();

Error: Call to undefined method Ginq\Iterator\OrderedIterator::count()

/work/vendor/ginq/ginq/src/Ginq/Util/IteratorUtil.php:116
/work/vendor/ginq/ginq/src/Ginq/Ginq.php:156

左様か

sogaohsogaoh

count するのはあきらめたら話が進んだが、どうやらいっちゃいけないところまでいっちゃってるっぽい
9 件でぐるぐる回るはずが、$cur が 25 まで進んでる

果てに出ているエラー

Trying to access array offset on value of type null
sogaohsogaoh

これじゃなかった。。(ちゃんと抜け出てた)

sogaohsogaoh

toList

https://github.com/akanehara/ginq/blob/master/src/Ginq/Util/IteratorUtil.php#L43-L50


    public static function toList($it)
    {
        $acc = array();
        foreach ($it as $v) {   // <- ここで落ちる場合があるっぽい
            $acc[] = $v;
        }
        return $acc;
    }

$it の 中身は var_dump した結果、 Ginq\Iterator\JoinIterator
これの $this->it はどうやら↓で生成されている(new SelectManyWithJoinIterator)ようで、コンストラクタは通過していた
https://github.com/akanehara/ginq/blob/master/src/Ginq/Iterator/JoinIterator.php#L122

引き続き ↓(SelectManyWithJoinIterator # rewind)を追う。。
https://github.com/akanehara/ginq/blob/master/src/Ginq/Iterator/SelectManyWithJoinIterator.php#L105-L111

sogaohsogaoh

環境上の理由で必殺 var_dump デバッグしてたら


    public function rewind()
    {
var_dump('rewind start');
        $this->outer->rewind();
var_dump('rewind 1');
        $this->fetchOuter();
var_dump('rewind 2');
        $this->fetchInner();
var_dump('rewind end');
    }

string(12) "toList start"
string(26) "Ginq\Iterator\JoinIterator"
string(14) "__construct in"
string(15) "__construct out"
string(12) "rewind start"
string(8) "rewind 1"
string(8) "rewind 2"

rewind end が出てない・・・
で、次の var_dump を打ち込むまえに GitHub のソースを見たら、違いに気付いた。

https://github.com/akanehara/ginq/blob/master/src/Ginq/Iterator/SelectManyWithJoinIterator.php#L105-L111


    public function rewind()
    {
        $this->outer->rewind();
        $this->fetchOuter();
        $this->skipEmpty();     // <- !!!
        $this->fetchInner();
    }

というわけで、 プルリクを出すことにした。
https://github.com/akanehara/ginq/pull/68

sogaohsogaoh

メンションしたいと思って検索したら作者が見つかった

sogaohsogaoh

    "require": {
-        "ginq/ginq": "~0.2.3"
-        "ginq/ginq": "~0.2.4"            // rewind の内容変わってなかった
+        "ginq/ginq": "dev-master"
    }

にしてソースを最新にしたが、toList でまだ落ちるようだ。つづく。

refs
https://packagist.org/packages/ginq/ginq

sogaohsogaoh

    public function rewind()
    {
var_dump('rewind 0');
        $this->outer->rewind();
var_dump('rewind 1');
        $this->fetchOuter();
var_dump('rewind 2');
        $this->skipEmpty();
var_dump('rewind 3');
        $this->fetchInner();
var_dump('rewind 4');
    }

こうしたところ


string(8) "rewind 0"
string(8) "rewind 1"
string(8) "rewind 2"
string(8) "rewind 3"
E

こうなったので、fetchInner を追う

なお、PHPUnit 9.5.0 by Sebastian Bergmann and contributors. になってた。

sogaohsogaoh

さらに追跡を進めているが・・・

fetchInner のデバッグ 残骸

    protected function fetchInner()
    {
var_dump('fetchInner 0');
        if ($this->valid()) {
//var_dump('fetchInner 1');
            $innerV = $this->inner->current();
//var_dump('fetchInner 2');
////var_dump(['iv'=>$innerV]);
            $innerK = $this->inner->key();
//var_dump('fetchInner 3');
////var_dump(['ik'=>$innerK]);
var_dump($this->valueJoinSelector);
////var_dump(['ov'=>$this->outerV]);
////var_dump(['ok'=>$this->outerK]);
            $this->v = $this->valueJoinSelector->select(
                $this->outerV, $innerV,
                $this->outerK, $innerK
            );
var_dump($this->keyJoinSelector);
var_dump(['v'=>$this->v]);
var_dump('fetchInner 4');
            $this->k = $this->keyJoinSelector->select(
                $this->outerV, $innerV,
                $this->outerK, $innerK
            );
var_dump(['k'=>$this->k]);
var_dump('fetchInner 5');
        }
var_dump('fetchInner 6');
    }

valueJoinSelector・keyJoinSelector の 実体は Ginq\JoinSelector\DelegateJoinSelector で、
その実装は以下の通り


    public function select($v0, $v1, $k0, $k1)
    {
var_dump('DelegateJoinSelector select');
//var_dump(['func'=>$this->func]);
        $f = $this->func;
        //return $f($v0, $v1, $k0, $k1);   //<- オリジナル
        $rv = $f($v0, $v1, $k0, $k1);      // <- ここで落ちている。Delegation できてない?
var_dump('DelegateJoinSelector selected');
var_dump($rv);
        return $rv;
    }

今日はここでギブアップ。
代替手段があれば置き換える、かな・・・

sogaohsogaoh

issue をクローズしました。
あとは、PHP と PHPUnit のバージョンアップ対応をこちらで行いたいところ。

現象が再現するコードを、ある程度公開可能な状態にしてお渡しして調査していただいた結果、
アプリケーション側のコードに NULL に対して安全になっていない箇所があると特定いただきました。

そのため、本件アプリケーション側を修正します。
調査いただき、本当にありがとうございました。お手数おかけしてしまい申し訳なくも思っています。

代わりになるか、自信がないのですが、PHP と PHPUnit のバージョンアップ対応を行っておいて、
今後のメンテナンスに少し寄与できればと思います。少々お時間ください。

https://github.com/akanehara/ginq/issues/69#issuecomment-745215606

このスクラップは2020/12/21にクローズされました