gorogoroyasu

福岡の開発会社で働いている。

Maximum function nesting level of '256' reached, aborting!

タイトルのエラーが発生したので原因を調べた。

何を言ってるかよくわからなかったので、ググった。

参考サイト1
参考サイト2

発生状況とかを書いておこうと思う。

CakePHP3 を使用 HogeBehavior.php を 読み込んだ。

UsersTable.php

public function initialize ()
{
    parent::initialize();
    $this->addBehavior('Hoge');
}
// 続きのコード

そして、 HogeBehavior.php の中に問題があった。

use Cake\ORM\TableRegistry;

public initialize ()
{
    parent::initialize();
    $this->Users = TableRegistry::get('Users');
}

Users から読み出す HogeBehavior.php の中で
TableRegistry::get('User') をしていたのだ。

原因がわかったので一件落着。

結論

UserTable から読み込む Behavior の中で TableRegistry::get('User'); をしてはいけない。

原因調査 だが、せっかくなので事故原因を調査してみた。
vendor の中を読む。

まず、use \Cake\ORM\TableRegistryとあるので、 TableRegistry の中を読みに行った。

すると、public static function get($alias, array $options = [])という メソッドが見つかった。

このメソッドは、static::locator()->get($alias, $options) をreturn している。
そして、この return の前で error が出ているらしかったので、更に深く潜った。

ところで、

return static::locator()->get($alias, $options);

という書き方は、遅延静的束縛というらしい。 このクラスを継承しているクラスのオブジェクトからこのメソッドが呼ばれた場合、 継承先のlocator()を読み込むという書き方らしい。(説明が難しいし微妙に違いそう。)

しかし、今回は直接このクラスを呼んでいるので、

return self::locator()->get($alias, $options);

と等価だ。(と思う)

ということで、 \Cake\ORM\TableRegistry クラスの中の

public static function locator(LocatorInterface $locator = null)

を呼んでいる。

このクラスの返り値は

static::$_locator;  

であり、ありがたいことにコメントで

* @var \Cake\ORM\Locator\LocatorInterface  

とある。

つまり、

Cake\ORM\Locator\TableLocator

が返ってくる。
すなわち、

return static::locator()->get($alias, $options);

の中の

static::locator() = Cake\ORM\Locator\TableLocator

となる。

そこで、Cake\ORM\Locator\TableLocatorクラスのget()メソッドを探しに行った。

この、179行目、

$this->_instances[$alias] = $this->_create($options);

で呼んでいる、

protected function _create(array $options)

が今回のエラーの原因だった。

このメソッドの中で

return new $options['className']($options);

という風に
$options['className']($options) を繰り返し new して返している。
ここがポイントだった。

つまり、

TableRegistry::get('User');

により、UsersTable クラスのオブジェクトが生成される。 そして、UsersTable クラスの

public function initialize() 

が動き、もう一度

$this->addBehavior('Hoge');

が呼ばれる。 そして、TableRegistry が動きまたUsersTable クラスが new される。

この繰り返しが256回おこり、 参考サイト1 に書いてあるエラーが起こったのだった。

これが事故原因。

解決方法は、

Cake\ORM\TableRegistry

の使い方に気をつける。

という話でした。

vendor の中見るのたのしー!

感想

なんか、全体的に感想文みたいになってしまったが、
vendor の中を見るのがこんなに楽しいとは思わなかった。
これからも継続して読んでみようと思う。