arrayへのキャスト

引数をスカラでも配列でも受け取れるようにしたい時、次のようなコードを書いているんだが、あまりにも面倒くさいので時々array型にキャストしたくなってくる。

<?php
function getList($status = null) {
    // 配列に統一
    if (!is_array($status)) {
        $status = isset($status) ? array($status) : array();
    }

    // get data
    $list = getRawList();

    // filtering
    $result = array();
    foreach ($list as $data) {
        if (empty($status) || in_array($data['status'], $status)) {
            $result[] = $data;
        }
    }
    return $result;
}

$data = getList();              // 絞り込みなし
$data = getList(3);             // ステータスが3のやつ
$data = getList(array(3, 4));   // ステータスが3と4のやつ
?>

これを

<?php
function some_func($args = null) {
    // 配列に統一
    $args = (array)$args;
}
?>

こんな感じに。
ものすごい乱暴だけどPHPではarray型へのキャストが用意されていて、スカラならその値をひとつの要素とした配列に、配列ならそのままの状態にキャストすることができる。


ちなみに実際には前後にも引数があるとか、ランタイム(チェックボックスでチェックしたやつなど)な状況を想定しているので可変長引数にしてfunc_get_args()というのはなしの方向で。

色んなものをarrayにキャストしてみる

で、普通に使う分にはよさそうなんだが、微妙な値をキャストしたらどうなるのか気になったので一通りやってみた。

<?php

class Foo {
  public $foo;
}

echo "[undefined variable]\n";
$c_undefined = (array)$undefined;
var_dump($c_undefined);

echo "\n[NULL]\n";
$null = null;
$c_null = (array)$null;
var_dump($c_null);

echo "\n[scalar]\n";
$scalar = 123;
$c_scalar = (array)$scalar;
var_dump($c_scalar);

echo "\n[array]\n";
$array = array(1, 2, 3);
$c_array = (array)$array;
var_dump($c_array);

echo "\n[object(without properties)]\n";
$object = new StdClass();
$c_object = (array)$object;
var_dump($c_object);

echo "\n[object(with properties)]\n";
$object2 = new Foo();
$c_object2 = (array)$object2;
var_dump($c_object2);

echo "\n[resource]\n";
$resource = fopen('php://stdin', 'r');
$c_resource = (array)$resource;
var_dump($c_resource);
fclose($resource);

?>

結果はこんな感じ。

[undefined variable]

Notice: Undefined variable: undefined in C:\temp\array_cast.php on line 8
array(0) {
}

[NULL]
array(0) {
}

[scalar]
array(1) {
  [0]=> int(123)
}

[array]
array(3) {
  [0]=> int(1)
  [1]=> int(2)
  [2]=> int(3)
}

[object(without properties)]
array(0) {
}

[object(with properties)]
array(1) {
  ["foo"]=> NULL
}

[resource]
array(1) {
  [0]=> resource(5) of type (stream)
}

引数なのでundefined variableはいいとして、オブジェクトをキャストしたときがまずそうだ。
となると期待した結果を得るには

<?php
$array1 = is_array($args) ? $args : (isset($args) ? array($args) : array());
$array2 = is_object($args) ? array($args) : (array)$args;
?>

こうするしかないのか。
前者はPHPで三項演算子のネストがおかしいという罠があったので避けたいところ。


まぁ、それ以前に「何やってるのかわからない」とか「配列にキャストきめぇ」と言われるのが目に見えているので面倒でも普通に書いておいたほうが無難か。


ちなみにprotectedやprivateなメンバを持ったオブジェクトをダンプしたらキモイことになったんだけど、それはまた別のお話。