カテゴリの投稿: » 技術情報

libxml2.7.0 – 2.7.2で、実体参照が消える

Written in 2009年08月21日 by | コメントする( 0件 )

libxml2.7.0以降で、実態参照が消失する不具合があり、これを使ってコンパイルされたPHPでは、XMLをパースする際に問題になる。

■実態参照とは

XML文書内で直接記述できない文字や記号(たとえば、 “<” や “>” など)を表記する際に用いられる記述方法です。以下のようなものがある。

  • 「&」 ・・・ 「&amp;」
  • 「< 」・・・ 「&lt;」
  • 「> 」・・・ 「&gt;」
  • 「” 」・・・ 「&quot;」
  • 「’ 」・・・ 「&apos;」

XML内の、<description>タグ、<content>タグなどの中にHTMLを記述する場合、多くの場合以下のような記述を用いる。

<description>
<![CDATA[
<h1>タイトル</h1>
<p>文章がここにはいる</p>
]]>
</description>

このようにしてCDATAセクションを使うことで、実態参照を用いずに記述することが可能である。しかし、CDATAを使わずに実態参照を用いて記述されているXMLも数多く存在する。

<description>
&lt;h1&gt;タイトル&lt;/h1&gt;
&lt;p&gt;文章がここにはいる&lt;/p&gt;
</description>

このようになる。

■実態参照が使われたXMLをパースする際の問題

こういったXML文書をlibxml2.7.0以降を使ったPHP関数でパースすると、実態参照だけが消失し、以下のような見るに堪えない結果となる。

h1タイトル/h1
p文章がここにはいる/p

さくらインターネットのレンタルサーバでは、各種ミドルウェアのバージョンアップが早く、libxml2.7.2などがすでに導入されており、そのサーバを利用している場合、RSSやXMLから情報収集しているプログラムで不具合が生じることになるだろう。

■対処方法

専用サーバであれば、libxmlのバージョンを2.6.30に落とすなどして対処できるが、そうでない場合には別の対処が必要となるだろう。考えられるひとつの方法は、XML内の実態参照をデコードし、CDATAを使った記述に書き換えた上でパースすることだ。以下にPHPでそれを行うための関数を示す。

