Первое, это то, что вам нужно разобраться, как именно работают push-уведомления.
Второе, ненужно порождать лишние компоненты.
Для PHP тоже имеются решения
https://packagist.org/packages/minishlink/web-push
Для начала вам потребуется понять, что подписки браузеров нужно хранить в отдельной таблице или коллекции в зависимости от вашего основного хранилища.
Для личных сообщений необходимо будет реализовать опциональную, но включенную по умолчанию связь. Следует помнить о том, что один и тот же пользователь может заходить с разных устройств. Например с десктопа, ноута и таблетки. У одного пользователя может быть несколько подписок.
Вам потребуется сервер очередей, например RabbitMQ. У вас будет 2 вида задач - публичные и частные.
Публичная задача - это широковещательная задача, цель которой охватить всех пользователей.
Например, когда публикуется новый материал на сайте и сайт готов принять всех желающий.
Для него потребуется
простая очередь. Задача воркера будет заключаться в том, чтобы найти всех подписчиков, создать задание для каждого из них и поместить это задание во вторую
очередь со множеством воркеров, которые будут непосредственно отправлять пуши.
Первое задание будет содержать id - материала.
Второе будет содержать id материала и id подписки.
Данный подход позволит очереди работать очень быстро. Плюс выборка из базы по id практически мгновенна вне зависимости от движка + отлично кэшируется на уровне самой базы. Доставать из базы нужно только необходимый минимум полей.
Рассылка личных сообщений может быть сделана на основе второй очереди, дополнив задание несколькими полями. Т.е. воркерам будет все равно что они доставляют.
Записывать задание со всеми полями для глобальных публикаций не очень хорошая идея, т.к. это будет занимать много памяти и вызывать тормоза. Плюс такое решение не очень хорошо масштабируется.
Воркеры можно сделать и на Node.js, а можно на PHP. У RabbitMQ есть все необходимое для этого. Можно поискать и другие очереди, однако RabbitMQ достаточно популярное и проверенное решение.