Finally a simple xml => array class.
Functioning like SimpleXML library.
<?php
class xml {
private $parser;
private $pointer;
public $dom;
public function __construct($data) {
$this->pointer =& $this->dom;
$this->parser = xml_parser_create();
xml_set_object($this->parser, $this);
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($this->parser, "tag_open", "tag_close");
xml_set_character_data_handler($this->parser, "cdata");
xml_parse($this->parser, $data);
}
private function tag_open($parser, $tag, $attributes) {
if (isset($this->pointer[$tag]['@attributes'])) {
$content = $this->pointer[$tag];
$this->pointer[$tag] = array(0 => $content);
$idx = 1;
} else if (isset($this->pointer[$tag]))
$idx = count($this->pointer[$tag]);
if (isset($idx)) {
$this->pointer[$tag][$idx] = Array(
'@idx' => $idx,
'@parent' => &$this->pointer);
$this->pointer =& $this->pointer[$tag][$idx];
} else {
$this->pointer[$tag] = Array(
'@parent' => &$this->pointer);
$this->pointer =& $this->pointer[$tag];
}
if (!empty($attributes))
$this->pointer['@attributes'] = $attributes;
}
private function cdata($parser, $cdata) {
$this->pointer['@data'] = $cdata;
}
private function tag_close($parser, $tag) {
$current = & $this->pointer;
if (isset($this->pointer['@idx']))
unset($current['@idx']);
$this->pointer = & $this->pointer['@parent'];
unset($current['@parent']);
if (isset($current['@data']) && count($current) == 1)
$current = $current['@data'];
else if (empty($current['@data'])||$current['@data']==0)
unset($current['@data']);
}
}
?>
maybe I'll do some explanations on habr
XML 해석기 함수
소개
XML(eXtensible Markup Language)은 웹에서 구조화된 문서 교환을 위한 데이터 형식입니다. The World Wide Web consortium(W3C)에서 정의한 표준입니다. XML과 관련 기술에 대한 정보는 » http://www.w3.org/XML/에서 볼 수 있습니다.
PHP 확장은 James Clark의 expat를 사용합니다. 이 툴킷은 XML 문서를 처리할 수 있게 하지만, 유효성을 검증하지는 않습니다. PHP에서도 지원하는 세가지 문자 인코딩을 지원합니다: US_ASCII, ISO-8859-1, UTF-8. UTF-16은 지원하지 않습니다.
이 확장은 XML 파서를 작성하고 여러가지 XML 이벤트에 대한 핸들러를 정의할 수 있게 합니다. 각각의 XML 파서는 조절할 수 있는 약간의 인자를 가집니다.
요구 조건
이 확장은 » http://www.jclark.com/xml/expat.html에서 찾을 수 있는 expat를 사용합니다. expat에 들어있는 Makefile은 기본값으로 라이브러리를 생성하지 않기 때문에, 다음의 make 규칙을 사용할 수 있습니다:
libexpat.a: $(OBJS)
ar -rc $@ $(OBJS)
ranlib $@
설치
이 함수들은 번들된 expat 라이브러리를 이용하여 기본값으로 활성화되어 있습니다. XML 지원을 비활성화 하려면 --disable-xml을 사용하십시오. PHP를 아파치 1.3.9 이상의 모듈로 컴파일한다면, PHP는 자동적으로 아파치에 번들된 expat를 사용합니다. 번들된 expat 라이브러리를 사용하지 않으려면, PHP 설정에 --with-expat-dir=DIR을 지정하십시오. DIR은 expat를 설치한 베이스 디렉토리를 지정해야 합니다.
PHP 윈도우 버전에서는 이 확장에 대한 지원이 포함되어 있습니다. 이 함수들을 이용하기 위해서 추가로 확장을 읽어들일 필요가 없습니다.
실행시 설정
이 확장은 php.ini 설정이 존재하지 않습니다.
자원형
xml
xml_parser_create()와 xml_parser_create_ns()가 반환하는 xml 자원은 xml 파서 인스탠스를 참조하고, 이 확장이 제공하는 함수들이 사용합니다.
예약 상수
이 확장은 다음의 상수들을 정의합니다. 이 확장을 PHP에 내장했거나, 실행시에 동적으로 읽어들일 경우에만 사용할 수 있습니다.
- XML_ERROR_NONE (integer)
- XML_ERROR_NO_MEMORY (integer)
- XML_ERROR_SYNTAX (integer)
- XML_ERROR_NO_ELEMENTS (integer)
- XML_ERROR_INVALID_TOKEN (integer)
- XML_ERROR_UNCLOSED_TOKEN (integer)
- XML_ERROR_PARTIAL_CHAR (integer)
- XML_ERROR_TAG_MISMATCH (integer)
- XML_ERROR_DUPLICATE_ATTRIBUTE (integer)
- XML_ERROR_JUNK_AFTER_DOC_ELEMENT (integer)
- XML_ERROR_PARAM_ENTITY_REF (integer)
- XML_ERROR_UNDEFINED_ENTITY (integer)
- XML_ERROR_RECURSIVE_ENTITY_REF (integer)
- XML_ERROR_ASYNC_ENTITY (integer)
- XML_ERROR_BAD_CHAR_REF (integer)
- XML_ERROR_BINARY_ENTITY_REF (integer)
- XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF (integer)
- XML_ERROR_MISPLACED_XML_PI (integer)
- XML_ERROR_UNKNOWN_ENCODING (integer)
- XML_ERROR_INCORRECT_ENCODING (integer)
- XML_ERROR_UNCLOSED_CDATA_SECTION (integer)
- XML_ERROR_EXTERNAL_ENTITY_HANDLING (integer)
- XML_OPTION_CASE_FOLDING (integer)
- XML_OPTION_TARGET_ENCODING (integer)
- XML_OPTION_SKIP_TAGSTART (integer)
- XML_OPTION_SKIP_WHITE (integer)
이벤트 핸들러
정의된 XML 이벤트 핸들러는:
| 핸들러를 설정하는 PHP 함수 | 이벤트 설명 |
|---|---|
| xml_set_element_handler() | 엘레멘트 이벤트는 XML 파서가 시작과 끝 태그에 도달했을 때 발생합니다. 시작 태그와 끝 태그에 별도의 핸들러가 존재합니다. |
| xml_set_character_data_handler() | Character data is roughly all the non-markup contents of XML documents, including whitespace between tags. Note that the XML parser does not add or remove any whitespace, it is up to the application (you) to decide whether whitespace is significant. |
| xml_set_processing_instruction_handler() | PHP 프로그래머는 이미 프로세싱 인스트럭션(PIs)에 익숙할 것입니다. <?php ?>는 프로세싱 인스트럭션이고, php은 "PI 타겟"이라 불립니다. 예약된 "XML"로 시작하는 PI 타겟들을 제외하면, 이들에 대한 핸들링은 어플리케이션 특화입니다. |
| xml_set_default_handler() | What goes not to another handler goes to the default handler. You will get things like the XML and document type declarations in the default handler. |
| xml_set_unparsed_entity_decl_handler() | 이 핸들러는 unparsed (NDATA) 엔트리의 정의를 호출합니다. |
| xml_set_notation_decl_handler() | 이 핸들러는 notation의 정의를 호출합니다. |
| xml_set_external_entity_ref_handler() | This handler is called when the XML parser finds a reference to an external parsed general entity. This can be a reference to a file or URL, for example. See the external entity example for a demonstration. |
케이스 폴딩
The element handler functions may get their element names case-folded. Case-folding is defined by the XML standard as "a process applied to a sequence of characters, in which those identified as non-uppercase are replaced by their uppercase equivalents". In other words, when it comes to XML, case-folding simply means uppercasing.
By default, all the element names that are passed to the handler functions are case-folded. This behaviour can be queried and controlled per XML parser with the xml_parser_get_option() and xml_parser_set_option() functions, respectively.
오류 코드
다음 상수들이 XML 오류 코드로 정의되어 있습니다 (xml_parse()가 반환합니다):
- XML_ERROR_NONE
- XML_ERROR_NO_MEMORY
- XML_ERROR_SYNTAX
- XML_ERROR_NO_ELEMENTS
- XML_ERROR_INVALID_TOKEN
- XML_ERROR_UNCLOSED_TOKEN
- XML_ERROR_PARTIAL_CHAR
- XML_ERROR_TAG_MISMATCH
- XML_ERROR_DUPLICATE_ATTRIBUTE
- XML_ERROR_JUNK_AFTER_DOC_ELEMENT
- XML_ERROR_PARAM_ENTITY_REF
- XML_ERROR_UNDEFINED_ENTITY
- XML_ERROR_RECURSIVE_ENTITY_REF
- XML_ERROR_ASYNC_ENTITY
- XML_ERROR_BAD_CHAR_REF
- XML_ERROR_BINARY_ENTITY_REF
- XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF
- XML_ERROR_MISPLACED_XML_PI
- XML_ERROR_UNKNOWN_ENCODING
- XML_ERROR_INCORRECT_ENCODING
- XML_ERROR_UNCLOSED_CDATA_SECTION
- XML_ERROR_EXTERNAL_ENTITY_HANDLING
문자 인코딩
PHP의 XML 확장은 » 유니코드 문자셋을 통해 서로 다른 문자 인코딩을 지원합니다. 문자 인코딩에는 소스 인코딩과 타겟 인코딩의 두 종류가 존재합니다. PHP 내부에서 문서 표현은 항상 UTF-8로 인코드되어 있습니다.
소스 인코딩은 XML 문서가 parse되었을 때 이루어집니다. XML 파서를 작성할 때, 소스 인코딩을 지정할 수 있습니다. (이 인코딩은 XML 파서가 종료될때까지 변경할 수 없습니다) 지원하는 소스 인코딩은 ISO-8859-1, US-ASCII, UTF-8입니다. 앞쪽의 두개는 싱글-바이트 인코딩이기에, 각각의 문자는 하나의 바이트로 표현됩니다. UTF-8은 1에서 4바이트 사이에서 다양한 수의 비트(21까지)를 조합하여 인코드할 수 있습니다. PHP에서 사용하는 기본 소스 인코딩은 ISO-8859-1입니다.
타겟 인코딩은 PHP가 XML 핸들러 함수에 데이터를 넘길 때 이루어집니다. XML 파서를 작성하면, 타겟 인코딩을 소스 인코딩과 동일하게 설정하지만, 이는 언제라도 변경할 수 있습니다. 타켓 인코딩은 문자 데이터뿐만 아니라 태그 이름과 프로세싱 인스트럭션 타겟에도 영향을 미칩니다.
If the XML parser encounters characters outside the range that its source encoding is capable of representing, it will return an error.
If PHP encounters characters in the parsed XML document that can not be represented in the chosen target encoding, the problem characters will be "demoted". Currently, this means that such characters are replaced by a question mark.
예제
XML 문서를 파싱하는 몇몇 예제 PHP 스크립트입니다.
XML 엘레멘트 구조 예제
This first example displays the structure of the start elements in a document with indentation.
Example#1 XML 엘레멘트 구조 보기
<?php
$file = "data.xml";
$depth = array();
function startElement($parser, $name, $attrs)
{
global $depth;
for ($i = 0; $i < $depth[$parser]; $i++) {
echo " ";
}
echo "$name\n";
$depth[$parser]++;
}
function endElement($parser, $name)
{
global $depth;
$depth[$parser]--;
}
$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
if (!($fp = fopen($file, "r"))) {
die("XML 입력을 열 수 없습니다.");
}
while ($data = fread($fp, 4096)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
die(sprintf("XML 에러: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
xml_parser_free($xml_parser);
?>
XML 외부 엔티티 예제
This example highlights XML code. It illustrates how to use an external entity reference handler to include and parse other documents, as well as how PIs can be processed, and a way of determining "trust" for PIs containing code.
XML documents that can be used for this example are found below the example (xmltest.xml and xmltest2.xml.)
Example#3 External Entity Example
<?php
$file = "xmltest.xml";
function trustedFile($file)
{
// only trust local files owned by ourselves
if (!eregi("^([a-z]+)://", $file)
&& fileowner($file) == getmyuid()) {
return true;
}
return false;
}
function startElement($parser, $name, $attribs)
{
echo "<<font color=\"#0000cc\">$name</font>";
if (sizeof($attribs)) {
while (list($k, $v) = each($attribs)) {
echo " <font color=\"#009900\">$k</font>=\"<font
color=\"#990000\">$v</font>\"";
}
}
echo ">";
}
function endElement($parser, $name)
{
echo "</<font color=\"#0000cc\">$name</font>>";
}
function characterData($parser, $data)
{
echo "<b>$data</b>";
}
function PIHandler($parser, $target, $data)
{
switch (strtolower($target)) {
case "php":
global $parser_file;
// If the parsed document is "trusted", we say it is safe
// to execute PHP code inside it. If not, display the code
// instead.
if (trustedFile($parser_file[$parser])) {
eval($data);
} else {
printf("Untrusted PHP code: <i>%s</i>",
htmlspecialchars($data));
}
break;
}
}
function defaultHandler($parser, $data)
{
if (substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") {
printf('<font color="#aa00aa">%s</font>',
htmlspecialchars($data));
} else {
printf('<font size="-1">%s</font>',
htmlspecialchars($data));
}
}
function externalEntityRefHandler($parser, $openEntityNames, $base, $systemId,
$publicId) {
if ($systemId) {
if (!list($parser, $fp) = new_xml_parser($systemId)) {
printf("Could not open entity %s at %s\n", $openEntityNames,
$systemId);
return false;
}
while ($data = fread($fp, 4096)) {
if (!xml_parse($parser, $data, feof($fp))) {
printf("XML error: %s at line %d while parsing entity %s\n",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser), $openEntityNames);
xml_parser_free($parser);
return false;
}
}
xml_parser_free($parser);
return true;
}
return false;
}
function new_xml_parser($file)
{
global $parser_file;
$xml_parser = xml_parser_create();
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 1);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
xml_set_processing_instruction_handler($xml_parser, "PIHandler");
xml_set_default_handler($xml_parser, "defaultHandler");
xml_set_external_entity_ref_handler($xml_parser, "externalEntityRefHandler");
if (!($fp = @fopen($file, "r"))) {
return false;
}
if (!is_array($parser_file)) {
settype($parser_file, "array");
}
$parser_file[$xml_parser] = $file;
return array($xml_parser, $fp);
}
if (!(list($xml_parser, $fp) = new_xml_parser($file))) {
die("could not open XML input");
}
echo "<pre>";
while ($data = fread($fp, 4096)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
die(sprintf("XML error: %s at line %d\n",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
echo "</pre>";
echo "parse complete\n";
xml_parser_free($xml_parser);
?>
Example#4 xmltest.xml
<?xml version='1.0'?>
<!DOCTYPE chapter SYSTEM "/just/a/test.dtd" [
<!ENTITY plainEntity "FOO entity">
<!ENTITY systemEntity SYSTEM "xmltest2.xml">
]>
<chapter>
<TITLE>Title &plainEntity;</TITLE>
<para>
<informaltable>
<tgroup cols="3">
<tbody>
<row><entry>a1</entry><entry morerows="1">b1</entry><entry>c1</entry></row>
<row><entry>a2</entry><entry>c2</entry></row>
<row><entry>a3</entry><entry>b3</entry><entry>c3</entry></row>
</tbody>
</tgroup>
</informaltable>
</para>
&systemEntity;
<section xml:id="about">
<title>About this Document</title>
<para>
<!-- this is a comment -->
<?php echo 'Hi! This is PHP version '.phpversion(); ?>
</para>
</section>
</chapter>
이 파일은 xmltest.xml에서 포함합니다:
Example#5 xmltest2.xml
<?xml version="1.0"?> <!DOCTYPE foo [ <!ENTITY testEnt "test entity"> ]> <foo> <element attrib="value"/> &testEnt; <?php echo "This is some more PHP code being executed."; ?> </foo>
Table of Contents
- utf8_decode — Converts a string with ISO-8859-1 characters encoded with UTF-8 to single-byte ISO-8859-1
- utf8_encode — Encodes an ISO-8859-1 string to UTF-8
- xml_error_string — Get XML parser error string
- xml_get_current_byte_index — Get current byte index for an XML parser
- xml_get_current_column_number — Get current column number for an XML parser
- xml_get_current_line_number — Get current line number for an XML parser
- xml_get_error_code — Get XML parser error code
- xml_parse_into_struct — Parse XML data into an array structure
- xml_parse — Start parsing an XML document
- xml_parser_create_ns — Create an XML parser with namespace support
- xml_parser_create — Create an XML parser
- xml_parser_free — Free an XML parser
- xml_parser_get_option — Get options from an XML parser
- xml_parser_set_option — Set options in an XML parser
- xml_set_character_data_handler — Set up character data handler
- xml_set_default_handler — Set up default handler
- xml_set_element_handler — Set up start and end element handlers
- xml_set_end_namespace_decl_handler — Set up end namespace declaration handler
- xml_set_external_entity_ref_handler — Set up external entity reference handler
- xml_set_notation_decl_handler — Set up notation declaration handler
- xml_set_object — Use XML Parser within an object
- xml_set_processing_instruction_handler — Set up processing instruction (PI) handler
- xml_set_start_namespace_decl_handler — Set up start namespace declaration handler
- xml_set_unparsed_entity_decl_handler — Set up unparsed entity declaration handler
XML
28-Jul-2008 03:09
18-May-2008 12:18
This is peace of the code. It edit xml file.
<?
$songs = Array();
function start_element($parser, $name, $attrs){
global $songs;
if($name == "song"){
array_push($songs, $attrs);
}
}
function end_element ($parser, $name){}
$playlist_string = file_get_contents("test.xml");
$parser = xml_parser_create();
xml_set_element_handler($parser, "start_element", "end_element");
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parse($parser, $playlist_string) or die("Error parsing XML document.");
print "<br />";
if($_POST['action'] == "ins"){
array_push($songs, Array(
"title" => $_POST['title'],
"artist" => $_POST['artist'],
"path" => $_POST['path']));
$songs_final = $songs;
}else if($_POST['action'] == "del"){
$songs_final = Array();
foreach($songs as $song){
if($song['title'] != $_POST['title']){
array_push($songs_final, $song);
}
}
}
$write_string = "<songs>";
foreach($songs_final as $song){
$write_string .= "<song>";
$write_string .= "<title>".$song['title']."</title>";
$write_string .= "<artist>".$song['artist']."</artist>";
$write_string .= "<path>".$song['path']."</path>";
$write_string .= "</song>";
}
$write_string .= "</songs>";
$fp = fopen("test.xml", "w+");
fwrite($fp, $write_string) or die("Error writing to file");
fclose($fp);
print "<em>Song inserted or deleted successfully :)</em><br />";
print "<a href=\"index.php\" title=\"return\">Return</a>";
?>
29-Apr-2008 01:48
An update to the function below. Fixes a bug where the data of the first tag, would occasionally get appended to the beginning of the tag data of the second tag.
<?php
foreach($dom['child_nodes'][0]['child_nodes'] as $key => $value) {
$tagname = $value['tag_name'];
if(isset($value['child_nodes'][0])) {
$numarrays = count($value['child_nodes']);
if($numarrays > 1) {
$contents = "";
foreach($value['child_nodes'] as $key => $value2) {
$contents .= $value2;
}
}else {
$contents = $value['child_nodes'][0];
}
}else {
$contents = 'isempty';
}
$artmp = array($tagname => $contents);
array_push_associative($xmlarray,$artmp);
unset($artmp);
}
?>
25-Apr-2008 02:28
If anyone else is having issues figuring out how to utilize the xml class that people have created and modified, don't worry as you are not alone. It took me a bit to come up with a solution that I liked, but I feel this does the job quite nice.
I read through the entire structure of the xml file and create an associative array based on the tag names.
I didn't worry about tag attributes as I didn't need to use them; so remember that if you use this method, you are only getting the tag name and the data inside the tag...that is all, no attributes!!
I am not going to include the xml class as it has been copy pasted multiple times already on this thread. Just scroll down for the xml class.
First let me show just an example of the EXTREMELY simple xml structure I was working with. Again, you will need to make modifications depending on the structure of the xml file you are working with! (I know I could use simplexml but I have php4 and not 5).
<?xml version="1.0"?>
<menuitems>
<menutype>1</menutype>
<product>Just some product info</product>
<shipping>some stuff</shipping>
</menuitems>
The custom associative array push function taken from:
http://us.php.net/manual/en/function.array-push.php#58705
The xml class file is located here:
http://us.php.net/manual/en/ref.xml.php#81910
<?php
// Obtain the exact path to the xml file
$xmlfile = "mydata.xml";
$fp = fopen($xmlfile,"r"); // open the xml file
$xml = fread($fp, filesize($xmlfile)); // read in the size of the file into the variable xml
fclose($fp); // close the stream
$xml_parser = new xml(); // create a new xml class instance
$xml_parser->parse($xml); // parse the variable xml which contains our xml data
$dom = $xml_parser->dom; // make a variable that holds the entire dom
/*
This part extracts the xml nodes from the dom and places them into an associative array.
The associative array key is the name of the tag; the value is the tag contents.
We simply create an array on the fly using the name and contents, and hit that array
with our original array using the array_push_associative function. We then check if
isset to prevent errors from being displayed. If the tag contents are empty,
I put the string isempty inside so I can easily check to see later if there is contents or not.
*/
$xmlarray = array(); // the array we are going to store the information within the tag
$contents = "";
foreach($dom['child_nodes'][0]['child_nodes'] as $key => $value) {
$tagname = $value['tag_name'];
if(isset($value['child_nodes'][0])) {
$numarrays = count($value['child_nodes']);
if($numarrays > 1) {
foreach($value['child_nodes'] as $key => $value2) {
$contents .= $value2;
}
}else {
$contents = $value['child_nodes'][0];
}
}else {
$contents = 'isempty';
}
$artmp = array($tagname => $contents);
array_push_associative($xmlarray,$artmp);
unset($artmp);
}
unset($xml); // free up resources
unset($xml_parser); // free up resources
unset($dom); // free up resources
?>
You may be wondering why there is a nested count and foreach loop inside the main foreach loop. The reason that exists is that the xml class that I am using in this example, the one that is four posts down from this one, has the wonderful behavior in that when something hits the length of 1024 characters, it creates a new element in the array and puts the next 1024 characters into that next element etc. This caused me massive confusion as to why some of my data was getting cut off.
So say I wanted to display the data inside the product tag, all I would need to do is this:
<?php
echo $xmlarray['product']
?>
I sincerely hope this helps people figure out how to utilize the xml class quicker than I did!
If anyone has suggestions, modifications, or whatever, please post it here!
Thanks
19-Apr-2008 12:10
I used shawn's code that is an ongoing fix/update of a very nice php 4 & 5 compatible class.
It works great, only it kept giving me errors when the array isn't set, (I have errors set to show all).
<?php
// Here is the old function that gave errors:
function makeChildNode() {
if (!is_array($this->pointer['child_nodes'])){
$this->pointer['child_nodes'] = array();
}
return count($this->pointer['child_nodes']);
}
// Here is the new function that does not spit errors:
function makeChildNode() {
if (!isset($this->pointer['child_nodes'])){
$this->pointer['child_nodes'] = array();
}
return count($this->pointer['child_nodes']);
}
?>
03-Apr-2008 12:24
Well I posted my script with an example fread($fp, 4096) meaning that it will only read 4k. It was just for a quick example. If you used that to input data from a really long XML file to the parser that would be the problem.
you could replace the 4096 with filesize("file.xml") or try replacing that example test code part with:
$xml = implode('',file("http://localhost/test.xml"));
$xml_parser = new XML_Class();
$xml_parser->parse($xml);
print_r($xml_parser->dom);
I've tried to recreate your problems by posting entire howto of installing LDAP into character data space of a node and can't get it to fail. Please email with more info if the above isn't the problem.
But on that routine you posted from that website. The problem with that one is it seems to be padding with unnecessary arrays. It will overwrite different nodes with the same name if they are within the same parent. And the number one biggest issue for me is that it drops attributes. That is totally bogus. It's a lot cleaner to store most values in attributes than making a zillion nodes and storing the data for something small like a integer or a float as character data.
Example:
<coords x="1.53234" y="56.287" z="4.32" />
VS
<coords><x>1.53234</x><y>56.287</y><z>4.32</z></coords>
To me the top is very readable where the later makes my eyes bleed.
Any ways what is good about the links code is the error checking. Isolating all the code in the parse method instead of constructor so the object is recyclable. And than releases the xml parser.
I'm definitely going to be putting that stuff into my class after I post this note.
But let me know if its still truncating.
18-Mar-2008 07:52
The reason why you would want to make your own simplistic DOM parser is because a lack of compatible between PHP 4's domxml and PHP 5's dom.
So it is for portability without having to wrapper the two different DOMs.
If you need a simple light weight XML parser that is portable this is the best way. If you are writing applications for a particular server and more concerned with functionality and speed go with a compiled in DOM.
Here is the fix to Emmetts code...
<?PHP
$fp = fopen("test.xml","r");
$xml = fread($fp, 4096);
fclose($fp);
$xml_parser = new xml();
$xml_parser->parse($xml);
$dom = $xml_parser->dom;
print_r($dom);
class xml {
var $parser;
var $pointer;
var $dom;
function xml() {
$this->pointer =& $this->dom;
$this->parser = xml_parser_create();
xml_set_object($this->parser, $this);
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
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 makeChildNode() {
if (!is_array($this->pointer['child_nodes'])){
$this->pointer['child_nodes'] = array();
}
return count($this->pointer['child_nodes']);
}
function tag_open($parser, $tag, $attributes) {
$idx = $this->makeChildNode();
$this->pointer['child_nodes'][$idx] = Array(
'_idx' => $idx,
'_parent' => &$this->pointer,
'tag_name' => $tag,
'attributes' => $attributes,
);
$this->pointer =& $this->pointer['child_nodes'][$idx];
}
function cdata($parser, $cdata) {
//drop text nodes that are just white space formatting characters
if (trim($cdata) != "") {
$idx = $this->makeChildNode();
$this->pointer['child_nodes'][$idx] = $cdata;
}
}
function tag_close($parser, $tag) {
$idx =& $this->pointer['_idx'];
$this->pointer =& $this->pointer['_parent'];
unset($this->pointer['child_nodes'][$idx]['_idx']);
unset($this->pointer['child_nodes'][$idx]['_parent']);
}
}
?>
07-Mar-2008 02:00
@[emmett dot thesane at yahoo dot com]: That code didn't work for me, but it seems that using the DOM functions (http://php.net/manual/en/ref.dom.php) would be more efficient.
10-Dec-2007 04:19
There's a couple of vital flaws in aquariusrick's example:
1. Multiple tags of the same name will overwrite one another.
2. Text nodes within an element are all strung together, with no information saved regarding their order with respect to non-text nodes.
It provided a good starting point, however, for a DOM-builder that *does* allow those things. This should be a more familiar structure for people used to DOM-walking in the browser; children of each node are stored in "childNodes". Text nodes are simply a child node that is only a string, instead of an array.
$xml_parser = new xml();
$xml_parser->parse($xml);
$dom = $xml_parser->dom;
print_r($dom);
class xml {
var $parser;
var $pointer;
var $dom;
function xml() {
$this->pointer =& $this->dom;
$this->parser = xml_parser_create();
xml_set_object($this->parser, $this);
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
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 makeChildNode() {
if (!isset($this->pointer['childNodes'])){
$this->pointer['childNodes'] = array();
}
return count($this->pointer['childNodes']);
}
function tag_open($parser, $tag, $attributes) {
$idx = $this->makeChildNode();
$this->pointer['childNodes'][$idx] = Array(
'_idx' => $idx,
'tagName' => $tag,
'parentNode' => &$this->pointer,
'attributes' => $attributes,
);
$this->pointer =& $this->pointer['childNodes'][$idx];
}
function cdata($parser, $cdata) {
$idx = $this->makeChildNode();
$this->pointer['childNodes'][$idx] = $cdata;
//text node -- has no other attributes than the content
}
function tag_close($parser, $tag) {
$idx =& $this->pointer['_idx'];
$this->pointer =& $this->pointer['_parent'];
unset($this->pointer['childNodes'][$idx]['_idx']);
}
}
05-Dec-2007 08:43
Here's another attempt at a very simple script that parses XML into a structure:
<?php
#Usage:
//$xml_parser = new xml();
//$xml_parser->parse($xml);
//$dom = $xml_parser->dom;
class xml {
var $parser;
var $pointer;
var $dom;
function xml() {
$this->pointer =& $this->dom;
$this->parser = xml_parser_create();
xml_set_object($this->parser, $this);
xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
xml_set_element_handler($this->parser, "tag_open", "tag_close");
xml_set_character_data_handler($this->parser, "cdata");
}
function parse($data) {
xml_parse(