8

CitibankからOFXファイルをダウンロードしました。このファイルにはhttp://www.ofx.net/DownloadPage/Files/ofx102spec.zip (ファイル OFXBANK.DTD) で定義された DTD があり、OFX ファイルはSGML有効のようです。PHP 5.4.13 のDomDocumentで試しているのですが、いくつか警告が出てファイルが解析されません。私のコードは次のとおりです。

$file = "source/ACCT_013.OFX";
$dtd = "source/ofx102spec/OFXBANK.DTD";
$doc = new DomDocument();
$doc->loadHTMLFile($file);
$doc->schemaValidate($dtd);
$dom->validateOnParse = true;

OFX ファイルは次のように始まります。

OFXHEADER:100
DATA:OFXSGML
VERSION:102
SECURITY:NONE
ENCODING:USASCII
CHARSET:1252
COMPRESSION:NONE
OLDFILEUID:NONE
NEWFILEUID:NONE

<OFX>
<SIGNONMSGSRSV1>
<SONRS>
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<DTSERVER>20130331073401
<LANGUAGE>SPA
</SONRS>
</SIGNONMSGSRSV1>
<BANKMSGSRSV1>
<STMTTRNRS>
<TRNUID>0
<STATUS>
<CODE>0
<SEVERITY>INFO
</STATUS>
<STMTRS>
<CURDEF>COP
<BANKACCTFROM> ...

PHPからの呼び出しのために、サーバー(Centos)に任意のプログラムをインストールして使用することができます。

PD: このクラスhttp://www.phpclasses.org/package/5778-PHP-Parse-and-extract-financial-records-from-OFX-files.htmlは機能しません。

4

3 に答える 3

5

まず第一に、XML が SGML のサブセットであっても、有効な SGML ファイルは整形式の XML ファイルであってはなりません。XML はより厳密であり、SGML が提供するすべての機能を使用するわけではありません。

DOMDocumentXML (および SGML ではない) ベースであるため、これは実際には互換性がありません。

その問題の次に、Ofexfin1.doc の2.2 Open Financial Exchange Headersを参照してください。

Open Financial Exchange ファイルのコンテンツは、単純な一連のヘッダーと、それに続くそのヘッダーによって定義されたコンテンツで構成されます

そしてさらに:

最後のヘッダーの後に空白行が続きます。次に (タイプ OFXSGML の場合)、SGML で読み取り可能なデータは <OFX> タグで始まります。

したがって、最初の空白行を見つけて、そこまですべてを削除します。次に、最初に SGML を XML に変換して、SGML 部分を DOMDocument にロードします。

$source = fopen('file.ofx', 'r');
if (!$source) {
    throw new Exception('Unable to open OFX file.');
}

// skip headers of OFX file
$headers = array();
$charsets = array(
    1252 => 'WINDOWS-1251',
);
while(!feof($source)) {
    $line = trim(fgets($source));
    if ($line === '') {
        break;
    }
    list($header, $value) = explode(':', $line, 2);
    $headers[$header] = $value;
}

$buffer = '';

// dead-cheap SGML to XML conversion
// see as well http://www.hanselman.com/blog/PostprocessingAutoClosedSGMLTagsWithTheSGMLReader.aspx
while(!feof($source)) {

    $line = trim(fgets($source));
    if ($line === '') continue;

    $line = iconv($charsets[$headers['CHARSET']], 'UTF-8', $line);
    if (substr($line, -1, 1) !== '>') {
        list($tag) = explode('>', $line, 2);
        $line .= '</' . substr($tag, 1) . '>';
    }
    $buffer .= $line ."\n";
}

// use DOMDocument with non-standard recover mode
$doc = new DOMDocument();
$doc->recover = true;
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$save = libxml_use_internal_errors(true);
$doc->loadXML($buffer);
libxml_use_internal_errors($save);

echo $doc->saveXML();

次に、このコード例は次の (再フォーマットされた) XML を出力します。これは、DOMDocument がデータを適切にロードしたことも示しています。

<?xml version="1.0"?>
<OFX>
  <SIGNONMSGSRSV1>
    <SONRS>
      <STATUS>
        <CODE>0</CODE>
        <SEVERITY>INFO</SEVERITY>
      </STATUS>
      <DTSERVER>20130331073401</DTSERVER>
      <LANGUAGE>SPA</LANGUAGE>
    </SONRS>
  </SIGNONMSGSRSV1>
  <BANKMSGSRSV1>
    <STMTTRNRS>
      <TRNUID>0</TRNUID>
      <STATUS>
        <CODE>0</CODE>
        <SEVERITY>INFO</SEVERITY>
      </STATUS>
      <STMTRS><CURDEF>COP</CURDEF><BANKACCTFROM> ...</BANKACCTFROM>
</STMTRS>
    </STMTTRNRS>
  </BANKMSGSRSV1>
</OFX>

これがDTDに対して検証できるかどうかはわかりません。多分これはうまくいきます。さらに、SGML が同じ行のタグの値で書かれていない場合 (そして、各行に 1 つの要素のみが必要である場合)、この脆弱な変換は壊れます。

于 2013-04-01T00:58:20.213 に答える