マーク付け言語の構文解析

マーク付け言語の構文解析について。

マーク付け言語の構文解析クラス

クラスの作成経緯

XSLTプロセッサによるXSLT変換の際に、出力メソッドでインデント付けを有効にしたX/HTMLへの出力を行う場合、HTML出力メソッドであれブロック要素とインライン要素の区別を仕様上することはありません。また、出力内容が [XHTML10] の場合にメディアタイプをtext/htmlと指定したとしても、HTML互換性ガイドラインで提示される方法で空要素の空タグを閉じたりもしません。そこで、変換結果のソース整形を行う為に先ずソース解析を行うこのクラスを作成しました。

私が作成した ML Parser は、PHPの拡張ライブラリ・XMLパーサ関数とは異なり、SGML/XML両構造化文書に対応しています。ですから、SGMLベースのHTMLやXMLベースのXHTMLの文書解析も勿論行えます。

MLParserクラス

public class MLParser extends Object

構造

コンストラクタ

public void MLParser([String $encoding])
マーク付け言語の構文解析を行います。
引数
String $encoding
出力のエンコーディング名(省略化)。mbstringモジュールがサポートする文字エンコーディングのみ指定できます。

メソッド

public void setSAXHandler(Object $handler)
文書解析ハンドラのメソッドを設定する。
引数
Object $handler
オブジェクト指向ハンドラ
public void setDocumentHandler(callback $startHandler, callback $endHandler)
文書のハンドラを設定する。
引数
callback $startHandler
開始ハンドラ関数
callback $endHandler
終了ハンドラ関数
public void setDTDHandler(callback $handler)
文書型宣言(DTD)のハンドラを設定する。
引数
callback $handler
ハンドラ関数
public void setPIHandler(callback $handler)
処理命令(PIs)のハンドラを設定する。
引数
callback $handler
ハンドラ関数
public void setCommentHandler(callback $handler)
コメントのハンドラを設定する。
引数
callback $handler
ハンドラ関数
public void setElementHandler(callback $startHandler, callback $endHandler)
要素の開始タグおよび終了タグのハンドラを設定する。
引数
callback $startHandler
開始ハンドラ関数
callback $endHandler
終了ハンドラ関数
public void setCharactersHandler(callback $handler)
文字データのハンドラを設定する。
引数
callback $handler
ハンドラ関数
public void setCDATASectionHandler(callback $handler)
CDATAセクションのハンドラを設定する。
引数
callback $handler
ハンドラ関数
public void setMarkedSectionHandler(callback $handler)
SGMLのマーク区間のハンドラを設定する。
引数
callback $handler
ハンドラ関数
public void setDefaultHandler(callback $handler)
デフォルトのハンドラを設定する。
引数
callback $handler
ハンドラ関数
public void addPreserveSpace(String $name [, ...])
空白文字を保持する要素の設定を追加する(幾つでも指定可)。未設定の場合には、SGMLでは全ての要素で非保持、XMLではxml:space属性を解釈(属性が未宣言及び値が'preserve'ならば保持、'default'ならば非保持)します。
引数
String $name
要素型
public void addEmptyElements(String $name [, ...])
SGMLの空の要素型の設定を追加する(幾つでも指定可)。
引数
String $name
要素型
public Integer getCurrentLine()
現在の行番号を取得する。
返り値
行番号
public Integer getCurrentPosition()
現在の文字位置を取得する。
返り値
文字位置
public String getNamespaceToElement(String $qname)
要素の名前空間を取得する。(これはXML用のメソッドです。名前空間に対応したXML文書でのみ取得可能です。
引数
String $qname
QName
返り値
URI
public String getNamespaceToAttribute(String $qname)
属性の名前空間を取得する。(これはXML用のメソッドです。名前空間に対応したXML文書でのみ取得可能です。
引数
String $qname
QName
返り値
URI
public void parse(String $source [, Integer $markup])
文書の処理を開始する。
引数
String $source
処理する文書データ
Integer $markup
処理する文書データのマーク付け言語の種類 [MLPARSER_PARSE_SGML|MLPARSER_PARSE_XML]

クラスの使用例

次の例は、MLParserクラスを使用したサンプルです。

<?php
ini_set("include_path", "./");
// require_once("org/purl/net/osamurai/markup/MLParser.php"); // for PHP 4
require_once("markup/MLParser.php"); // for PHP 5

$filename = "data.xml";
$level = 0;

function startElement($name, $attrs) {
  global $level;
  for ($i=0; $i<$level; $i++) {
    print "  ";
  }
  print "{$name}\n";
  $level++;
}

function endElement($name) {
  global $level;
  $level--;
}

$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);

$parser = new MLParser;
$parser->setElementHandler('startElement', 'endElement');
$parser->parse($contents, MLPARSER_PARSE_XML);

unset($parser);
?>

上の例では、XML文書の構文を解析し、要素の構造をインデントを付けて表示します。これと同様の処理をHTML等のSGML関連規格でマーク付けされた文書に行う場合には、次のように記述を一部変更(及び追加)すると良い。

...
$parser = new MLParser;
$parser->setElementHandler('startElement', 'endElement');
// HTML 4.01 Strict DTD
$parser->addPreserveSpace("pre");
$parser->addEmptyElements("area", "base", "br", "col", "hr",
                          "img", "input", "link", "meta", "param");
// HTML 4.01 Transitional DTD
//$parser->addEmptyElements("basefont", "isindex");
// HTML 4.01 Frameset DTD
//$parser->addEmptyElements("frame");
$parser->parse($contents, MLPARSER_PARSE_SGML);
...

注釈

  • 以前のPHP4用のソースコードでは、処理時の内部表現は、常に定数'MLPARSER_INTERNAL_ENCODING'の文字エンコーディング名でエンコードされます。後継版のPHP5用では、ソースエンコーディング(出力)及びターゲットエンコーディング(入力)の際にエンコーディングを行う仕組みに改善・修正しました。
  • 構文解析の際にDTDの解析は行いません。また、[XML11] は考慮していません。
  • SGMLの構文解析では、要素の省略化マーク付け(短縮タグ機構)には対応していません。他にも、処理命令 <?experiment> ... <?/experiment> の書式にも対応していません。
  • ご利用の際の注意事項などもご覧下さい。

SAXHandlerインターフェイス

public interface SAXHandler extends Object

構造

メソッド

public void startDocument()
文書のハンドラ(開始)
public void endDocument()
文書のハンドラ(終了)
public void documentTypeDefinition(String $doctypedecl)
文書型宣言のハンドラ
引数
String $doctypedecl
文書型宣言
public void processingInstruction(String $target, String $data)
処理命令のハンドラ
引数
String $target
PIのターゲット
String $data
PIの内容
public void comment(String $data)
注釈のハンドラ
引数
String $data
コメントの内容
public void startElement(String $name, Array $atts)
要素のハンドラ(開始タグ)
引数
String $name
要素型
Array $atts
属性指定 [array([属性名 => 属性値 [, ...]])]
public void endElement(String $name)
要素のハンドラ(終了タグ)
引数
String $name
要素型
public void characters(String $data)
文字列のハンドラ
引数
String $data
文字列
public void cdataSection(String $cdsect)
CDATAセクションのハンドラ
引数
String $cdsect
CDATAセクション
public void markedSection(String $markedsect)
マーク区間のハンドラ
引数
String $markedsect
マーク区間
public void defaultHandler(String $data)
デフォルトのハンドラ
引数
String $data
文字列

サンプル

概要

マーク付け言語の構文解析クラスの処理の際にコールするSAXHandlerインターフェイスを実装したハンドラのサンプルです。取り敢えず、オブジェクト指向ハンドラのサンプルとして、本サイトで「ソース整形(改行と字下げ)」に使用しているハンドラを公開します。

ちなみに、本サイトのソース(XSLT変換結果の出力)の改行と字下げ付けは、処理時間を短縮するためにデフォルトでは無効にしています。ソースを綺麗にして読みたい方は、各種設定(或いは、URIにindent=onというクエリーを付与)にてインデント機能を有効に変更されてください。一応、出力がXHTMLの場合には、媒体型により内容モデルの処理方法が異なります。

構造
ハンドラの使用例

次の例は、ソース整形(改行と字下げ)ハンドラを使用したサンプルです。

<?php
ini_set("include_path", "./");
/*
// for PHP 4
require_once("org/purl/net/osamurai/markup/MLParser.php");
require_once("org/purl/net/osamurai/markup/IndentHandler.php");
*/
// for PHP 5
require_once("markup/MLParser.php");
require_once("markup/IndentHandler.php");

$filename = "data.xml";
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
fclose($handle);

$parser = new MLParser;
$handler = new IndentHandler(&$parser);
$handler->setMinimize(INDENTHANDLER_MINIMIZE_EMPTY_ELEMENTS);
$parser->setSAXHandler($handler);
$parser->parse($contents, MLPARSER_PARSE_XML);

unset($parser, $handler);
?>

注釈

  • サンプルにて利用できるメソッド等については、ファイル中の注釈をご覧下さい。