php dbus 0.1.0 (Derick's php-dbus) 仍然在發展階段,其中還有許多待解決的問題。故目前使用時,可能會碰到許多意外事件。本文整理了我的使用經驗,可為 PHP 程序員以 php-dbus 撰寫 DBus 服務之參考。
基本概念
php-dbus 0.1.0 目前的實現內容,只能將一個 PHP 類別視為一個 DBus 個體(object)的介面(interface)內容。
PHP 類別內的靜態方法將自動被視為 DBus 個體介面的方法。
所以 PHP 實作一個 DBus 個體介面時,要撰寫一個類別,在其中設計靜態方法。
最後調用 Dbus::registerObject($object_path, $interface_name, $php_class_name);
將此類別註冊為指定的 DBus 個體介面。
你註冊的 DBus 介面名稱與其對應的類別名稱無關,而且也沒有綁定關係。
若你需要,你可以重複將這個類別註冊為不同的 DBus 個體之介面。
例如下面的範例,將提供一個名為 blog.rock 的 DBus 服務。
此服務將註冊三個 DBus 個體,並且各自定義了一個 DBus 介面。
而這三個 DBus 介面都是由同一個 PHP 類別所註冊。
設計介面方法時,基本上和設計一般的 PHP 類別方法相同,在參數列指示傳入的參數名稱,透過 return 回傳值。
傳入的參數可以是任何 PHP 內建的資料型別,例如整數、字串、陣列。回傳的值也可以是任何 PHP 內建的資料型別。
但受限於 DBus Type 本身可處理的內容,回傳值有下列注意要項。
-
DbusArray 不允許容納不同資料型別的元素。
-
因為 PHP 語法並不支援一個函數回傳多個值,所以回傳多個值的情形另行處理,而且其中有 bug 。
DbusArray
PHP 的陣列有兩種型式,一種是純數字索引的傳統陣列,另一種是以鍵值索引的關聯式陣列。前者對應於 DbusArray ,後者對應於 DbusDict 。而且 DbusArray 內的元素,必須是同一種資料型別。
當你回傳的 PHP 陣列含有不同資料型別的元素時,php-dbus 會嘗試以 DbusDict 型態處理,但可能會失敗。既使成功轉換為 DbusDict 了,此結果也可能不符合呼叫者的預期。若呼叫者屬於 C/C#/Java 靜態型別語言,它們將擲出型別例外。
多個回傳值與 DbusSet
如果你想直接回傳多個值,而不透過陣列,那麼你必須使用一個 DbusSet 個體包裹它們。此外,DbusSet 目前有 bug 。它不會複製要回傳值的內容,故被呼叫的方法結束後,放置在 DbusSet 內的回傳值,其內容所在的記憶體空間也被釋放了。以至於呼叫者要取得回傳值時,將取不到正確值,甚或是發生違法存取記憶體的錯誤。故而,你要放在 DbusSet 內的回傳值,必須是一個全域變數或是一個類別的靜態變數。如下所示:
DBusSignal
DBus Signal 被 php-dbus 實作為一個獨立的類別 DbusSignal。要在某個 DBus 介面上實現一個 Signal 的方式是在你新增 DbusSignal 個體時,指定這個訊號要附加的介面名稱。另一方面,為了方便 DBus 個體可以主動發出訊號,最好是將你配置的 DbusSignal 個體指派給 DBus 類別的靜態成員。如此一來,你就可以透過該靜態成員,呼叫 DbusSignal 個體的 send() 方法送出訊號。如下所示。
Introspect
大多數程式語言對於 DBus 的支援,一般皆透過 dbus-glib-binding 實現,同時運用本身的反射機制,自動為你實作的 DBus 個體產生 org.freedesktop.DBus.Introspectable 介面。但是 php-dbus 採用 DBus low-level API 實作,並受限於其能力,故不會自動產生 org.freedesktop.DBus.Introspectable 和 org.freedesktop.DBus.Properties 這兩個泛用介面;後者較為少用,一般也不會實作它。若你需要提供此介面,則你必須自行定義 org.freedesktop.DBus.Introspectable 和 org.freedesktop.DBus.Properties 兩介面的內容。當然不定義也可以,只是其他人就不能透過 org.freedesktop.DBus.Introspectable 查詢你的 DBus 服務的介面。
定義 org.freedesktop.DBus.Introspectable 的方式非常簡單。它只有一個名為 Introspect 的方法,回傳一個內容為 XML 文件的字串。該 XML 文件透過固定的方式描述你的 DBus 服務提供的 DBus 個體與其介面。你可以向 org.freedesktop.DBus 服務查詢其內容,了解其 XML 文件的格式。細節則請參考 D-Bus 規格文件。
在本文中,我使用一個固定的 XML 文件描述本文實作的範例服務。
撰寫 DBus client 與傾聽訊號
我在 《Write a PHP DBus client》一文中,便已示範過如何撰寫 DBus 客戶端以及傾聽服務的訊號。請自行前往閱讀。在此僅提供一個傾聽本文示範服務之訊號的客戶端程式。
Service sample
綜合以上所述,實作一個完整可用的 DBus 服務。
各位可以先用 dbus-send 觀察此服務的是否正常運作。
開啟第一個終端機,執行 sample-service.php,放置play.
# 終端機1
$ php sample-service.php
....Zero.....One..........Two...............Array...
開啟第二個終端機,執行 wait-signal.php, 放置play.
# 終端機2
$ php wait-signal.php
get signal 1
get signal 2
string(7) "signal2"
開啟第三個終端機,用 dbus-send 向 sample-service 要求服務,並觀察前兩個終端機輸出的訊息。
# 終端機3
$ dbus-send --session --print-reply --dest=blog.rock \
/blog/rock org.freedesktop.DBus.Introspectable.Introspect
$ dbus-send --session --print-reply --dest=blog.rock \
/blog/rock blog.rock.EchoZero
method return sender=:1.79 -> dest=:1.82 reply_serial=2
int32 0
$ dbus-send --session --print-reply --dest=blog.rock \
/blog/rock blog.rock.EchoOne string:"world"
method return sender=:1.79 -> dest=:1.83 reply_serial=2
string "Hello world"
$ dbus-send --session --print-reply --dest=blog.rock \
/blog/rock blog.rock.EchoTwo string:"two"
method return sender=:1.79 -> dest=:1.84 reply_serial=2
string "two"
int32 2
$ dbus-send --session --print-reply --dest=blog.rock \
/blog/rock blog.rock.EchoArray
method return sender=:1.79 -> dest=:1.85 reply_serial=2
array [
string "array"
string "echo"
]
$ dbus-send --session --print-reply --dest=blog.rock \
/blog/rock blog.rock.TestSignal
參考文件
相關文章
樂多舊網址: http://blog.roodo.com/rocksaying/archives/13799073.html