XMLReader — объявление XML разрешено только в начале документа

Я использую встроенный XMLReader в php для чтения данных из внешних каналов XML. Когда я пытаюсь прочитать канал, который начинается с новой строки, я получаю следующую ошибку:

ErrorException: XMLReader::read(): http://example.com/feeds/feed1.xml:2: parser error : XML declaration allowed only at the start of the document

Я думаю, это потому, что подача начинается с новой строки, но я не знаю, как решить проблему? Как я могу заставить его пропустить первую строку, если она содержит новую строку?

Я не могу найти никого, как решил эту проблему. У них есть обходной путь с использованием SimpleXMLElement, но я не могу загрузить весь документ в память.

Вот мой код:

$reader = new XMLReader;
$reader->open($linkToExternalFeed);

while ($reader->read() && $reader->name != 'item');

while ($reader->name == 'item')
{
$node = new SimpleXMLElement($reader->readOuterXML());

$this->doSomeParsing($node);

unset($node);

$reader->next($reader->name);
}

$reader->close();

0

Решение

Вы можете написать streamwrapper, который фильтрует поток. После того, как он найдет первый не пробел, он удалит фильтр и начнет передавать данные в XMLWriter.

class ResourceWrapper {

private $_stream;

private $_filter;

private $context;

public static function createContext(
$stream, callable $filter = NULL, string $protocol = 'myproject-resource'
): array {
self::register($protocol);
return [
$protocol.'://context',
\stream_context_create(
[
$protocol => [
'stream' => $stream,
'filter' => $filter
]
]
)
];
}

private static function register($protocol) {
if (!\in_array($protocol, \stream_get_wrappers(), TRUE)) {
\stream_wrapper_register($protocol, __CLASS__);
}
}

public function removeFilter() {
$this->_filter = NULL;
}

public function url_stat(string $path , int $flags): array {
return [];
}

public function stream_open(
string $path, string $mode, int $options, &$opened_path
): bool {
list($protocol, $id) = \explode('://', $path);
$context = \stream_context_get_options($this->context);
if (
isset($context[$protocol]['stream']) &&
\is_resource($context[$protocol]['stream'])
) {
$this->_stream = $context[$protocol]['stream'];
$this->_filter = $context[$protocol]['filter'];
return TRUE;
}
return FALSE;
}

public function stream_read(int $count) {
if (NULL !== $this->_filter) {
$filter = $this->_filter;
return $filter(\fread($this->_stream, $count), $this);
}
return \fread($this->_stream, $count);
}

public function stream_eof(): bool {
return \feof($this->_stream);
}
}

Использование:

$xml = <<<'XML'


<?xml version="1.0"?>
<person><name>Alice</name></person>
XML;

// open the example XML string as a file stream
$resource = fopen('data://text/plain;base64,'.base64_encode($xml), 'rb');

$reader = new \XMLReader();
// create context for the stream and the filter
list($uri, $context) = \ResourceWrapper::createContext(
$resource,
function($data, \ResourceWrapper $wrapper) {
// check for content after removing leading white space
if (ltrim($data) !== '') {
// found content, remove filter
$wrapper->removeFilter();
// return data without leading whitespace
return ltrim($data);
}
return '';
}
);
libxml_set_streams_context($context);
$reader->open($uri);

while ($foundNode = $reader->read()) {
var_dump($reader->localName);
}

Ouput:

string(6) "person"string(4) "name"string(5) "#text"string(4) "name"string(6) "person"
2

Другие решения

Не идеально, но это будет просто прочитать источник и ltrim() первой части содержимого и записать его во временный файл, вы сможете прочитать файл с именем $tmpFile

$tmpFile = tempnam(".", "trx");
$fpIn = fopen($linkToExternalFeed,"r");
$fpOut = fopen($tmpFile, "w");
$buffer = fread($fpIn, 4096);
fwrite($fpOut, ltrim($buffer));
while ( $buffer = fread($fpIn, 4096))    {
fwrite($fpOut, $buffer);
}
fclose($fpIn);
fclose($fpOut);

я использую tmpname() чтобы сгенерировать уникальное имя файла, вы можете установить для него все, что вам нравится. Также может быть полезно удалить этот файл после его обработки, чтобы сэкономить место и удалить потенциально конфиденциальную информацию.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector