Đọc và viết XML DOM với PHP

Sử dụng thư viện DOM, bộ phân tích cú pháp SAX và các biểu thức chính quy

Các kỹ thuật myriad sẵn có để đọc và viết XML trong PHP. Bài viết này trình bày ba phương thức để đọc XML: sử dụng thư viện DOM, sử dụng bộ phân tích cú pháp SAX, và sử dụng các biểu thức chính quy. Việc viết XML bằng cách sử dụng DOM và khuôn mẫu văn bản PHP cũng sẽ được đề cập.

Jack Herrington, Tổng biên tập, Code Generation Network

Jack D. Herrington là kỹ sư phần mềm cao cấp với hơn 20 năm kinh nghiệm. Ông là tác giả của ba cuốn sách: Code Generation in Action, Podcasting Hacks và PHP Hacks. Ông cũng đã viết hơn 30 bài báo.



08 01 2010

Việc đọc và viết Ngôn ngữ Đánh dấu Mở rộng (XML) trong PHP hình như hơi đáng ngại. Trong thực tế, XML và tất cả các công nghệ liên quan của nó có thể là nặng nề. Tuy nhiên, đọc và viết XML trong PHP không phải là một tác vụ đáng ngại. Trước tiên, bạn cần phải học một chút về XML -- nó là gì và sử dụng vào đâu. Sau đó, bạn cần phải học cách đọc và viết XML trong PHP, những thứ mà bạn có thể thực hiện bằng nhiều cách.

Bài viết này cung cấp bài học vỡ lòng ngắn trên XML rồi giải thích cách đọc và viết XML trong PHP.

XML là gì?

XML là một định dạng lưu trữ dữ liệu. Nó không định nghĩa dữ liệu nào đang được ghi lại hoặc cấu trúc của dữ liệu đó. XML chỉ định nghĩa các thẻ và thuộc tính cho các thẻ đó. Một thẻ XML có dạng đúng trông giống như thế này:

<name>Jack Herrington</name>

Thẻ <name> này chứa một số văn bản: Jack Herrington.

Một thẻ XML mà không chứa văn bản trông giống như thế này:

<powerUp />

Có thể có nhiều cách hơn để mã hoá một điều gì đó trong XML. Ví dụ, thẻ này tạo cùng một đầu ra như thẻ trước:

<powerUp></powerUp>

Bạn cũng có thể bổ sung các thuộc tính vào một thẻ XML. Thí dụ, thẻ <name> này chứa các thuộc tính đầu tiêncuối cùng:

<name first="Jack" last="Herrington" />

Bạn cũng có thể mã hoá các ký tự đặc biệt trong XML. Thí dụ, một ký hiệu ampersand (&) được mã hoá như thế này:

&

Một tài liệu XML chứa các thẻ và thuộc tính được định dạng như các thí dụ đã cho là đúng ngữ pháp, nghĩa là các thẻ được cân đối, và các ký tự được mã hoá đúng cách. Liệt kê 1 là một thí dụ về XML đúng ngữ pháp.

Liệt kê 1. Một thí dụ về mục lục XML các sách
  <books>
  <book>
  <author>Jack Herrington</author>
  <title>PHP Hacks</title>
  <publisher>O'Reilly</publisher>
  </book>
  <book>
  <author>Jack Herrington</author>
  <title>Podcasting Hacks</title>
  <publisher>O'Reilly</publisher>
  </book>
  </books>

XML trong Liệt kê 1 chứa một danh mục của các cuốn sách. Thẻ cha <books> (các sách) gồm một tập hợp các thẻ <book> (sách), mỗi thẻ chứa các thẻ <author> (tác giả), <title> (tiêu đề), và <publisher> (nhà xuất bản).

Các tài liệu XML là hợp lệ khi cấu trúc của các thẻ và nội dung của chúng được xác thực bởi một tệp lược đồ bên ngoài. Tệp lược đồ có thể được xác định trong một loạt các định dạng. Nhằm phục vụ bài viết này, tất cả mọi thứ bạn cần là XML đúng ngữ pháp.

