人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

PHPのxml_parse関数について教えてください。


xml_set_character_data_handler($xml_parser, "characterData");
ここで指定したcharacterData($parser, $data)の$dataにはちゃんと情報が入っているのに、これをglobal変数につっこんだのち、
xml_set_element_handler($xml_parser, "startElement", "diaryElement");
のfunction diaryElement($parser, $name)に移動したとき突っ込んだglobalの値を参照したら桁落ちしているのはどういうわけでしょうか?


ちなみに、自分の思ったとおりに動かないサンプルとソースはこちら。
http://kuippa.s188.xrea.com/h/ ここにソースもおいています。
これでつかってる getxml.php というファイルです。
xml_parseの使い方がよくわかっていないので、こんな風にやるんだよ?というサンプルなどがあればお教えいただければこれ幸い。

●質問者: くいっぱ
●カテゴリ:コンピュータ ウェブ制作
✍キーワード:name PHP ソース ファイル 変数
○ 状態 :終了
└ 回答数 : 2/2件

▽最新の回答へ

1 ●
●35ポイント ベストアンサー

調べてみたところ、xml_set_character_data_handlerに似たような現象(要素が途中で分割されてしまって最後の部分しか取得できない)がいくつか載っていました。

それを参考にして回答いたします。

ソースを見させていただきましたが、$retXmlの初期化の位置を修正すれば良いと思います。

getxml.phpの

function characterData($parser, $data){
global $retXml;
$retXml = "";
$retXml = $data;
//$retXml = mb_convert_encoding($data,"utf-8","utf-8");
// ここにはちゃんとデータがはいっているのだけど…
}

の部分を

function characterData($parser, $data){
global $retXml;
//$retXml = "";
$retXml .= $data;
//$retXml = mb_convert_encoding($data,"utf-8","utf-8");
// ここにはちゃんとデータがはいっているのだけど…
}

のように変更し、characterData内では$retXmlの初期化を行わず、次々と$dataの内容をappendするようにします。

これで複数に分割されてしまった要素の全てを$retXmlに入れることができます。

このままですと、$retXmlが初期化される場所がなくなってしまいます。

そこで、xml_set_element_handlerのend_element_handlerとして呼ばれる各種関数の最後に$retXmlを初期化する内容を追記します。

getxml.phpを確認したところ、xml_set_element_handlerのend_element_handler($xmlElement)は、fGetXmlの2つ目の引数で取得するものでした。

sns.phpにてfGetXmlの2つ目の引数にはdiaryElementとbmarkElementが指定されています。

そこで、getxml.php内のdiaryElementとbmarkElementの最後に

unset($GLOBALS['retXml']);

という行を追記し、ここで$retXmlを初期化します。

初期化の方法は

$retXML = "" ;

でも良いはずです。

(恥ずかしながら違いがよく分かってません…。)

これで上手く動くようになると思うのですが、いかがでしょうか。

一度確認してみてください。

◎質問者からの返答

おー、できました!

printで吐かせていたので、気がつきませんでしたがcharacterDataって一回で完結してなかったんですね。

ありがとうございました。


2 ● j0hn
●35ポイント

例えば、以下のようなXMLの場合

<description>文字列文字列……文字列</description>

characterData関数が呼ばれたときに「文字列……」のすべてが一度に渡されるように思えるのですが、実際にはそうではなく、何度かにわけて渡されるようです。

startElementで

global $retXml;
$retXml = "";

として、characterData内で、 $retXml = $dataとしているところを

$retXml = $retXml . $data (もしくは $retXml .= $data)

として、連結して伸ばしていくというのはどうでしょうか。

PHPのマニュアルのコメント欄にはxml_set_character_data_handlerでセットされたhandlerがうけとるdataの最大長が1024バイトなので、それ以上のデータが渡されるときには連結して受け取れ、とあります。(今、うちのPHPで試してみた感じだと、500バイトちょっとで千切れるようです) このことをきちんとマニュアル本体の説明に書いておくべきではないか、というコメントもあります。

freadで最大4096バイトしか読まずに処理しているわけですから、handlerのほうも途中で切れてもいいように書いておかないとまずい、ということでしょうか。

◎質問者からの返答

ありがとうございます。一度のコールで完結しているものだと思っていました。

characterData内でprintさせてちゃんとデータが入ってるのになとおもったのですが、実際には何度か走ってたのですね。ありがとうございました。

解決しました。

関連質問


●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