count関数の仕様
PHPのcount関数ではまったらしい。まぁ、ちゃんと仕様を理解していない自分が悪いのだが‥
何にはまったかというと、ある関数Fは戻り値として「空でない配列」か「false」を返す。で、「count(関数Fの戻り値)」を判定条件に含むループがあるのだが、「count(false)」となるときにループ内の処理が実行されて、なんでだ!?ということに。
count関数は、配列以外を渡した場合、nullまたはCountableでなければ「1」を返すのですね。やれやれ。
一応、確認プログラムを作って動かしてみたメモ。
<?php ini_set('display_errors', 'yes'); class C1 { private $c = 5; function count() { return $this->c; } } class C2 implements Countable { private $c = 5; function count() { return $this->c; } } $data = array(); $data['empty string'] = ''; $data['non-empty string'] = 'hoge'; $data['string \'0\''] = '0'; $data['empty array'] = array(); $data['one elem array'] = array(1); $data['two elems array'] = array(1, 2); $data['array in array'] = array(array()); $data['array in array'] = array(array(3)); $data['bool true'] = true; $data['bool false'] = false; $data['int 0'] = 0; $data['int 1'] = 1; $data['null'] = null; $data['C1'] = new C1(); $data['C2'] = new C2(); print('<pre>'); foreach ($data as $i => $d) { printf("| %-16s |", $i); ob_start(); var_dump($d); $s = ob_get_contents(); $s = preg_replace( array("/ *\n */", "/^ +/", "/ +/", "/ +$/"), array(" ", "", " ", ""), $s); ob_end_clean(); printf(" %-48s |", $s); printf(" %1s |", is_array($d)); printf(" %1s |", ($d instanceof Countable)); ob_start(); var_dump(count($d)); $s = str_replace("\n", " ", ob_get_contents()); $s = preg_replace(array("/^ +/", "/ +/", "/ +$/"), array("", " ", ""), $s); ob_end_clean(); printf(" %-6s |", $s); ob_start(); var_dump(count($d, COUNT_RECURSIVE)); $s = str_replace("\n", " ", ob_get_contents()); $s = preg_replace(array("/^ +/", "/ +/", "/ +$/"), array("", " ", ""), $s); ob_end_clean(); printf(" %-6s |", $s); print("\n"); } print('</pre>'); ?>
実行結果。ちょっと横幅があって見づらいけど‥
| empty string | string(0) "" | | | int(1) | int(1) | | non-empty string | string(4) "hoge" | | | int(1) | int(1) | | string '0' | string(1) "0" | | | int(1) | int(1) | | empty array | array(0) { } | 1 | | int(0) | int(0) | | one elem array | array(1) { [0]=> int(1) } | 1 | | int(1) | int(1) | | two elems array | array(2) { [0]=> int(1) [1]=> int(2) } | 1 | | int(2) | int(2) | | array in array | array(1) { [0]=> array(1) { [0]=> int(3) } } | 1 | | int(1) | int(2) | | bool true | bool(true) | | | int(1) | int(1) | | bool false | bool(false) | | | int(1) | int(1) | | int 0 | int(0) | | | int(1) | int(1) | | int 1 | int(1) | | | int(1) | int(1) | | null | NULL | | | int(0) | int(0) | | C1 | object(C1)#1 (1) { ["c":"C1":private]=> int(5) } | | | int(1) | int(1) | | C2 | object(C2)#2 (1) { ["c":"C2":private]=> int(5) } | | 1 | int(5) | int(5) |
COUNT_RECURSIVEオプションを指定するというのも一緒に試してみたが、配列の中の配列の中の‥という構造だと、末端の要素だけでなく、配列自体の個数もカウントに入っているらしい。