最近更新: 2011-08-22

php-dbus unboxing

我先前在「PHP DBus client」一文中,只粗略地提到某些 DBus 方法或信號傳回的資料項目,要調用其方法 getData() 取值。 沒有說明為什麼,因此其他人在使用時,經常又再問我如何取用某某 DBus 方法的回傳值。 那些不能直接取值的資料項目,其實都是被封箱的(boxed)。

Variant, Struct, Dict, Array 等型態,在 DBus 規格中皆歸類為封箱型態 (Boxed type)。 封箱型態的意義可參考 Boxing and Unboxing (C# Programming Guide) 之解釋。 PHP DBus 模組從遠端個體取得封箱型態的內容時,會將它們配置為 DbusVariant, DbusStruct, DbusDict, DbusArray 等類別個體。 這些被封箱在冠上 Dbus 之名的型態裡的內容,需要先拆封 (unboxing) 為 PHP 基本型態後,才能得其值。 拆封方法便是 $obj->getData()

範例一是一個簡單的拆封範例。我向 Linux 桌面環境上常用的 NetworkManager 調用 GetAll() 方法查詢其目前屬性狀態。 回傳的屬性狀態是一個封箱資料,所以要先對目標拆封才能得值。

<?php
$NM_Service = 'org.freedesktop.NetworkManager';
$NM_Player_Path = '/org/freedesktop/NetworkManager';
$DBus_Properties_Interface = 'org.freedesktop.DBus.Properties';

$d = new Dbus(Dbus::BUS_SYSTEM);

$proxy_obj = $d->createProxy(
    $NM_Service, $NM_Player_Path, $DBus_Properties_Interface);

$result = $proxy_obj->GetAll('org.freedesktop.NetworkManager');

var_dump($result); // boxing values

$unboxed_result = $result->getData(); //unboxing
$cs = $unboxed_result['ActiveConnections']->getData()->getData();
echo 'Name of connection 0: ', $cs[0]->getData(), "\n";
?>

上述範例所得到的封箱資料,像是個俄羅斯娃娃,資料內容被層層包裝。 當我們需要多次從這個 DBus 封箱資料中取值時,就要重複上述拆封工作,這實在是件煩事。 所幸要把它完全拆封倒也不是難事,一個遞迴函數就可以解決。

範例二是 DBus 封箱資料的泛用拆封函數,它會將所有封箱資料全部拆封。

<?php
function dbus_unboxing($v) {
    if (is_array($v)) {
        foreach ($v as $k => $vv) {
            $v[$k] = dbus_unboxing($vv);
        }
        return $v;
    }
    else if (is_object($v) and strpos(get_class($v), 'Dbus') === 0) {
        return dbus_unboxing($v->getData());
    }
    return $v;
}

$NM_Service = 'org.freedesktop.NetworkManager';
$NM_Player_Path = '/org/freedesktop/NetworkManager';
$DBus_Properties_Interface = 'org.freedesktop.DBus.Properties';

$d = new Dbus(Dbus::BUS_SYSTEM);

$proxy_obj = $d->createProxy(
    $NM_Service, $NM_Player_Path, $DBus_Properties_Interface);

$result = $proxy_obj->GetAll('org.freedesktop.NetworkManager');

$unboxed_result = dbus_unboxing($result);

var_dump($unboxed_result); // unboxing values

echo 'Name of connection 0: ', $unboxed_result['ActiveConnections'][0], "\n";

?>

比對範例一與範例二的 var_dump 結果,可以看出這個拆封函數會遺失型態資訊 (例如 dbus signature)。目前還看不出有無副作用。

此外,「Boxing and Unboxing (C# Programming Guide)」提到了拆封工作的效能問題。 雖然我們此處談的是 PHP ,但基本道理是一樣的。 從 dbus_unboxing() 的程式碼中,應該也可想像拆封一個有多層封箱的資料時,將會發生多次資料內容配置工作。 因此從效能面考量,若你只需要向一個封箱資料取其中一筆內容時,你應先考慮用範例一的方式取值,而不建議直接調用本文所寫的 dbus_unboxing() 函數將其全部拆封。

相關文章
樂多舊網址: http://blog.roodo.com/rocksaying/archives/16404035.html