Портрет 4X_Pro
Был в Сети 11 сент. 2025 г., 23:01
Мультиблог
4X_Pro
Кратко о себе: Web-разработчик. Пишу на PHP, Python, JavaScript. Знаю Ruby и Go, со студенческих времён более-менее помню C и asm. Специализируюсь на ускорении загрузки сайтов и разработке ботов для Telegram. Linuxоид (использую Debian+LXDE). Сторонник IndieWeb, slow lifer.

Социальные сети


Новости сайта в Telegram

t.me/4x_pro

Компьютерное

Особенности работы с HTML в PHP 8.4

4X_Pro

В версиях PHP до 8.4 для очистки выборочной очистки HTML использовался класс DOMDocument, который изначально был предназначен для работы с XML. Однако в версии 8.4 его использование приводит к тому, что возвращается пустая строка. Стал искать решение, и выяснилось, что с этой версии именно для HTML появился новый класс: Dom\HTMLDocument, а для выборки с помощью XPath вместе с ним теперь следует использовать Dom\XPath.
В итоге код для обработки HTML приобретает примерно такой вид:
<?php
function process_html($html) {
    if (
version_compare(PHP_VERSION,'8.4','>=')) { // для PHP 8.4 и выше
      
$dom Dom\HTMLDocument::createFromString($html,LIBXML_HTML_NOIMPLIED);
      
$xpath = new Dom\XPath($dom);
    }
    else { 
// для более старых версий PHP
      
$html mb_encode_numericentity($html, [0x800x10FFFF0, ~0], $charset);      
      
$dom = new \DOMDocument('1.0',$charset);
      
$dom->formatOutput false;
      
$dom->loadHTML($htmlLIBXML_NONET|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD); // LIBXML_NONET — for protection against XXE, LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD — to don't add DOCTYPE and html/body tags
      
$xpath = new DOMXPath($dom);            
    }

// TODO: код обработки

$result=$dom->saveHTML(); // возвращаем результат
if (version_compare(PHP_VERSION,'8.4','<')) $result = \mb_decode_numericentity($result,[0x800x10FFFF0, ~0], $charset);
return 
$result;
}


Ещё одно существенное отличие: Dom\XPath возвращает имена тегов в заглавном регистре, тогда как DOMXPath — в том регистре, в котором теги написаны в HTML-коде, что также может влиять на обработку.

Немного о заголовках в PHP

4X_Pro

Те, кто занимается разработкой на PHP уже давно и не использует frameworks, наверное, не раз сталкивались с необходимостью правильно выводить код HTTP-ответа с правильной версией протокола и текстовым описанием (например, Ok для 200, Not Found для 404 и т.д.).
Раньше для этих целей приходилось писать множество проверок условий вида:
if ($status==200) header($_SERVER['HTTP_PROTOCOL'].' 200 Ok'); elseif ($status==404) header($_SERVER['HTTP_PROTOCOL'].' 404 Not Found'); // elseif обработка других статусов.
Задача эта совершенно рутинная, и возникает вопрос, неужели нельзя это сделать средствами самого языка PHP. Как выяснилось, ещё в версии 5.4 такая возможность появилась — это функция http_response_code, которой достаточно передать числовой код статуса ответа. Например, если вызвать http_response_code(404), то сервер автоматически сгенерирует заголовок HTTP/1.1 404 Not Found (если запрос был по протоколу HTTP 1.1). Также можно вызвать функцию без параметров, чтобы получить код, который был установлен до этого (по умолчанию он равен 200). Так что теперь выдача заголовка сводится к всего одной строчке кода!
Кроме того, в той же версии появилась ещё одна полезная функция: header_register_callback. Эта функция позволяет устанавливать обработчик, который будет вызван перед отправкой HTTP-заголовков клиенту. В нём можно использовать функции headers_list для получения списка подготовленных заголовков и header_remove для удаления тех, которые требуется исключить из отправки.

Красивая транслитерация для URL с помощью AWS Lambda

4X_Pro

Занимаясь SEO, столкнулся с тем, что во многих CMS URLы для страниц генерируются так: название прогоняется через транслитератор, потом небуквенные символы заменяются на _, и на этом все. С учетом того, что при наполнении сайта данные часто вставляются через буфер обмена с лишними пробелами, получаем ужасные адреса вида http://example.com/-ochen-horoshiy--tovar_-_kupite-оbyazatelno_. Глядя на это, я решил, что нужно создать транслитератор, который будет делать красивые URL. Такой транслитератор должен уметь следующее:

  • делать регистр букв всегда нижним
  • удалять все посторонние символы, кроме букв, цифр, тире и прочерков, а пробелы заменять на «-» (в соответствии с рекомендациями Google);
  • обрезать пробелы по краям, а также несколько пробелов, идущих подряд;
  • уметь обрезать URL по первой запятой (это полезно для многих магазинов, где после запятой часто идут второстепенные параметры товара, которые не требуется выносить в URL);
  • уметь обрезать URL по границе слова так, чтобы не превышать указанную длину.
Встроить такое в какую-либо конкретную CMS несложно. Но хотелось сделать какое-то более универсальное решение — API, к которой можно было бы обращаться из любой CMS. И вот недавно я узнал о бессерверных вычислениях и платформе Lambda для Amazon Web Services. Я счел, что она для таких задач подходит идеально и решил попробовать её в деле.
"Подробнее о том, как создавался такой транслитератор и пример интеграции"

