Деякі думки про PHP XML/DCOM та Curl


Як використовувати все це добро, багато написано в Мережі, тому зупинюся на практичних моментах "експлуатації" (стаття написана спеціально для dkws.org.ua)

Уявимо, що у нас є сайт, на який потрібно з XML видерти деяку інформацію (припустимо з стрічки новин або ще з чого-небудь). Нехай є шлях самого RSS/XML:

$patha = "https://www.vcerecepti.com/i/%D1%81%D0%B0%D0%BB%D0%B0%D1%82.html?rss=1"

Код буде приблизно таким:

// для нормальної роботи з UTF-8
mb_Internal_Encoding ( 'UTF-8');

// Створюємо новий документ
$xmlDoc = новий DOMDocument();

// Завантажуємо посилання (написано для dkws.org.ua)
$xmlDoc->load($patha);

// Отримуємо елементи з "channel"
$channel=$xmlDoc->getElementsByTagName('channel')->item(0);
$channel_title = $channel->getElementsByTagName('title')
->item(0)->childNodes->item(0)->nodeValue;
$channel_link = $channel->getElementsByTagName('link')
->item(0)->childNodes->item(0)->nodeValue;
$channel_desc = $channel->getElementsByTagName('description')
->item(0)->childNodes->item(0)->nodeValue;

// Виводимо елементи з "channel"

// Загалом, наступний аналіз мало цікавий, оскільки про це і так написано в мережі

Все працює чудово, поки не з'являється підлий амперсанд в URL, наприклад:

$patha = "https://www.vcerecepti.com/i/%D1%81%D0%B0%D0%BB%D0%B0%D1%82.html?rss=1&p=2";

З'явиться помилка EntityRef: expecting ';' in ім'я_RSS

Якщо вам пощастить, то & можна просто замінити на &

Але може бути варіант гірший, коли американці є в самому XML-коді. У цьому випадку їх також потрібно замінити на &. Є два способи. Перший - це отримати XML-код за допомогою file_get_contents() або будь-яким іншим способом, наприклад, за допомогою curl, закачавши на сервері, а потім прочитавши його в змінну. Після того функцією str_replace змінюємо & & та отриманий XML-код передаємо в клас методом loadXML():

$xmlDoc->loadXML($xml);

Але тут виникають проблеми продуктивності. DOMDocument і так не дуже швидкий, а якщо ще й проводити всі ці маніпуляції з курлом та заміною амперсандів, то нетерплячі користувачі можуть не дочекатися виведення сторінки. Тому виникла думка кешування. Створюється окремий сценарій, який запускається, скажімо, раз на добу кроном і проходить по всіх сторінках RSS, тобто:
https://www.vcerecepti.com/i/%D1%81%D0%B0%D0%BB%D0%B0%D1%82.html?rss=1
https://www.vcerecepti.com/i/%D1%81%D0%B0%D0%BB%D0%B0%D1%82.html?rss=1&p=2
https://www.vcerecepti.com/i/%D1%81%D0%B0%D0%BB%D0%B0%D1%82.html?rss=1&p=3
https://www.vcerecepti.com/i/%D1%81%D0%B0%D0%BB%D0%B0%D1%82.html?rss=1&p=4
і т.д.

Кожну сторінку він зберігає у файлі, наприклад, rep1.xml, rep2.xml, rep3.xml і т.д. У цих файлах він замінює амперсанди на потрібну послідовність символів. А наш основний сценарій (Деніс Колнісниченко, dkws. org. ua) працює не з віддаленим RSS/XML, а завантажує методом load() створені раніше "правильні" XML-файли: rep1.xml, rep2.xml і т.д. - Залежно від переданої йому змінної $ p.

По-перше, ми суттєво прискорюємо сценарій, тому що не потрібно завантажувати XML з віддаленого сервера, а ми завантажуватимемо його з локального диска.

По-друге, ми позбавляємося ситуації, коли віддалений сервер "завис", з ним немає зв'язку або сталася ще якась оказія. В цьому випадку наш основний сценарій просто не відкрився б, тому що не зміг би отримати XML. А так XML під боком. Навіть якщо з віддаленим сервером щось і станеться, сценарій просто використовуватиме старий варіант, створений день тому.

Є недолік. Якщо сторінок багато і вони більші можна перевищити максимальний час виконання. У цьому випадку потрібно розбити сценарій на 3-4, кожен з яких отримуватиме свою порцію XML. Наприклад, перший обробляє перші 15 сторінок, другий – наступні 15 сторінок тощо. Всі ці сценарії, щоб не було зайвого навантаження на сервер, потрібно запускати з інтервалом в 5 - 10 хвилин. Також потрібно враховувати звичайний час оновлення ресурсу, з якого здирається XML (створено для dkws.org.ua): якщо він оновлюється о 7-й ранку, то запускати наші сценарії потрібно, починаючи з 7:20, щоб вони отримали не вчорашній XML, а свіжий .

Зразковий код отримання та приводу до почуття XML-коду для однієї сторінки:

// Шлях до XML
$ ch = curl_init ($ patha);
// Відкриваємо файл rep2.xml
$fp = fopen("rep2.xml", "w");

curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);

// Отримуємо та зберігаємо файл
curl_exec($ch);
curl_close($ch);
fclose($fp);

// Заміна у файлі
$file = fopen('rep.xml', 'r');
$text = fread($file, filesize('rep2.xml'));
fclose($file);
$file = fopen('rep.xml', 'w');
fwrite($file, str_replace('&p', '&p', $text));
fclose($file);

На цьому все. Успіхів!