PHP Conference Japan 2024

xml_set_object

(PHP 4, PHP 5, PHP 7, PHP 8)

xml_set_object在物件中使用 XML 解析器

警告

此函式已於 PHP 8.4.0 起不建議使用。 強烈建議不要依賴此函式。

描述

#[\Deprecated]
xml_set_object(XMLParser $parser, object $object): true

此函式允許在 object 內使用 parser。所有回呼函式都可以使用 xml_set_element_handler() 等函式設定,並假設為 object 的方法。

參數

parser

要在物件內使用的 XML 解析器參考。

object

要在其中使用 XML 解析器的物件。

回傳值

永遠回傳 true

更新日誌

版本 描述
8.4.0 此函式現已棄用,請改為將適當的 callable 值傳遞給 xml_set_()
8.0.0 parser 現在需要 XMLParser 實例;先前需要有效的 xml resource

範例

範例 1 xml_set_object() 範例

<?php
class CustomXMLParser
{
private
$parser;

function
__construct()
{
$this->parser = xml_parser_create();

xml_set_object($this->parser, $this);
xml_set_element_handler($this->parser, "tag_open", "tag_close");
xml_set_character_data_handler($this->parser, "cdata");
}

function
parse($data)
{
xml_parse($this->parser, $data);
}

function
tag_open($parser, $tag, $attributes)
{
var_dump($tag, $attributes);
}

function
cdata($parser, $cdata)
{
var_dump($cdata);
}

function
tag_close($parser, $tag)
{
var_dump($tag);
}
}

$xml_parser = new CustomXMLParser();
$xml_parser->parse("<A ID='hallo'>PHP</A>");
?>

以上範例將輸出

string(1) "A"
array(1) {
  ["ID"]=>
  string(5) "hallo"
}
string(3) "PHP"
string(1) "A"

新增註解

使用者貢獻的註解 17 則註解

wake dot shinigami at gmail dot com
19 年前
經過大量文件搜尋後,我終於讓 xml_set_object() 運作了。正如我發現的解決方案所暗示的那樣,重點是錯誤的。

在取得類別包含的 XML 解析器來變更實例的成員時,出現了許多問題。這是因為,從我的猜測來看,這些函式使用的是該類別的新匿名實例或未實例化的版本。

我們想要確保的是,解析器正在以類別特定實例的成員方法存取其處理常式。這可以使用傳遞回呼的陣列方法來完成,將物件設定為此實例的參考。這樣,您就知道解析器會正確呼叫函式。

範例
<?php
class Parser {

private
$parser;
private
$data;

public function
__construct() {
$this->parser = NULL;
$this->data = '';
}

private function
ParseElementStart($parser, $name, $attrs) {
// 程式碼
}
private function
ParseElementEnd($parser, $name) {
// 程式碼
}

public function
Parse($XMLdata) {
$this->parser = xml_parser_create();
xml_set_object($this->parser, $this);
xml_set_element_handler($this->parser,
array(&
$this, 'ParseElementStart'),
array(&
$this, 'ParseElementEnd'));
xml_parse($this->parser, $XMLdata);
xml_parser_free($this->parser);
}
}
?>

使用這個,當您呼叫 Parse 方法時,就可以修改該實例中的資料。我不確定在使用陣列回呼時,是否不需要 xml_set_object,但我保留它只是為了確保 xml_parse 函式知道它位於物件中。

如上所述,我建議為了記憶體考量,XML 解析器最好在同一個函式中建立、使用和釋放,以確保所有內容都已正確清除。
bettmenn at gmx dot de
20 年前
在下方新增至 'lmfe at mega dot ist dot utl dot pt' 的貢獻,我必須說,如果物件的 MemberVars 沒有從建構子初始化,這對 PHP 來說是很常見的行為。
到目前為止,對我來說效果最好的是類似這樣的方式
<?php
class foo {
var
$bar
// 建構子
function foo() {
unset(
$this->bar); // 每次實例化時,都會為物件清除變數
}
}
?>
lmfe at mega dot ist dot utl dot pt
20 年前
使用 PHP 4.3.0,我遇到了這個奇怪的行為

<?php
class xml {

/* (如同上方文件中所指定的) */

} // xml 類別結束

/* 這段程式碼可以執行 */
$xml_parser = new xml();
$xml_parser->parse("<A ID='hallo'>PHP</A>");

/* 這段程式碼可以執行 */
$xml_parser2 = new xml();
$xml_parser2->parse("<A ID='hallo2'>PHP2</A>");

