Archive for the ‘Module’ катэгорыя

Памылка PHP. Узровень укладзенасці. Занадта глыбокая рэкурсіўная залежнасць

12 сакавіка, 2010

Я ўсталяваў PHP 5.2 на адным з маіх тэставых кампутараў сёння і некалькі біт кода, які раней працаваў нармальна ў версіі 5.1.6 выкінуў фатальныя памылкі ў новай версіі. Паведамленне пра памылку было «Занадта глыбокі ўзровень укладзенасці - рэкурсіўная залежнасць?», і гэта заняло крыху часу

каб знайсці корань праблемы. Вось што я зрабіў не так.

У PHP ёсць два аператара параўнання, == і ===. Агульнавядома, што першая не мае строгіх патрабаванняў да тыпу, а вось другая. Такім чынам, напрыклад

рэха ( ілжыва == 0 ); // праўда

рэха ( хлусня === 0 ); // ілжывы

– 0 з'яўляецца цэлым лікам, а false - лагічным

Мая праблема ўзнікла з-за выкарыстання нестрогага ўводу з аб'ектамі.

$a = новы MyObj();
$b = новы MyObj();
калі( $a == $b )

Я не думаў, што я раблю з гэтым кодам. Пры параўнанні двух аб'ектаў з выкарыстаннем аператара нестрогага параўнання (==) PHP параўноўвае ўсе ўласцівасці аб'ектаў і, калі яны супадаюць, аб'екты лічацца роўнымі. Калі яны не супадаюць, яны не роўныя. Па сутнасці, мы маем рэкурсіўнае параўнанне ўсіх уласцівасцей кожнага аб'екта, і ўсе іх уласцівасці, і г.д.. пакуль мы не дасягнем асноўных тыпаў дадзеных, такіх як радкі і цэлыя лікі.

Калі, аднак, мы выкарыстоўваем строгае параўнанне (===), PHP праверыць, ці з'яўляюцца гэтыя два аб'екты аднолькавымі, не толькі аб'екты з аднолькавымі ўласцівасцямі.

клас MyObj
{
публічны $p;
}

$a = новы MyObj();
$b = новы MyObj();
$c = новы MyObj();
$а->р = 1;
$б->р = 1;
$в->р = 2;
рэха ( $a == $c ); // ілжывы
рэха ( $a == $b ); // праўда
рэха ( $a === $b ); // ілжывы

Праблема ўзнікае, калі ва ўласцівасцях вашых аб'ектаў ёсць цыклічныя спасылкі. Такім чынам, напрыклад

клас MyObj
{
публічны $p;
}
клас OtherObj
{
публічны $q;
}

$a = новы MyObj();
$b = новы OtherObj();
$а->p = $b;
$б->q = $a; // кругавая спасылка: $а->п->q === $a

$c = новы MyObj();
$d = новы OtherObj();
$в->p = $d;
$д->q = $c;// іншая кругавая спасылка: $в->п->q === $c

рэха ( $a == $c ); // Фатальная памылка:
Занадта глыбокі ўзровень укладзенасці – рэкурсіўная залежнасць?

Каб параўнаць $a з $c, PHP павінен параўнаць іх уласцівасці. Такім чынам, логіка ў PHP выглядае прыкладна так: $a == $c, калі $a->p == $c->p, калі $a->п->q == $c->п->q, калі $a->п->д->p == $c->п->д->р і г.д. бясконца.

PHP 5.1 здавалася, неяк згладзіць праблему (верагодна, пасля пэўнага ўзроўню рэкурсіі ён проста вярнуўся ілжывым) – і звычайна гэта атрымлівалася добра. PHP 5.2 правільна стварае фатальную памылку вышэй.

Як толькі вы ведаеце праблему, рашэнне лёгкае – выкарыстоўваць строгае параўнанне.

рэха ( $a === $c ); // ілжывы (і ніякай памылкі)

Строгае параўнанне проста правярае, ці знаходзяцца два аб'екты ў адным і тым жа месцы ў памяці, і таму нават не разглядае значэнні ўласцівасцей.

N.B. Такая ж праблема можа ўзнікнуць пры выкарыстанні адмоўленых аператараў параўнання (выкарыстоўваць !== замест !=) і пры выкарыстанні in_array (выкарыстоўвайце трэці параметр in_array, каб паказаць строгае параўнанне).