Nếu bạn nghĩ rằng XML trong rất giống Ngôn ngữ Đánh dấu Siêu văn bản (HTML) thì bạn đã đúng. Cả XML và HTML là ngôn ngữ dựa trên các thẻ, và chúng có nhiều sự giống nhau. Tuy nhiên, điều quan trọng là phải lưu ý rằng trong khi các tài liệu XML có thể là HTML đúng ngữ pháp, không phải tất cả các tài liệu XML là HTML đúng ngữ pháp. Thẻ ngắt (br) là một ví dụ tuyệt hảo về những sự khác nhau giữa XML và HTML. Ngắt dòng này là HTML đúng ngữ pháp, nhưng XML không đúng ngữ pháp:

<p>This is a paragraph<br>
With a line break</p>

Ngắt dòng này là XML và HTML đúng ngữ pháp:

<p>This is a paragraph<br />
With a line break</p>

Nếu bạn muốn viết HTML cũng là XML đúng ngữ pháp, hãy theo chuẩn Ngôn ngữ Đánh dấu Siêu văn bản Mở rộng (XHTML) từ World Wide Web Consortium (W3C) (xem Tài nguyên). Tất cả các trình duyệt hiện đại trả về XHTML. Ngoài ra, nó còn có thể sử dụng các công cụ XML để đọc XHTML và để tìm dữ liệu trong các tài liệu, dễ hơn nhiều so với việc phân tích cú pháp thông qua HTML.

Đọc XML bằng cách sử dụng thư viện DOM

Cách dễ nhất để đọc một tệp XML đúng ngữ pháp là sử dụng thư viện Mô hình Đối tượng Tài liệu (DOM) được biên dịch sang một số cài đặt của PHP. Thư viện DOM đọc toàn bộ tài liệu XML sang bộ nhớ và trình bày nó như một cây các nút, như minh hoạ trong Hình 1.

Hình 1. Cây XML DOM dùng cho XML các sách
Cây XML DOM dùng cho XML các sách

Nút books tại đỉnh của cây có hai thẻ book con. Trong mỗi cuốn sách (book) , có các nút author, publisher, và title. Các nút author, publisher, và title mỗi thứ có các nút văn bản con chứa văn bản.

Bộ mã để đọc tệp XML các sách và hiển thị nội dung sử dụng DOM hiển thị trong Liệt kê 2.

Liệt kê 2. Đọc XML các sách (books) với DOM
  <?php
  $doc = new DOMDocument();
  $doc->load( 'books.xml' );
  
  $books = $doc->getElementsByTagName( "book" );
  foreach( $books as $book )
  {
  $authors = $book->getElementsByTagName( "author" );
  $author = $authors->item(0)->nodeValue;
  
  $publishers = $book->getElementsByTagName( "publisher" );
  $publisher = $publishers->item(0)->nodeValue;
  
  $titles = $book->getElementsByTagName( "title" );
  $title = $titles->item(0)->nodeValue;
  
  echo "$title - $author - $publisher\n";
  }
  ?>

Kịch bản lệnh khởi động bằng cách tạo một đối tượng new DOMdocument và nạp XML các sách vào đối tượng đó thông qua sử dụng phương thức load. Sau đó kịch bản lệnh sử dụng phương thức getElementsByName để nhận một danh mục tất cả các phần tử với tên cho trước.

Trong vòng lặp của các nút book, kịch bản lệnh sử dụng phương thức getElementsByName để nhận nodeValue cho các thẻ author, publisher, và title. nodeValue là văn bản trong nút. Sau đó kịch bản lệnh hiển thị các giá trị đó.

Bạn có thể chạy kịch bản lệnh PHP trên dòng lệnh như thế này:

% php e1.php
PHP Hacks - Jack Herrington - O'Reilly
Podcasting Hacks - Jack Herrington - O'Reilly
%

Như bạn thấy, một dòng được in ra cho mỗi khối sách. Đó là một xuất phát tốt. Tuy nhiên, điều gì xảy ra nếu bạn không có quyền truy cập đến thư viện XML DOM?


Đọc XML thông qua sử dụng bộ phân tích cú pháp SAX

Một cách khác để đọc XML là sử dụng API Đơn giản cho bộ phân tích cú pháp XML (SAX). Hầu hết tất cả các cài đặt của PHP có bộ phân tích cú pháp SAX. Bộ phân tích cú pháp SAX chạy trên mô hình gọi lại. Mỗi khi một thẻ được mở ra hoặc đóng lại, hoặc bất kỳ lúc nào bộ phân tích cú pháp trông thấy một văn bản nào đó, nó thực hiện các cuộc gọi lại đến một số hàm do người dùng định nghĩa với các thông tin nút hoặc văn bản.