/* 這段程式碼無法執行 */
$xml_parser = new xml();
$xml_parser->parse("<A ID='hallo3'>PHP3</A>");
?>

在第三個程式碼區塊中,php 回報找不到它需要的處理器。
這個問題似乎只有在變數被多次使用時才會發生。
呼叫 xml_parser_free 沒有幫助
zitan at mediasculpt dot net
21 年前
關於建立抽象「回呼處理器」(如同其他一些註解中提到的)的說明。在這種情況下,我建議擴展基礎的 XML 類別並覆寫處理器方法。我想要這樣做的原因是,如果你有一個獨立的回呼方法類別,它會導致問題,例如,如果你想從 XML 檔案中收集資訊並將其儲存在陣列中。你可以使用全域變數來解決這個問題,但我偏好只在需要時使用它們 ;)

範例

<?php
class xml_output extends xml{

var
$output = array();

function
xml_output(){
$this->xml();
}

//覆寫基礎方法
function tag_open($parser, $tag, $attributes)
{
array_push($this->output, "<$tag, attributes>");
}

function
cdata($parser, $cdata)
{
array_push($this->output, "$cdata");
}

function
tag_close($parser, $tag)
{
array_push($this->output, "</$tag>");
}

}

$xml_parser = & new xml_output();
$xml_parser->parse("<A ID='hallo'>PHP</A>");
echo(
"$xml_parser->output");
?>
i_sofer at yahoo dot com
24 年前
在物件中使用 xml 解析器似乎有一個問題,即修改後的值(甚至是物件本身的值)在解析結束後就會遺失...
joey at gimo dot co dot uk
8 年前
當 [$object_instance, "method_name"] 是可呼叫的時,這個函數的用途似乎非常有限。
hwheinzen at t-online dot de
20 年前
(補充 zitan 的註解)
為回呼處理器類別提供輸出函數似乎也很容易。

範例

<?php
class CallBack {
var
$name = 'Callback';
var
$info = 'Information!';
function
toString() {
return
$this->name.': '.$this->info;
}
}
class
Main {
var
$name = 'Main';
var
$callBackObject;
function
setCallBack(&$cBIn) { $this->callBackObject = $cBIn; }
function
toString() {
return
$this->name.': '.$this->callBackObject->toString();
}
}

$cb = & new CallBack;
$m = & new Main;
$m->setCallBack(&$cb);
echo
$cb->toString();
echo
$m->toString();
?>

因此,在回呼處理器類別中,例如在 tag_close() 中,於解析操作期間收集資訊後,可以擷取它們。
sbeam at syxyz dot net
22 年前
如同範例中那樣,將物件作為呼叫時的參考 (&$this) 傳遞會在 php 4.1+ 中產生警告。請改用 xml_set_object($xp,$this);。這似乎沒有破壞任何東西 - 但我不確定。
chiefgeek at ecodedesign.com
22 年前
只是對我上面寫的範例做一點補充。它需要一些改進。

由於 PHP 預設按值傳遞,當你像這樣傳遞陣列時
array($callback_handler, 'handler_method')
PHP 會複製 callback_handler 物件,並在副本中使用 handler_method。

由於許多原因,這不是理想的情況...我不會在這裡深入探討...但你現在應該已經有概念了。

解決這個問題的最好方法是更改一些東西。在函數宣告中,將參數從 $callback_handler 更改為 &$callback_handler。因此,你的宣告現在應該看起來像這樣

function set_callback_handler(&$callback_handler)
{
...
}

現在,每次你參考 $callback_handler 時,將其更改為 &$callback_handler。例如

xml_set_element_handler(
$this->xml_parser,
array(&$callback_handler, 'start_element'),
array(&$callback_handler, 'close_Element' ));

這確保 PHP 將始終使用相同的物件。
chiefgeek at ecodedesign.com
22 年前
如果你想在你的 xml 類別中硬式編碼你的 start_element、end_element 等函數,那麼 xml_set_object 非常棒。

但如果你想提高應用程式的模組化程度呢?

解決方案是建立一個通用的 XMLParser 類別,它處理所有事情,*除了* 回呼函數。然後建立一個抽象的 XMLCallbackHandler 類別,你可以擴展它以提供任何你想要的自訂設定。

那麼,你如何告訴 php 的 xml_parser 你想使用這個其他類別來處理回呼函數?

xml_set_object?只有當函數在你從中呼叫此方法的物件*內*時,此方法才有效。

解決方案在 xml_set_element_handler() 函數中。
看看這個範例程式碼...

