QDBM のユーティリティ API : Cabin

便利そうな関数がいっぱいあったので、その中から cbxmlbreak & cbxmlattrs を使った単機能エクステンションの習作を。
XML (ていうか SGML) のテキストデータを配列に分割する関数です。DOM や正規表現でゴリゴリするよりシンプルな処理に。


普段は CodeGen_PECL は雛形生成だけで、コードは C のソースファイルを直接編集していますが、今回は spec ファイルに全部書いてみました。
まじめに作り込むときは CodeGen_PECL の吐くコードはちょっと不満があるけど、こういったものをサクッと作るには良いですね。

<?xml version="1.0" ?>
<extension name="splitxml" version="0.0.1">
<summary>splitxml</summary>
<license>LGPL</license>
<release>
	<version>0.0.1</version>
	<date>2007-07-11</date>
	<state>devel</state>
	<notes>TEST</notes>
</release>
<deps language="c" platform="all">
	<with name="qdbm" mode="pkg-config">
		<header name="cabin.h"/>
		<lib name="qdbm"/>
	</with>
</deps>
<function role="public" name="splitxml">
	<proto>array splitxml(string xml[, bool remove_comments])</proto>
	<code><![CDATA[
CBLIST *nlist = cbxmlbreak(xml, remove_comments);
int index = 0;
int len = 0;
const char *node;

if (!nlist) {
	php_error(E_WARNING, "cbxmlbreak() failed.");
	zval_dtor(return_value);
	RETURN_FALSE;
}

while (NULL != (node = cblistval(nlist, index, &len))) {
	if (len > 1 && node[0] == '<' && node[1] != '/' && node[1] != '!') {
		CBMAP *amap = cbxmlattrs(node);
		const char *key;
		zval *tmp;

		if (!amap) {
			php_error(E_WARNING, "cbxmlattr() failed.");
			cblistclose(nlist);
			zval_dtor(return_value);
			RETURN_FALSE;
		}

		MAKE_STD_ZVAL(tmp);
		array_init(tmp);

		cbmapiterinit(amap);
		while (NULL != (key = cbmapiternext(amap, &len))) {
			if (len == 0) {
				char *tag = cbsprintf("<%s>", cbmapiterval(key, NULL));
				(void)add_next_index_string(tmp, tag, 1);
				free(tag);
			} else {
				(void)add_assoc_string(tmp, (char *)key, (char *)cbmapiterval(key, NULL), 1);
			}
		}
		cbmapclose(amap);
		(void)add_next_index_zval(return_value, tmp);
	} else {
		(void)add_next_index_string(return_value, (char *)node, 1);
	}
	index++;
}

cblistclose(nlist);
	]]></code>
</function>
</extension>


他にも MIME および CSV 系の関数は PHP にありそうでなかったり、あっても実装が微妙だったりするのでラップする価値ありです。