Хабрастатс

в 11:14, , рубрики: dom, php, xpath, Программирование, статистика, метки: , , ,

Хабрастатистика

Собственно после появления довольно интересного и популярного топика Хабракамп товарищ opium создал вопрос, где предложил создать скрипт статистики.

Несмотря на работу, личную жизни и другие факторы, которые затянули создание скрипта, я все-таки его допилил до какой-то кондиции.

Также я столкнулся с проблемами, которые раньше еще не видал.
Полезный опыт, как никак.

Проблема первая

Изначально, увидев топик, я захотел клиентский скрипт, т.е. на языке javascript.
Но тут меня ожидала бритва в виде политики безопасности, поэтому все мои попытки ajax-запросов и iframe успехом не увенчались. Я подсознательно понимал, что на js какая-нибудь бяка, да будет.

Проблема вторая, хитрая

Думаю, ну и ладно, буду писать на PHP (хотя очень хотелось написать на Python, который я изучаю потихоньку, но это бы затянулось надолго). Продумал детали, начал писать код.
Я получил рейтинги комментов первого уровня используя средства DOM. В моей голове вертятся мысли «надо привести их к integer». Понимаю, что есть значения 0, значения с плюсом и минусом. Минус пусть будет, плюс отрезаю, кастую файрболл в integer, смотрю что вышло через print_r. Я знал, что должен встретить рейтинг -34, но его там не было! Я впал в такой ступор, что решил пойти немного прогуляться и развеять закипевшие от удивления мозги.
Долго рассказывать, как я искал баг, скажу сразу: проблема была в знаке минус, который почему-то шел закодированно как-то. Фикс выглядит как костыль; очень надеюсь что есть на хабре люди, которые знают грамотное решение проблемы (попрошу заметить, что на stackoverflow и php manual я ответа не нашел, может искал плохо?):

/**
 * Here is awesome "-"
 * Parsed num is -34
 * var_dump($num); string(5) "–34"
 * TODO: FIX THIS UTF-8 SHIT
 */
if (strlen($int) !== strlen($num)) {
    preg_match('/d+/', $num, $m);
    $int = intval('-' . $m[0]);
}

Немного полезной информации

libxml errors

При загрузке данных в объект DOMDocument у меня плохо парсились entities и естественно я получал E_WARNING. Хорошо, что это не первый опыт работы с DOM, поэтому обернул

libxml_use_internal_errors(true);
$dom->loadHTML('html content');
libxml_clear_errors();

Описывать функции не буду, все отлично указано в документации.
По хорошему надо по новой вызвать libxml_use_internal_errors с указав параметр false, но т.к. больше парсить мне ничего не надо, то я решил опустить этот момент.

DOMXpath

Как можно было догадаться, в бой вступил класс DOMXPath, который упрощает работу по поиску нужных элементов в документе.
Я понимал, что корневой элемент у меня div#comments, поэтому сохранил его для дальнейшего использования. А сам запрос xpath инкапсулировал.
DOMXPath->query возвращает DOMNodeList. Я добавил DOMElement[], чтобы сохранить автокомплит для IDE во время переборал результата в цикле foreach.
Плюс я добавил возможность указания кастомного контекста, который может понадобиться в будущем. Я сейчас думаю, как реализовать подсчет ответов на вопросы (да что скрывать, алгоритм сбора вопросов тоже надо исправить, чтобы рекурсивно прошелся по дереву комментариев), и понимаю, что тут контекст запроса будет как раз кстати.

/**
 * Execute xpath query
 *
 * @param string  $query   XPath query
 * @param DOMNode $context [Optional] Context
 * @return DOMNodeList|DOMElement[]
 */
private function query($query, DOMNode $context = null) {
    if ($context === null) {
        $context = $this->context;
    }

    return $this->xpath->query($query, $context);
}

FINISH HIM

Ссылка на проект:
github.com/miraage/habrastats
Для локальных тестов я сохранил файлик habratopic.htm, чтобы не ждать каждый раз загрузки топика.
Топик по умолчанию — Хабракамп. Можно передать через habrastats.php?id=XXXX

Хабрастатс

Хостить в данный момент демо негде.

The End

Буду рад Вашим комментариям, замечаниям, конструктивной критике и пулл реквестам.

P.S.
Если все-таки решите делать пулл реквест, то делайте его из отдельной feature ветки.
В этой статье довольно понятно описывается работа с ветками в git.

Автор: Miraage


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js