Lợi thế của một bộ phân tích cú pháp SAX là ở chỗ nó thực sự nhẹ. Bộ phân tích cú pháp không giữ lại quá lâu bất kỳ cái gì trong bộ nhớ, cho nên nó có thể được dùng cho các tệp cực lớn. Nhược điểm là ở chỗ việc viết các cuộc gọi lại bộ phân tích cú pháp SAX là một điều quá phiền phức. Liệt kê 3 hiển thị bộ mã để đọc tệp XML các sách và hiển thị nội dung thông qua sử dụng SAX.

Liệt kê 3. Đọc XML các sách với bộ phân tích cú pháp SAX
  <?php
  $g_books = array();
  $g_elem = null;
  
  function startElement( $parser, $name, $attrs ) 
  {
  global $g_books, $g_elem;
  if ( $name == 'BOOK' ) $g_books []= array();
  $g_elem = $name;
  }
  
  function endElement( $parser, $name ) 
  {
  global $g_elem;
  $g_elem = null;
  }
  
  function textData( $parser, $text )
  {
  global $g_books, $g_elem;
  if ( $g_elem == 'AUTHOR' ||
  $g_elem == 'PUBLISHER' ||
  $g_elem == 'TITLE' )
  {
  $g_books[ count( $g_books ) - 1 ][ $g_elem ] = $text;
  }
  }
  
  $parser = xml_parser_create();
  
  xml_set_element_handler( $parser, "startElement", "endElement" );
  xml_set_character_data_handler( $parser, "textData" );
  
  $f = fopen( 'books.xml', 'r' );
  
  while( $data = fread( $f, 4096 ) )
  {
  xml_parse( $parser, $data );
  }
  
  xml_parser_free( $parser );
  
  foreach( $g_books as $book )
  {
  echo $book['TITLE']." - ".$book['AUTHOR']." - ";
  echo $book['PUBLISHER']."\n";
  }
  ?>

Kịch bản lệnh khởi động bằng cách thiết lập mảng g_books, giữ lại tất cả các cuốn sách và thông tin của chúng trong bộ nhớ, và biến g_elem, ghi lại tên của thẻ mà kịch bản lệnh hiện đang xử lý. Sau đó kịch bản lệnh định nghĩa các hàm gọi lại. Trong thí dụ này, các hàm gọi lại là startElement, endElement, và textData. Hàm startElementendElement được gọi ra khi các thẻ được mở và đóng tương ứng. Hàm textData được được gọi trên văn bản giữa khởi động và kết thúc các thẻ.

Trong thí dụ này, thẻ startElement tìm kiếm thẻ book để khởi động một phần tử mới trong mảng book. Sau đó, hàm textData xem xét phần tử hiện tại để xem nó là một thẻ publisher, title, hoặc author. Nếu như vậy thì hàm đó đưa văn bản hiện tại vào cuốn sách hiện tại.

Để việc phân tích cú pháp được thực hiện, kịch bản lệnh tạo bộ phân tích cú pháp với hàm xml_parser_create. Tiếp theo, nó thiết đặt các bộ xử lý gọi lại. Sau đó kịch bản lệnh đọc trong tệp và gửi các mảng thông tin của tệp đến bộ phân tích cú pháp. Sau khi tệp được đọc, hàm xml_parser_free xoá bỏ bộ phân tích cú pháp. Phần cuối của kịch bản lệnh xuất ra nội dung của g_books.

Như bạn thấy, đây là bộ mã cho phép viết vững chãi hơn nhiều so với dạng tương đương DOM. Nếu bạn không có thư viện DOM hoặc thư viện SAX thì sao? Có phương án nào khác không?


Phân tích cú pháp XML với các biểu thức chính quy

Chắc chắn tôi bị một số kỹ sư chê vì đề cập tiếp cận này, nhưng bạn có thể phân tích cú pháp XML bằng các biểu thức chính quy. Liệt kê 4 cho thấy một thí dụ của việc sử dụng hàm preg_ để đọc tệp các sách.