class XMLParser
{

...

function set_callback_handler($callback_handler)
{
// 指派 startElement endElement 函數
xml_set_element_handler(
$this->xml_parser,
array($callback_handler, 'start_element'),
array($callback_handler, 'close_Element' ));

/* 透過將具有此 ($object, 'function_name') 結構的陣列
傳遞給第二和第三個參數。我們可以告訴解析器
在另一個物件中尋找這些回呼函數。
*/

// 指派字元資料函數
xml_set_character_data_handler(
$this->xml_parser,
array($callback_handler, 'character_data'));
}
}

class myCallBackHandler
{
function start_element(...)
{
//程式碼放在這裡
}

function end_element(...)
{
//程式碼放在這裡
}

function character_data(...)
{
//程式碼放在這裡
}
}

現在剩下的就是使用這些類別了..

$parser = new XMLParser();
$parser->set_callback_handler(new myCallbackHandler());
dfoesch at cs dot nmsu dot edu
22 年前
英文解釋為什麼你要這樣做:$xml = & new xml();

好的,當 PHP 執行 "new xml()" 時,它會建立一個匿名變數(您無法用任何名稱參照的變數),然後在這個變數上執行建構子函數。好的,一旦完成,它會在上面的範例中以「值」的方式賦值。這表示您指向剖析器的指標實際上是指向您類別的匿名實例,而不是您使用的類別實例...因此造成所有變數的「影子」,其中在剖析器內部賦值會存取與剖析器外部不同的變數。PHP _應該_做的是(類似於 C++),讓這個陳述式的執行方式是,賦值是以「參照」的方式完成,這樣您就可以將新的名稱賦值給實際建構的類別,而不是賦值給您所建構類別的副本。

如果他們做對了,這段程式碼就不會出錯。
匿名
22 年前
呼叫時傳遞參照的方式已被棄用,所以這個範例是有錯誤的,正如前面已經提到的。然而,從 4.04 版本(我認為)開始,"new" 可以傳回物件的參照。因此,在建構子中初始化剖析器,並將結果儲存在您的物件中的乾淨方式是這樣做:

$xml_parser = & new xml();

請參閱 https://php.dev.org.tw/manual/en/language.oop.newref.php

Ivan
pete dot nelson at serverSXPYAZMsolved dot com
23 年前
<p>回覆 jon9mm 的問題,我發現了相同的問題(函數 'startElement' 不存在)。我最後找到了解決方法,透過重新閱讀 xml_set_object 的文件。您必須在您的剖析器函數中使用 xml_set_object($this->parser, &$this)。</p> <p>當該函數被呼叫時,您的物件會暫時變成剖析器物件並共享作用域(所以它可以看見 'startElement' 等)。一旦該函數完成,xml_set_object(...) 呼叫就會超出作用域,而您的物件就不再與剖析器綁定。所以最簡單的解決方案是在同一個函數中呼叫 xml_set_object(...)、執行任何剖析,然後呼叫 xml_parser_free(...)。</p>
<p>請記住,不要在您的物件建構子中呼叫 xml_set_object(...) - 您的物件會永遠與剖析器物件綁定,而您會失去對物件成員函數的所有存取權限。</p>
<p>這一切都在上面的範例中解釋過,但我讀了好幾次才理解它如何運作。</p>
nick at category4 dot com
23 年前
如果您在物件中使用 XML 剖析器,請小心不要意外呼叫物件中不存在的任何方法。PHP 不會抱怨找不到遺失的方法,而是會說它無法找到處理常式,即使您已正確指示它們。(在 FreeBSD 上使用 PHP 4.0.5 版本)。
williams at cse dot unl dot edu
23 年前
如果沒有在物件的建構子中呼叫 xml_set_object,剖析器修改的值將會被儲存。否則,這些值似乎不會被儲存,如上面 "i_sofer" 所述。
--- 開始範例程式碼 ---
class foo {
var xmlparser;
function foo() { \\建構子
$this->xmlparser=xmlparser_create();
}
function parse() {
xml_set_object($this->xmlparser,&$this);
\\也要包含資料處理常式
\\以及此處的元素處理常式
}
}
--- 結束範例程式碼 ---
malcontent at msgto dot com
24 年前
除非在您的 INI 檔案中將 allow_call_time_pass_reference 設定為 true,否則此範例會產生警告。它也不會接受以值方式傳遞的呼叫。
robert at webmotion dot com
24 年前
雖然 PHP 類別物件沒有自動解構子是事實,但建立一個名為 destroy 的方法並手動執行解構是非常簡單的。在上面的範例中,可能不需要立即釋放 XML 剖析器...從範例來看,該物件似乎是可以重複使用的。
To Top