Сравнение XML-документов на предмет изменений в PHP - PullRequest
0 голосов
/ 19 июля 2010

В настоящее время я использую PHP для загрузки нескольких файлов XML из Интернета (нелокальных), используя simplexml_load_file(). Это, как вы можете себе представить, довольно неуклюжий процесс, значительно замедляющий время загрузки (7 секунд для загрузки 7 файлов), и, возможно, может быть больше файлов для загрузки. Эти файлы меняются не часто, но изменения должны отображаться на странице сразу после их внесения.

Одна идея, которая у меня была, заключалась в том, чтобы кэшировать версию каждого канала и вывод html, который я генерирую из этого канала в моей БД. Затем каждый раз, когда пользователь загружает страницу, каналы будут сравниваться; если они отличаются, я бы запустил свой существующий код, сгенерировал HTML, вывел его и сохранил в БД. Однако, если это то же самое, я мог бы просто вывести кэшированный HTML.

Мои две проблемы с этим:

Безопасность: если я храню копию файла XML, может ли это представлять угрозу безопасности, поскольку я не контролирую содержимое этого файла?

Скорость. Основная цель - увеличить скорость загрузки страницы. Будет ли процесс, описанный выше, увеличивать скорость, или он просто перегружает сервер большим количеством дел? Спасибо за вашу помощь!

Ответы [ 2 ]

4 голосов
/ 19 июля 2010

Как насчет того, чтобы задание cron сканировало каждый внешний источник XML, скажем, ежечасно или ежечасно и обновляло его при необходимости?

Это не было бы в 100% реальном времени, но сняло бы нагрузку с вашей веб-страницы - это всегда будет использовать кэшированные файлы. Я не думаю, что существует надежный способ опроса внешних источников обновлений, кроме фактической загрузки файла (теоретически, должно быть возможно получить правильные заголовки кэша, но я бы не стал полагаться на их правильную настройку)

Безопасность: если я храню копию файла XML, может ли это представлять угрозу безопасности, поскольку я не контролирую содержимое этого файла?

Едва ли. Для полной уверенности храните кэшированные XML-файлы вне корневого веб-каталога. Любая остающаяся угроза остается такой же, как если бы вы передавали поток в прямом эфире.

2 голосов
/ 20 июля 2010

У меня была одна идея - кэшировать версию каждого канала и вывод html, который я генерирую из этого канала в моей БД. Затем каждый раз, когда пользователь загружает страницу, каналы будут сравниваться; если они отличаются, я бы запустил свой существующий код, сгенерировал HTML, вывел его и сохранил в БД. Однако, если это то же самое, я мог бы просто вывести кэшированный HTML.

Вместо того, чтобы кэшировать файл XML самостоятельно, вы должны установить поля If-None-Match или If-Modified-Since в заголовке запроса. Таким образом, вы можете проверить, изменились ли файлы без необходимости их загрузки.

Это можно сделать, установив потоковый контекст для libxml перед запуском simplexml_load_file(). Если файл не изменился, вы получите ответ 304 Not Modified, и simplexml_load_file не удастся.

Вы также можете использовать stream_context_get_default для установки общего контекста потока, затем извлечь файл XML в строку с file_get_contents и передать его в simplexml_load_string().

Вот пример первого способа:

Class CachedXml {
    public $element,$url;

    private $mod_date, $etag;

    public function __construct($url){
        $this->url = $url;
        $this->element = NULL;
        $this->mod_date = FALSE;
        $this->etag = FALSE;
    }

    public function updateXml(){
        if($this->mod_date || $this->etag){
            $opts = array(
                'http'=>array(
                'header'=>"If-Modified-Since: $this->mod_date\r\n" .
                          "If-None-Match: $this->etag\r\n"
                )
            );
            $context = stream_context_create($opts);
            libxml_set_streams_context($context);
        }
        if($attempt = @ simplexml_load_file($this->url)){
            $this->element = $attempt;
            $headers = get_headers($this->url,1);
            $this->mod_date = $headers['Last-Modified'];
            $this->etag = $headers['ETag'];
            return TRUE;
        }
        return FALSE;
    }
}

$bob = new CachedXml('http://example.com/xml/test.xml');

if($bob->updateXml()){
    echo "Bob was just updated.<br />";
    echo " Bob's name is " . $bob->element->getName() . ".<br />";
}
else{
    echo "Bob was not updated.<br />";
}
...