Liệt kê 4. Đọc XML các sách với các biểu thức chính quy
  <?php
  $xml = "";
  $f = fopen( 'books.xml', 'r' );
  while( $data = fread( $f, 4096 ) ) { $xml .= $data; }
  fclose( $f );
  
  preg_match_all( "/\<book\>(.*?)\<\/book\>/s", 
  $xml, $bookblocks );
  
  foreach( $bookblocks[1] as $block )
  {
  preg_match_all( "/\<author\>(.*?)\<\/author\>/", 
  $block, $author );
  preg_match_all( "/\<title\>(.*?)\<\/title\>/", 
  $block, $title );
  preg_match_all( "/\<publisher\>(.*?)\<\/publisher\>/", 
  $block, $publisher );
  echo( $title[1][0]." - ".$author[1][0]." - ".
  $publisher[1][0]."\n" );
  }
  ?>

Lưu ý rằng bộ mã đó ngắn như thế nào. Nó khởi động bằng cách đọc tệp vào một chuỗi lớn. Sau đó nó sử dụng một hàm regex (hàm biểu thức chính quy) để đọc trong mỗi mục sách. Cuối cùng, bằng cách sử dụng vòng lặp foreach, kịch bản lệnh lặp mỗi khối sách và chọn ra tác giả, tiêu đề, và nhà xuất bản.

Vậy thì đâu là nhược điểm? Vấn đề đối với việc sử dụng bộ mã biểu thức chính quy để đọc XML là nó không kiểm tra trước để chắc chắn rằng XML đúng ngữ pháp. Có nghĩa là bạn không thể biết là bạn có XML không đúng ngữ pháp trước khi bạn bắt đầu đọc nó. Ngoài ra, một số dạng mẫu hợp lệ của các biểu thức chính quy không thể phù hợp với XML của bạn, do đó bạn sẽ phải sửa đổi chúng sau.

Tôi không bao giờ khuyên sử dụng các biểu thức chính quy để đọc XML, nhưng đôi khi nó là cách thích hợp nhất vì các hàm biểu thức chính quy luôn sẵn có để dùng. Đừng sử dụng biểu thức chính quy để đọc XML từ trực tiếp người dùng; bạn không điều khiển khuôn dạng hoặc cấu trúc của XML đó. Hãy luôn đọc XML từ người dùng bằng cách sử dụng một thư viện DOM hoặc bộ phân tích cú pháp SAX.


Viết XML bằng DOM

Đọc XML chỉ là một phần của phương trình. Viết nó thì sao nhỉ? Cách tốt nhất để viết XML là sử dụng DOM. Liệt kê 5 hiển thị cách DOM xây dựng tệp XML các sách.

Liệt kê 5. Viết XML các sách bằng DOM
  <?php
  $books = array();
  $books [] = array(
  'title' => 'PHP Hacks',
  'author' => 'Jack Herrington',
  'publisher' => "O'Reilly"
  );
  $books [] = array(
  'title' => 'Podcasting Hacks',
  'author' => 'Jack Herrington',
  'publisher' => "O'Reilly"
  );
  
  $doc = new DOMDocument();
  $doc->formatOutput = true;
  
  $r = $doc->createElement( "books" );
  $doc->appendChild( $r );
  
  foreach( $books as $book )
  {
  $b = $doc->createElement( "book" );
  
  $author = $doc->createElement( "author" );
  $author->appendChild(
  $doc->createTextNode( $book['author'] )
  );
  $b->appendChild( $author );
  
  $title = $doc->createElement( "title" );
  $title->appendChild(
  $doc->createTextNode( $book['title'] )
  );
  $b->appendChild( $title );
  
  $publisher = $doc->createElement( "publisher" );
  $publisher->appendChild(
  $doc->createTextNode( $book['publisher'] )
  );
  $b->appendChild( $publisher );
  
  $r->appendChild( $b );
  }
  
  echo $doc->saveXML();
  ?>

Tại phần trên đầu của kịch bản lệnh, mảng books được nạp vào với một số sách mẫu. Các dữ liệu đó có thể từ người dùng hoặc từ một cơ sở dữ liệu.

Sau khi các cuốn sách mẫu được nạp, kịch bản lệnh tạo một new DOMDocument mới và bổ sung nút books gốc vào nó. Rồi kịch bản lệnh tạo một phần tử cho tác giả, tiêu đề, và nhà xuất bản cho mỗi cuốn sách và thêm một nút văn bản vào mỗi nút đó. Bước cuối cùng cho mỗi nút book là gắn nó lại vào nút books gốc.