Решение проблемы с mysql_connect в PHP 7

4X_Pro

Как известно, в PHP7 окончательно удалили расширение mysql, и теперь функции вида mysql_connect, mysql_query вызывают ошибку "Call to undefined function". Тем не менее, еще встречается код, который использует именно эти функции, и который, зачастую, бывает сложно быстро исправить. Заставить работать такой код на PHP7 достаточно просто — нужно определить эти функции самостоятельно и вызывать в них соответствующие функции из расширения MySQLi. Недавно столкнулся с такой ситуацией и сделал небольшой файл mysql.php, в котором объявил наиболее часто используемые функции.


Подключить его можно следующим образом:


if (version_compare(PHP_VERSION, '7.0.0','>=')) include 'mysql.php';

Абсолютный URL на PHP

4X_Pro

К моему большому удивлению, в PHP нет готовой функции для построения абсолютного URL по относительному и базовому. Пришлось писать ее самому. И вот что получилось: Читать далее…

Как отформатировать сумму и дату на JavaScript

4X_Pro

Недавно потребовалось вывести в Интернет-магазине сумму заказа с десятичными разделителями, причем сделать это с помощью JavaScript. Стал искать, есть ли встроенные средства для этого и обнаружил объект NumberFormat. В простейшем случае форматирование выполняется так: var str = new Intl.NumberFormat().format(value), где value — это число, которое нужно отформатировать. По умолчанию используются свойства системной локали, но можно и указать ее явно: new Intl.NumberFormat('ru-RU'), и хеш с опциями в качестве второго параметра. Напримр, new Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB', currencyDisplay: 'symbol'}) явно указывает, что нужно использовать форматирование, применяемое для денежных сумм, использовать валюту рубль и выводить не "руб.", а ₽. О других опциях можно узнать в справочнике.


Поскольку некоторые старые броузеры с этим объектом работать не умеют, рекомендую выполнять проверку с помощью такой функции:


function formatPrice(value) {
 if (Intl.NumberFormat) return new Intl.NumberFormat('ru-RU',{ style: 'currency', currency: 'RUB', currencyDisplay: 'symbol'}).format(value);
 else return value; // если броузер не поддерживает этот объект, оставляем все как есть, без обработки
}

Для форматирования даты используйте объект Intl.DateTimeFormat c аналогичным методом format.

Получаем курс валют с помощью PHP и SOAP

4X_Pro

Часто при работе с Интернет-магазинами требуется сделать так, чтобы курсы валют обновлялись автоматически. Делается это достаточно просто. На сайте cbr.ru (Центрального Банка РФ) предусмотрено несколько Web-сервисов, работающих по протоколу SOAP и выдающих необходимую информацию. Итак, вот простой код, который получает информацию о курсе евро:



$wsdl = 'http://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx?WSDL'; // указываем адрес WSDL-описания SOAP-сервиса, оттуда PHP возьмет информацию о доступных методах и их параметрах
try {
 $euro_to = 0;
 $cbr = new SoapClient($wsdl,array('soap_version'=> SOAP_1_2, 'exceptions'=>true)); // создаем клиент для подключения к SOAP-сервису
 $date = $cbr->GetLatestDateTime(); // получаем последнюю дату, за которую есть курс
// можно, ко формировать ее вручную, но нужно учитывать, что по выходным и праздникам торги не проводятся, поэтому лучше сделать так
 $result = $cbr->GetCursOnDateXML(array('On_date'=>$date->GetLatestDateTimeResult)); //запрашиваем данные о курсах за указанную дату в формате XML
// к сожалению, возможности запросить курс отдельной валюты не предусмотрено, поэтому придется пройтись циклом по всем полученным
 if ($result->GetCursOnDateXMLResult->any) {  // если в ответе сервера есть XML с курсами валют
   $xml = new SimpleXMLElement($result->GetCursOnDateXMLResult->any); // открываем его как XML-объект
   foreach ($xml->ValuteCursOnDate as $currency) { // и начинаем обходить
    if ($currency->VchCode=='EUR') { // VchCode содержит код валюты, для евро это EUR, для доллара — USD
    $euro_to = floatval($currency->Vcurs); // цена за указанное в Vnom количество валюты
    $euro_from = $currency->Vnom; // для доллара и евро количество равно единице, для других валют может отличаться
// чтобы получить реальный курс за одну единицу валюты, нужно разделить $currency->Vcurs на $currency->Vnom
    }
   }
   if ($euro_to!=0) {
    // здесь можно вставить код, который обновляет данные в магазине
   }
 }
 else echo 'Error!';
}
catch (Exception $e) { // на всякий случай обработчик ошибок
echo 'Error: '.$e->getMessage();
}

HeadJS или полностью асинхронная загрузка сайта

4X_Pro

Один из самых важных показателей для современного сайта — как можно более быстрая загрузка и начало показа содержимого. Чтобы этого достичь, нужно устранить задержки, связанные с ожиданием подгрузки внешних ресурсов, сделав их асинхронными. Прежде я уже писал, как можно сделать асинхронными виджеты социальных сетей. Но недавно я нашел скрипт, который позволяет сделать асинхронной загрузку всех JavaScript и CSS-файлов, используемых на сайте. Называется он HeadJS, и кроме асинхронной загрузки имеет умеет еще делать feature detection, с помощью чего можно загружать скрипты выборочно в зависимости от возможностей устройства, с которого зашел пользователь. Читать далее…


Задать вопрос