function insertCDATA( $xml_str )
{
$target_tags = array( ‘description’, ‘content’, ‘content:encoded’ );
foreach ( $target_tags as $tag ) {
$ent = false;
if ( preg_match_all( ‘/<‘.$tag.'[^>]*>(.*?)<\/’.$tag.’>/ims’, $xml_str, $matches ) ) {
if ( is_array( $matches[1] ) && count( $matches[1] ) ) {
foreach ( $matches[1] as $m ) {
if ( !preg_match( ‘/^<!\[CDATA\[/im’, $m ) && preg_match( ‘/&gt;|&lt;|&amp;|&quot;|&apos;/ims’, $m ) ) {
$ent = true;
break;
}
}
}
if ( !$ent ) {
continue;
}
$encode = mb_detect_encoding( $xml_str );
$xml_str = preg_replace( ‘/(<‘.$tag.'[^>]*>)(?!\s*<!\[CDATA\[)(.*)(<\/’.$tag.’>)/Ueims’,
‘”\\1<![CDATA[“.html_entity_decode(“\\2″,ENT_COMPAT,”$encode”).”]]>\\3″‘, $xml_str );
}
}
return $xml_str;
}

function insertCDATA( $xml_str )
{
$target_tags = array( ‘description’, ‘content’, ‘content:encoded’ );
foreach ( $target_tags as $tag ) {
$ent = false;
if ( preg_match_all( ‘/<‘.$tag.'[^>]*>(.*?)<\/’.$tag.’>/ims’, $xml_str, $matches ) ) {
if ( is_array( $matches[1] ) && count( $matches[1] ) ) {
foreach ( $matches[1] as $m ) {
if ( !preg_match( ‘/^<!\[CDATA\[/im’, $m ) &&
preg_match( ‘/&gt;|&lt;|&amp;|&quot;|&apos;/ims’, $m ) ) {
$ent = true;
break;
}
}
}
if ( !$ent ) { continue; }
$encode = mb_detect_encoding( $xml_str );
$xml_str = preg_replace(
‘/(<‘.$tag.'[^>]*>)(?!\s*<!\[CDATA\[)(.*)(<\/’.$tag.’>)/Ueims’,
‘”\\1<![CDATA[“.html_entity_decode(“\\2″,ENT_COMPAT,”$encode”).”]]>\\3″‘, $xml_str );
}
}
return $xml_str;
}

この関数にXML文字列を渡すと、<description>タグ、<content>タグ、<content:enceded>タグを調べて、そこに実態参照があれば、CDATAによる記述に書き換えて返してくれます。

RSSをパースする際に、MagpieRSS(http://magpierss.sourceforge.net/)をご利用の方も多いだろう。このライブラリもlibxml2.7.xの影響を受ける。その場合、rss_fetch.incを開き、上記の関数を書き加えたうえで、_response_to_rss関数のはじめの部分を以下のように書き換えてやるといい。

/*===============================================*\
Function:   _response_to_rss
Purpose:    parse an HTTP response object into an RSS object
Input:      an HTTP response object (see Snoopy)
Output:     parsed RSS object (see rss_parse)
\*===============================================*/

function _response_to_rss ($resp) {
/* ++++++++++++++++++++ Simple Eye changed +++++++++++++++++++ */
//    $rss = new MagpieRSS( insertCDATA($resp->results), MAGPIE_OUTPUT_ENCODING, MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING );
/* ++++++++++++++++++++ Simple Eye changed +++++++++++++++++++ */
$rss = new MagpieRSS( $resp->results, MAGPIE_OUTPUT_ENCODING, MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING );

・・・・・・・・つづく・・・・・・・・・

session.bug_compat_42 と session.bug_compat_warn

Written in 2009年08月20日 by | コメントする( 0件 )

Warning: Unknown(): Your script possibly relies on a session side-effect which existed until PHP 4.2.3. Please be advised that the session extension does not consider global variables as a source of data, unless register_globals is enabled. You can disable this functionality and this warning by setting session.bug_compat_42 or session.bug_compat_warn to off, respectively. in Unknown on line 0
PHP バージョンが 4.2.3 とそれ以前には、たとえ register_globals が無効の場合でもグローバルスコープでセッション変数の初期化を 許してしまうドキュメント化されていない特徴/バグがあります。 この機能を使用している場合で session.bug_compat_warn も有効にしている場合、 PHP 4.3.0 とそれ以降のバージョンでは警告が発されます。 この特徴/バグは、 以下のディレクティブを無効にすることで無効にすることが可能です。
session.bug_compat_42
session.bug_compat_warn
上記の「この機能を使用している場合」に相当しているかどうかを
PHPがどんな条件で判断しているかを6つのケースから考える。
●エラーが出ない[case1.php]
($_SESSIONのキーとグローバル変数が違う場合)
<?php
session_start();
$bbb = $_GET[‘abc’];
$_SESSION[‘aaa’] = $bbb;
?>
●エラーが出ない[case2.php]
($_SESSIONのキーとグローバル変数が同じ場合でも、
外部からの変数を受け取っていない場合)
<?php
session_start();
$aaa = ‘abc’;
$_SESSION[‘aaa’] = $aaa;
?>
●エラーが出る[case3.php]
($_SESSIONのキーとグローバル変数が同じ場合で、かつ、
外部からの変数を受け取っている場合)
<?php
session_start();
$aaa = $_GET[‘abc’];
$_SESSION[‘aaa’] = $aaa;
?>
●エラーが出る[case4.php]
($_SESSIONのキーとグローバル変数が同じ場合で、
その$_SESSIONが外部からグローバル変数とは関係ない外部変数を受け取っている場合)
<?php
session_start();
$aaa = ‘abc’;
$_SESSION[‘aaa’] = $_GET[‘abc’];
?>
●エラーが出る[case5.php]
($_SESSIONのキーとグローバル変数が同じ場合で、
その$_SESSIONが外部からグローバル変数とは関係ない外部変数を
別のグローバル変数を介して受け取っている場合)
<?php
session_start();
$aaa = ‘abc’;
$bbb = $_GET[‘abc’];
$_SESSION[‘aaa’] = $bbb;
?>
●エラーが出ない[case6.php]
($_SESSIONのキーとグローバル変数が同じものがある場合で、
その$_SESSIONとは別の$_SESSIONが外部変数を受け取っている場合)
<?php
session_start();
$aaa = ‘abc’;
$bbb = $_GET[‘abc’];
$_SESSION[‘aaa’] = $aaa;
$_SESSION[‘ccc’] = $bbb;
?>
●結論(エラーが出る条件)
①$_SESSIONのキーと同じ名前のグローバル変数がある
例) $_SESSION[‘aaa’] と $aaa
②その$_SESSIONが間接的、直接的を問わず、何らかの外部変数を読み込んでいる
例1) $_SESSION[‘aaa’] = $_GET[‘abc’];
例2) $bbb = $_GET[‘abc’];
$_SESSION[‘aaa’] = $bbb;
●回避策
php.iniの以下のディレクティブをOffにする
session.bug_compat_42
session.bug_compat_warn

■■ 以下のエラーを検証する ■■

Warning: Unknown(): Your script possibly relies on a session side-effect which existed until PHP 4.2.3. Please be advised that the session extension does not consider global variables as a source of data, unless register_globals is enabled. You can disable this functionality and this warning by setting session.bug_compat_42 or session.bug_compat_warn to off, respectively. in Unknown on line 0

PHP バージョンが 4.2.3 とそれ以前には、たとえ register_globals が無効の場合でもグローバルスコープでセッション変数の初期化を 許してしまうドキュメント化されていない特徴/バグがあります。 この機能を使用している場合で session.bug_compat_warn も有効にしている場合、 PHP 4.3.0 とそれ以降のバージョンでは警告が発されます。 この特徴/バグは、 以下のディレクティブを無効にすることで無効にすることが可能です。

session.bug_compat_42
session.bug_compat_warn

上記の「この機能を使用している場合」に相当しているかどうかを PHPがどんな条件で判断しているかを6つのケースから考える。

●エラーが出ない[case1.php]

($_SESSIONのキーとグローバル変数が違う場合)

<?php
session_start();
$bbb = $_GET[‘abc’];
$_SESSION[‘aaa’] = $bbb;
?>

●エラーが出ない[case2.php]

($_SESSIONのキーとグローバル変数が同じ場合でも、外部からの変数を受け取っていない場合)

<?php
session_start();
$aaa = ‘abc’;
$_SESSION[‘aaa’] = $aaa;
?>

●エラーが出る[case3.php]

($_SESSIONのキーとグローバル変数が同じ場合で、かつ、外部からの変数を受け取っている場合)

<?php
session_start();
$aaa = $_GET[‘abc’];
$_SESSION[‘aaa’] = $aaa;
?>

●エラーが出る[case4.php]

($_SESSIONのキーとグローバル変数が同じ場合で、その$_SESSIONが外部からグローバル変数とは関係ない外部変数を受け取っている場合)

<?php
session_start();
$aaa = ‘abc’;
$_SESSION[‘aaa’] = $_GET[‘abc’];
?>

●エラーが出る[case5.php]

($_SESSIONのキーとグローバル変数が同じ場合で、その$_SESSIONが外部からグローバル変数とは関係ない外部変数を別のグローバル変数を介して受け取っている場合)

<?php
session_start();
$aaa = ‘abc’;
$bbb = $_GET[‘abc’];
$_SESSION[‘aaa’] = $bbb;
?>

●エラーが出ない[case6.php]

($_SESSIONのキーとグローバル変数が同じものがある場合で、その$_SESSIONとは別の$_SESSIONが外部変数を受け取っている場合)

<?php
session_start();
$aaa = ‘abc’;
$bbb = $_GET[‘abc’];
$_SESSION[‘aaa’] = $aaa;
$_SESSION[‘ccc’] = $bbb;
?>

●結論(エラーが出る条件)

①$_SESSIONのキーと同じ名前のグローバル変数がある

例) $_SESSION[‘aaa’] と $aaa

②その$_SESSIONが間接的、直接的を問わず、何らかの外部変数を読み込んでいる

例1) $_SESSION[‘aaa’] = $_GET[‘abc’];

例2)

$bbb = $_GET[‘abc’];
$_SESSION[‘aaa’] = $bbb;

●回避策

php.iniの以下のディレクティブをOffにする

session.bug_compat_42
session.bug_compat_warn

PHPデバッガdbgのインストール

Written in 2009年08月19日 by | コメントする( 0件 )

●dbgのホームページ

http://dd.cron.ru/dbg/

●ソースコード取得

http://dd.cron.ru/dbg/downloads.php

●インストールマニュアル

http://dd.cron.ru/dbg/installation.php

●準備

①PHPをインストールしよう。

②PREFIX/bin/にphpizeが作成されていることを確認する。

③ソースdbg-2.11.32-src.tar.gzをダウンロード

④次のプログラムがインストールされていることを確認。

autoconf 2.13 以降

automake 1.4 以降

libtool 1.3.3 以降

●ソース展開

/usr/local/src/dbg/でソースを展開する

tar xzvf dbg-2.11.32-src.tar.gz

●スクリプトdeferphpizeを調整

deferphpizeの初めの方にあるphpizeへのパスを適切なものにする

cd dbg-2.11.32

vi deferphpize

~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・

#!/bin/sh

phpize=${phpize:-“/usr/local/php/bin/phpize”}

if test -f $phpize; then

$phpize

else

echo “You have to make and install php first. Make sure phpize script is located at $phpize”

exit 1

fi

・・・

~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・~・

●deferphpizeを走らせる

./deferphpize

これにより、dbg.soライブラリがPHPのモジュールサブディレクトリに作成される。

私の場合は、/usr/local/php/lib/php/extensions/no-debug-non/zts-20050922/dbg.so

に作成された。これを/usr/local/php/lib/php/extensions/dbg.soに移動した。

PHP4の場合、ソースディレクトリの/usr/local/src/dbg/dbg-2.11.32/modules/dbg.so

に作成された。

●エラー対処

In file included from /usr/local/src/dbg/dbg-2.11.32/dbg.c:21:

/usr/local/php4-4apache1.3/include/php/main/php_network.h:78:25: openssl/ssl.h: No such file or directory

In file included from /usr/local/src/dbg/dbg-2.11.32/dbg.c:21:

/usr/local/php4-4apache1.3/include/php/main/php_network.h:129: error: syntax error before “SSL”

/usr/local/php4-4apache1.3/include/php/main/php_network.h:129: warning: no semicolon at end of struct or union

/usr/local/php4-4apache1.3/include/php/main/php_network.h:132: error: syntax error before ‘}’ token

/usr/local/php4-4apache1.3/include/php/main/php_network.h:162: error: syntax error before “SSL_METHOD”

make: *** [dbg.lo] エラー 1

このエラーが出たら、openssl-devel パッケージをインストールする必要がある。

yum install openssl-devel

●php.iniの設定

エクステンションへのパスの指定

extension_dir = “/usr/local/php/lib/php/extensions”

利用するエクステンションの指定

extension=dbg.so

dbgのセクション

[debugger]

debugger.enabled=on

debugger.profiler_enabled=on

●apacheの再起動

apacheを再起動させてphpinfo()を表示させて、ZENDのロゴ付近に以下の記述があり、dbgのセクションがあればインストール成功。

This program makes use of the Zend Scripting Language Engine:

Zend Engine v2.1.0, Copyright (c) 1998-2006 Zend Technologies

with DBG v2.11.32, (C) 2000,2005, by Dmitri Dmitrienko