Đoạn cuối của kịch bản lệnh xuất XML ra bản điều khiển nhờ phương thức saveXML. ((Bạn cũng có thể sử dụng phương thức save (ghi lại) để tạo một tệp từ XML.) Đầu ra của kịch bản lệnh hiển thị trong Liệt kê 6.

Liệt kê 6. Kết quả đầu ra từ kịch bản lệnh xây dựng DOM
  % php e4.php 
  <?xml version="1.0"?>
  <books>
  <book>
  <author>Jack Herrington</author>
  <title>PHP Hacks</title>
  <publisher>O'Reilly</publisher>
  </book>
  <book>
  <author>Jack Herrington</author>
  <title>Podcasting Hacks</title>
  <publisher>O'Reilly</publisher>
  </book>
  </books>
  %

Giá trị thực của việc sử dụng DOM là nó tạo XML luôn đúng ngữ pháp. Nhưng bạn có thể làm gì nếu bạn không có quyền truy cập DOM để tạo XML?


Viết XML bằng PHP

Nếu không có sẵn DOM, bạn có thể sử dụng khuôn mẫu văn bản PHP để viết XML. Liệt kê 7 hiển thị cách PHP xây dựng nên tệp XML các sách.

Liệt kê 7. Viết XML các sách bằng PHP
  <?php
  $books = array();
  $books [] = array(
  'title' => 'PHP Hacks',
  'author' => 'Jack Herrington',
  'publisher' => "O'Reilly"
  );
  $books [] = array(
  'title' => 'Podcasting Hacks',
  'author' => 'Jack Herrington',
  'publisher' => "O'Reilly"
  );
  ?>
  <books>
  <?php
  
  foreach( $books as $book )
  {
  ?>
  <book>
  <title><?php echo( $book['title'] ); ?></title>
  <author><?php echo( $book['author'] ); ?>
  </author>
  <publisher><?php echo( $book['publisher'] ); ?>
  </publisher>
  </book>
  <?php
  }
  ?>
  </books>

Phần trên đầu của kịch bản lệnh tương tự như kịch bản lệnh DOM. Phần cuối của kịch bản lệnh mở ra thẻ books, sau đó lặp lại qua mỗi cuốn, tạo thẻ book và toàn bộ các thẻ title, author, và publisher.

Vấn đề đối với tiếp cận này là việc mã hoá các thực thể. Để chắc chắn rằng thực thể được mã hoá đúng cách, hàm htmlentities phải được gọi trên mỗi mục, như trong Liệt kê 8.

Liệt kê 8. Sử dụng hàm htmlentities để mã hoá các thực thể
  <books>
  <?php
  
  foreach( $books as $book )
  {
  $title = htmlentities( $book['title'], ENT_QUOTES );
  $author = htmlentities( $book['author'], ENT_QUOTES );
  $publisher = htmlentities( $book['publisher'], ENT_QUOTES );
  ?>
  <book>
  <title><?php echo( $title ); ?></title>
  <author><?php echo( $author ); ?> </author>
  <publisher><?php echo( $publisher ); ?>
  </publisher>
  </book>
  <?php
  }
  ?>
  </books>

Đây là lý do nó lại làm phiền đến việc viết XML trong PHP cơ sở. Bạn nghĩ rằng bạn đang tạo XML hoàn hảo, nhưng sau đó bạn nhận thấy là một số phần tử nào đó không được mã hoá đúng cách khi cố gắng chạy nó với dữ liệu thực.


Kết luận

Quanh XML luôn có nhiều cường điệu và lộn xộn. Tuy nhiên, nó không khó khăn như bạn nghĩ -- nhất là trong một ngôn ngữ lớn như PHP. Khi bạn hiểu và thực hiện XML đúng cách, bạn sẽ thấy có nhiều công cụ mạnh mà bạn có thể sử dụng. XPath và XSLT là hai công cụ đáng thử nghiệm.

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

  • Hãy thăm PHP.net để tìm hiểu các tin tức mới nhất về PHP, tìm các tải về, và học tập từ những người dùng khác.
  • Tìm hiểu về bộ Phân tích cú pháp XML Expat, bộ phân tích mà được sử dụng để cung cấp các chức năng bộ phân tích cú pháp SAX dùng cho PHP.
  • Đổi mới dự án phát triển mã nguồn mở tiếp theo của bạn với phần mềm dùng thử của IBM, sẵn có để tải về hoặc trên đĩa DVD.

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=459880
ArticleTitle=Đọc và viết XML DOM với PHP
publish-date=01082010