Open source
Написал для себя — отдаю всем
Написаны для собственных проектов, затем доведены до общего вида. Каждый работает на живом сайте до публикации, а документация живёт здесь, а не в README.
- GPLv3
- форкайте и ставьте клиентам
- Работает в продакшене
- сначала на этом сайте
- 5 репозиториев
- более чем за 19 лет работы
WordPress
WP Bootstrapper
Чистка head, харденинг и защита uploads.
WordPress печатает в head десяток строк, которые нужны примерно никому: генератор с номером версии, RSD, wlwmanifest, shortlink, oEmbed-discovery, стили эмодзи. Gutenberg подключает свой CSS даже там, где блоков нет. Всё это снимается фильтрами — вопрос в том, что каждый раз их приходится вспоминать заново.
Это 48 переключателей в семи вкладках, где каждый — один конкретный хук или константа ядра. Подписи так и написаны: wp_generator, DISABLE_WP_CRON, should_load_separate_core_block_assets, big_image_size_threshold. Видно, что именно отключается, и это можно проверить по документации WordPress.
Голова документа
Генератор версии, RSD, wlwmanifest, shortlink вместе с HTTP-заголовком, ссылки на фиды, стили виджета комментариев, скрипт и стили эмодзи, dns-prefetch на s.w.org.
Гутенберг
Пятнадцать отдельных переключателей: wp-block-library и её тема, глобальные стили, classic-theme-styles, паттерны ядра, SVG-фильтры duotone в шапке, футере и админке, layout support, внутренний контейнер группы.
Защита uploads
В wp-content/uploads пишется управляемый блок .htaccess, запрещающий исполнение php, php3–php8, phtml и phar, и параллельно собирается web.config для IIS — через DOMDocument, а не склейкой строк, поэтому чужие правила в файле переживают запись. Запись атомарная; ежедневный крон и самопроверка возвращают защиту, если файл подменили.
Загрузка SVG
SVG и SVGZ разрешены только пользователю с правом manage_options и проверяются до попадания в uploads: сначала на script и foreignobject, обработчики on* и javascript:, затем разбором через DOMDocument с отключённой сетью. Отдельный плагин для этого не нужен.
Перебор авторов
Запросы ?author=1 и author_name отдают гостям 404, а канонический редирект глушится, чтобы WordPress не подсказал правильный адрес сам.
Стоимость самого плагина
Все настройки лежат в одной опции и читаются один раз за запрос. Зависимостей нет вообще: composer install не нужен, репозиторий разворачивается как есть.
Авторам плагинов
Внутри — собственный атрибут #[Hook]: методы помечаются им прямо в классе, а загрузчик один раз собирает карту хуков через Reflection и компилирует её в статический файл — дальше Reflection не работает, а объект класса создаётся только в момент срабатывания хука. Рядом — fluent Settings API на DTO, которым описаны все семь вкладок. Если пишете свои плагины, форкать имеет смысл именно это.
PHP 8.4+ · WordPress 6.7+ · GPLv3 — kostyasorokin/wp-bootstrapper на GitHub
WooCommerce
Региональный каталог WC
Цены и каталог по городам, отдельный URL на регион.
Один магазин продаёт в нескольких городах, и в каждом свои цены, а иногда и свой ассортимент. Обычный путь — дублировать товары под каждый город или подменять цену скриптом на одном адресе. Первое ломает учёт, второе ломает кеш и поисковую выдачу: у Киева и Львова один URL, и продвигать нечего.
Вместо этого — иерархическая таксономия region, настоящие адреса вида /shop/kyiv/ и отдельная пара цен — обычная и акционная — на каждый регион, хранящаяся в мета-полях самого товара. Товар остаётся один.
Адрес на город
Каждому региону — свой URL через собственные правила перезаписи. Это посадочная страница под региональный запрос, она спокойно живёт за полностраничным кешем, и её можно продвигать отдельно.
Цены по регионам
Обычная и акционная цена задаются для каждого города в карточке товара. Дублей товара нет — значит, не разъезжаются остатки, артикулы и отчёты.
Ассортимент по регионам
Товар показывается там, где он привязан к региону. Фильтрация идёт через таксономию, а не тяжёлым запросом по мета-полям — на больших каталогах это разница в скорости, а не в стиле.
Фид Google Merchant на каждый регион
Отдельный XML-фид на регион: кампании ведутся по городам без ручной сборки фида. Товар без цены для запрошенного региона уходит в фид с обычной ценой — это стоит знать до того, как на фид направлен бюджет.
Переключатель без JavaScript
Выбор города — обычная форма, отправляемая на сервер. Ни строчки фронтенд-скриптов: нечему конфликтовать с темой и нечего ждать до отрисовки.
PHP 8.3+ · WooCommerce · GPLv3 — kostyasorokin/wc-regional-catalog на GitHub
Телеграмма
Telegram Integration
События сайта — в Telegram, и вход на сайт — из него.
Почта — плохой канал для уведомления, на которое никто не отвечает. Она медленная, падает в спам, а на сайте, чьему серверу запрещено слать письма, не приходит вовсе. Telegram мгновенен, уже открыт на телефоне и отвечает ровно на тот вопрос, ради которого уведомление существует.
Половина, которую никто не делает как следует, — обратный путь: вход. Большинство плагинов вставляют копипастный виджет логина. Этот реализует authorization code flow с PKCE и криптографически проверяет токен личности до того, как появится сессия WordPress.
Сессия, которую не украсть на лету
Поток помнит, какой браузер его начал. Callback-ссылку, скопированную из чужой адресной строки, нельзя проиграть заново и получить чужой аккаунт: state-кука своя на каждый поток, и несовпадение обрывает попытку, а не впускает кого-то внутрь.
Две дыры, которые оставляет Telegram
Он не возвращает ни адреса почты, ни надёжного аватара. У нового аккаунта почта спрашивается один раз, а фото профиля забирается только по HTTPS и только с хостов самого Telegram — импорт картинки нельзя превратить в способ заставить сайт ходить по произвольным адресам.
Уведомления — по типам, а не списком
Контент, комментарии, пользователи, медиа, безопасность, обновления и Site Health — у каждого свой переключатель, а WooCommerce добавляет заказы, остатки и события корзины. Первая строка каждого сообщения собирается из настраиваемого владельцем заголовка, а не из зашитого шаблона.
Он говорит, когда сломан
Неудачная доставка сохраняет формулировку самого Telegram, чат назначения и размер сообщения — но никогда не тело: уведомление может нести данные формы. Состояние доставки отдаётся в Site Health, так что замолчавший канал виден сразу, а не замечается со временем.
Спам-фильтр под присмотром
Непрерывная серия заявок Contact Form 7, отклонённых как спам, трактуется как свидетельство о самом фильтре, а не об отправителях. Настоящий спам приходит волнами; фильтр, отклоняющий всё подряд, — нет. Это различие было оплачено на этом самом сайте.
Авторам плагинов
Интеграции с другими плагинами находятся сканированием каталога и кешируются, а не живут в списке, который кто-то должен не забыть поправить. Хелпер кнопки входа возвращает пустую строку, когда плагин не загрузился, вход выключен или посетитель уже авторизован, — шаблон печатает его без единой проверки вокруг.
Инфраструктура
Disable All Email
Один файл, делающий исходящую почту невозможной.
Сайт, который не может доставлять почту, не просто молчит. Боты заполняют его формы, он отправляет подтверждение по электронной почте на указанный ботом адрес, а эти адреса принадлежат реальным людям, которые попадают в папку "спам". За ними накапливаются отказы, и адрес сервера оказывается в черном списке — обычно это замечают, когда хостинг-провайдер пишет, чтобы узнать, что происходит.
Остановка почтового демона этого не предотвращает. Бинарник sendmail на месте, PHP отдаёт письма прямо ему, а тот сам открывает соединение наружу. Демон выключен — почта всё равно уходит.
Два слоя, из которых работает один
Как must-use-плагин файл загружается раньше, чем WordPress объявит собственный wp_mail, — имя занимается первым, и ниже по течению отправить не может никто. Если другой must-use-плагин успел раньше, вместо этого отправитель ядра останавливается фильтром.
Что отвечают вызывающим
Решает константа. Ответ «успех» позволяет формам и оформлению заказов завершаться штатно; ответ «ошибка» — честный вариант, когда другого канала доставки нет, ценой ошибки на экране у каждого посетителя.
Как попасть обратно
Когда плагин падает фатально, WordPress шлёт ссылку восстановления почтой, а у себя хранит только её хеш — рабочая ссылка не существует нигде, кроме того письма. Она пересобирается и записывается туда, где её всё ещё можно прочитать, и передаётся в экшен, чтобы её можно было отправить другим каналом.
На что он не претендует
Ничто в PHP не перехватит код, который сам вызывает функцию mail, открывает собственный SMTP-сокет или стучится в почтовый API голым cURL. Это закрывается на сервере. Плагин закрывает половину WordPress — и прямо говорит, где живёт вторая.
WordPress
KS Projects
Архив портфолио, который переживает тему, в которой его сделали.
Портфолио обычно живёт в теме: там объявлены тип записи, теги и все кейсы. Это работает до замены темы — а тогда вместе с ней уходит и весь архив, чего портфолио делать не должно ни при каких обстоятельствах.
Это тот же архив, но отдельным типом содержимого, с границей, проведённой в определённом месте: плагину принадлежат данные и экраны редактирования, теме — то, как всё это выглядит.
Заготовки, которые тема побеждает именем файла
С плагином едут три простых шаблона, чтобы новый сайт выглядел портфолио, а не лентой блога. Тема перехватывает любой из них, просто имея файл с тем же именем: ничего переключать и отменять не нужно. Классов фреймворков в заготовках нет намеренно — заготовка со своим оформлением выглядела бы неуместно в любой теме, кроме той, откуда она родом.
Что кейсу нужно на самом деле
Два метабокса. Карточка держит короткое имя, подзаголовок и адрес, по которому работа вышла в свет, — заголовок в портфолио обычно целое предложение, а сетке нужно имя. Титры держат то, что просил клиент, кто работал и что сделано, — полями, а не написанным руками HTML, поэтому редизайн не означает правку каждого проекта вручную.
Шаблон под каждый проект
Одному кейсу нужна галерея во всю ширину, другому — длинный текст. Проект может назвать PHP-файл в теме, и тот выводится вместо содержимого записи и только вместо него: заголовок, теги и титры остаются за темой. Выключение шаблона сразу возвращает версию из редактора, а пропавший файл откатывается к ней, а не оставляет пустую страницу.
Настройка, которой намеренно нет
Слаги в адресах менять можно. Имена типа записи и таксономии — нельзя: это те самые строки, под которыми лежит каждая запись в базе, и вынести их в настройки значило бы дать способ потерять архив одним кликом. Задать их по-прежнему можно в wp-config.php — до появления первого проекта и никогда после.
Как он устроен внутри
Классы по PSR-4, и хук объявлен прямо на методе, который его обрабатывает, а не в списке регистраций где-то в стороне: за этим нет ни скомпилированного кеша, ни обхода папок, поэтому один файл говорит, из чего плагин состоит. API, к которому обращается тема, — один класс и один импорт. Зависимостей в рантайме нет вовсе: репозиторий работает сразу после клонирования.
Что прогоняется перед релизом
Статический анализ на самом строгом уровне PHPStan, проверка стандартов кода с записанной причиной у каждого отступления и модульные тесты на то, о чём можно рассуждать отдельно. Две проверки существуют потому, что иначе их поломка молчалива: одна не пропускает поставляемый автозагрузчик, разошедшийся с исходниками, вторая — перевод, который отредактировали, но не скомпилировали.
PHP 8.5+ · WordPress 6.7+ · GPLv3 — kostyasorokin/wp-ks-projects на GitHub
Далее
Забирайте и пользуйтесь
Всё это бесплатно и открыто — ставьте на свои проекты и на клиентские. Ошибки и предложения — в issues на GitHub. Доработка под конкретный сайт или что-то новое для системы, которой здесь ещё нет, — отдельная работа, и о ней стоит написать.