{"id":456498,"date":"2025-04-18T15:00:17","date_gmt":"2025-04-18T15:00:17","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=456498"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=456498","title":{"rendered":"<span>\u0428\u0430\u0431\u043b\u043e\u043d \u043d\u0430 Laravel + FrankenPHP<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u0445 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e, \u043c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0414\u0435\u043d\u0438\u0441, \u044f PHP \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a <\/p>\n<p>\u042f \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u044e \u043d\u0430 \u0445\u0430\u043a\u0430\u0442\u043e\u043d\u0430\u0445 \u0437\u0430\u00a0\u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u00ab\u0436\u044b\u0431\u0438\u0439\u0440\u044b\u0440\u00bb\u00a0\u2014 <a href=\"https:\/\/%D0%B6%D1%8B%D0%B1%D0%B8%D0%B9%D1%80%D1%8B%D1%80.%D1%80%D1%84\/\" rel=\"noopener noreferrer nofollow\">https:\/\/\u0436\u044b\u0431\u0438\u0439\u0440\u044b\u0440.\u0440\u0444\/<\/a> \u0438 \u0443\u00a0\u043d\u0430\u0441\u00a0\u0431\u044b\u043b\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u0447\u0442\u043e\u00a0\u043d\u0435\u00a0\u0431\u044b\u043b\u043e \u0433\u043e\u0442\u043e\u0432\u043e\u0433\u043e \u0448\u0430\u0431\u043b\u043e\u043d\u0430, \u0441\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0441\u043f\u043e\u043a\u043e\u0439\u043d\u043e \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u0438 \u0437\u0430\u043d\u043e\u0432\u043e \u043d\u0435\u00a0\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442\u00a0\u0436\u0435 \u043a\u043e\u0434<\/p>\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0430 \u0442\u043e\u043c\u0443, \u043a\u0430\u043a\u00a0\u044f \u043f\u0438\u0441\u0430\u043b \u044d\u0442\u043e\u0442 \u0448\u0430\u0431\u043b\u043e\u043d, \u0441\u00a0\u043a\u0430\u043a\u0438\u043c\u0438 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0438 \u0432\u00a0\u0446\u0435\u043b\u043e\u043c \u0435\u0441\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435\u00a0\u0436\u0435\u043b\u0430\u043d\u0438\u0435, \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u043d\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u043e\u0439<\/p>\n<p>\u0425\u043e\u0447\u0443 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e\u00a0\u044d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0435\u00a0\u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0435 \u0438 \u0431\u0443\u0434\u0443 \u0436\u0434\u0430\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u043a\u0440\u0438\u0442\u0438\u043a\u0438<\/p>\n<blockquote>\n<p><a href=\"https:\/\/github.com\/deniskorbakov\/laravel-frankenphp-template\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0448\u0430\u0431\u043b\u043e\u043d\u0430<\/a><\/p>\n<p><a href=\"https:\/\/github.com\/deniskorbakov\" rel=\"noopener noreferrer nofollow\">\u043c\u043e\u0439 github<\/a><\/p>\n<p>\u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u0434\u043b\u044f \u0441\u0432\u044f\u0437\u0438: <a class=\"mention\" href=\"\/users\/Deniskorbakov\">@Deniskorbakov<\/a><\/p>\n<\/blockquote>\n<p><strong>\u0427\u0442\u043e \u0435\u0441\u0442\u044c \u0432 \u0433\u043e\u0442\u043e\u0432\u043e\u043c \u0448\u0430\u0431\u043b\u043e\u043d\u0435:<\/strong><\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b\u0439 \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u0442\u043e\u0447\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 FrankenPHP<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u043d\u044f\u0442\u043d\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0432 \u0434\u043e\u043a\u0435\u0440\u0435 + multi stage \u043f\u043e\u0434 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0438 \u043f\u0440\u043e\u0434<\/p>\n<\/li>\n<li>\n<p>\u0410\u0434\u043c\u0438\u043d\u043a\u0430<\/p>\n<\/li>\n<li>\n<p>\u0410\u043f\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e &#8212; \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e \u043f\u043e\u0447\u0442\u0435<\/p>\n<\/li>\n<li>\n<p>\u0412\u0435\u0431 \u0441\u043e\u043a\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d pipline &#8212; GitHub Actions<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d FrankenPHP:<\/strong><\/p>\n<p>\u0411\u044b\u043b\u043e \u0436\u0435\u043b\u0430\u043d\u0438\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u0442\u043e\u0447\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u044b\u0431\u043e\u0440 \u043f\u0430\u043b \u043d\u0430 RoadRunner \u0438 FrankenPHP<\/p>\n<p>FrankenPHP \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d \u0432\u043c\u0435\u0441\u0442\u043e RoadRunner, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0435\u0433\u043e \u043b\u0435\u0433\u0447\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0438 \u0432\u043e \u043c\u043d\u043e\u0433\u0438\u0445 \u0431\u0435\u043d\u0447\u043c\u0430\u0440\u043a\u0430\u0445 \u043e\u043d \u043d\u0435 \u0443\u0441\u0442\u0443\u043f\u0430\u0435\u0442 RR<\/p>\n<p><strong>\u0421\u0442\u0435\u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439:<\/strong><\/p>\n<ul>\n<li>\n<p>Laravel 12 <\/p>\n<\/li>\n<li>\n<p>FrankenPHP<\/p>\n<\/li>\n<li>\n<p>Docker\/Docker-compose<\/p>\n<\/li>\n<li>\n<p>Redis<\/p>\n<\/li>\n<li>\n<p>PostgreSQL<\/p>\n<\/li>\n<li>\n<p>Laravel Reverb &#8212; \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>Horizon &#8212; \u043e\u0432\u0431\u0435\u0440\u0442\u043a\u0430 \u0434\u043b\u044f \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>PhpStan\/PhpCodesniffer\/Rector &#8212; \u0441\u0442\u0430\u0442 \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u044b<\/p>\n<\/li>\n<li>\n<p>Filament &#8212; \u0430\u0434\u043c\u0438\u043d\u043a\u0430<\/p>\n<\/li>\n<li>\n<p>Beszel &#8212; \u043b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u044b\u0439 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433<\/p>\n<\/li>\n<li>\n<p>Scribe &#8212; \u0430\u043f\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>Traefik<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0451\u043c \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 + docker:<\/strong><\/p>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Laravel Octane + FrankenPHP<\/p>\n<blockquote>\n<p>Laravel Octane &#8212; \u044d\u0442\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0434\u043b\u044f \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 \u043d\u0430 RR, Swoole, Franken<\/p>\n<p>\u0442\u0430\u043a \u0436\u0435 \u0443 laravel Octane \u0445\u043e\u0440\u043e\u0448\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0443 + \u0441\u043a\u0430\u0437\u0430\u043d\u043e \u043a\u0430\u043a \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u044d\u0442\u043e \u0432\u0441\u0451 \u0434\u0435\u043b\u043e \u043d\u0430 \u043f\u0440\u043e\u0434\u0435<\/p>\n<\/blockquote>\n<p>\u041f\u043e\u043a\u0430\u0436\u0443 Dockerfile \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f<\/p>\n<pre><code class=\"yaml\">FROM dunglas\/frankenphp:1.4 AS base  RUN apt-get update \\     &amp;&amp; DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \\     git \\     unzip \\     librabbitmq-dev \\     libpq-dev \\     supervisor  RUN install-php-extensions \\     gd \\     pcntl \\     opcache \\     pdo \\     pdo_pgsql \\     pgsql \\     redis \\     zip  WORKDIR \/app  COPY --from=composer:2.8 \/usr\/bin\/composer \/usr\/local\/bin\/composer  COPY --from=node:23 \/usr\/local\/lib\/node_modules \/usr\/local\/lib\/node_modules COPY --from=node:23 \/usr\/local\/bin\/node \/usr\/local\/bin\/node  RUN ln -s \/usr\/local\/lib\/node_modules\/npm\/bin\/npm-cli.js \/usr\/local\/bin\/npm  FROM base AS dev  COPY .\/.docker\/supervisor\/supervisord.dev.conf \/etc\/supervisor\/conf.d\/supervisord.conf  CMD [\"\/usr\/bin\/supervisord\", \"-n\", \"-c\", \"\/etc\/supervisor\/conf.d\/supervisord.conf\"]  FROM base AS prod  COPY .\/.docker\/supervisor\/supervisord.prod.conf \/etc\/supervisor\/conf.d\/supervisord.conf  CMD [\"\/usr\/bin\/supervisord\", \"-n\", \"-c\", \"\/etc\/supervisor\/conf.d\/supervisord.conf\"]<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u0434\u043e\u043a\u0435\u0440 \u0444\u0430\u0439\u043b \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439, \u0438\u0437 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u0447\u0442\u043e \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0442\u044c \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f multi-stage \u0434\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0430<\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439, \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u0430, \u043a\u0440\u043e\u043d \u0437\u0430\u0434\u0430\u0447 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 supervisor<\/p>\n<p><strong>\u041a\u043e\u043d\u0444\u0438\u0433:<\/strong><\/p>\n<pre><code class=\"bash\">[supervisord] user=root nodaemon=true logfile=\/dev\/stdout logfile_maxbytes=0 pidfile=\/var\/run\/supervisord.pid  [program:octane] command=php \/app\/artisan octane:frankenphp --watch autostart=true autorestart=true stdout_events_enabled=true stderr_events_enabled=true stdout_logfile=\/dev\/stdout stdout_logfile_maxbytes=0 stderr_logfile=\/dev\/stderr stderr_logfile_maxbytes=0  [program:horizon] command=php \/app\/artisan horizon autostart=true autorestart=true stdout_events_enabled=true stderr_events_enabled=true stdout_logfile=\/dev\/stdout stdout_logfile_maxbytes=0 stderr_logfile=\/dev\/stderr stderr_logfile_maxbytes=0  [program:schedule] command=php \/app\/artisan schedule:run autostart=true autorestart=true stdout_events_enabled=true stderr_events_enabled=true stdout_logfile=\/dev\/stdout stdout_logfile_maxbytes=0 stderr_logfile=\/dev\/stderr stderr_logfile_maxbytes=0  [program:reverb] command=php \/app\/artisan reverb:start autostart=true autorestart=true stdout_events_enabled=true stderr_events_enabled=true stdout_logfile=\/dev\/stdout stdout_logfile_maxbytes=0 stderr_logfile=\/dev\/stderr stderr_logfile_maxbytes=0 <\/code><\/pre>\n<p>\u0422\u0430\u043a \u0436\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0441\u0442\u043e\u0438\u0442 traefik, \u043e\u043d \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u043a\u0430\u043a \u0440\u0435\u0432\u0435\u0440\u0441 \u043f\u0440\u043e\u043a\u0441\u0438 &#8212; \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044e \u043f\u043e \u043f\u043e\u0440\u0442\u0430\u043c<\/p>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u0440:<\/strong><\/p>\n<pre><code class=\"yaml\">  traefik:     image: traefik:v2.10     container_name: traefik.${APP_NAMESPACE}     command:       - --api.insecure=true       - --providers.docker=true       - --entrypoints.web.address=:80     ports:       - \"80:80\"       - \"8080:8080\"     volumes:       - \/var\/run\/docker.sock:\/var\/run\/docker.sock     networks:       - app   php:     build:       context: .       dockerfile: .docker\/php\/Dockerfile       target: dev     volumes:       - .:\/app     labels:       - \"traefik.enable=true\"       - \"traefik.http.routers.${APP_NAMESPACE}.rule=Host(`${APP_HOST:-localhost}`)\"       - \"traefik.http.services${APP_NAMESPACE}.loadbalancer.server.port=${APP_PORT:-8000}\"<\/code><\/pre>\n<p><strong>\u0425\u043e\u0447\u0443 \u043f\u043e\u0434\u043c\u0435\u0442\u0438\u0442\u044c \u0434\u0432\u0430 \u0433\u043b\u0430\u0432\u043d\u044b\u0445 \u043d\u044e\u0430\u043d\u0441\u0430:<\/strong><\/p>\n<ul>\n<li>\n<p>Hot reload \u0440\u0435\u0436\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>Alpine \u043e\u0431\u0440\u0430\u0437\u044b<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041f\u0440\u043e Hot reload:<\/strong><\/p>\n<p>Franken \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0434\u0432\u0443\u0445 \u0440\u0435\u0436\u0438\u043c\u0430\u0445<\/p>\n<p>1) Prod \u0440\u0435\u0436\u0438\u043c &#8212; \u043a\u043e\u0433\u0434\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438 \u043d\u0430\u0434\u043e \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440, \u0447\u0442\u043e\u0431 \u043e\u043d \u0438\u0437\u043c\u0435\u043d\u0438\u043b \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 (\u043f\u0440\u043e\u0441\u0442\u0430\u044f \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432)<\/p>\n<p>2)  Dev \u0440\u0435\u0436\u0438\u043c &#8212; \u043a\u043e\u0433\u0434\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u0434\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u0441\u0430\u043c \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 (\u0434\u0430\u043d\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043c\u0435\u0434\u043b\u0435\u043d\u0435\u0435 \u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438)<\/p>\n<p>\u041f\u0440\u0438 Dev \u0440\u0435\u0436\u0438\u043c\u0435 \u043d\u0430\u0434\u043e \u0447\u0442\u043e\u0431 \u0443 \u0432\u0430\u0441 \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430 Node js \u0442\u0430\u043a \u043a\u0430\u043a \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u043e\u0432 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 js \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <\/p>\n<p><strong>\u041f\u0440\u043e alpine \u043e\u0431\u0440\u0430\u0437\u044b:<\/strong><\/p>\n<p>\u0412 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0447\u0435\u0442\u043a\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0447\u0435\u043c\u0443 \u0438\u0445 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c:<\/p>\n<blockquote>\n<p>\u0421\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c, \u0430 \u0442\u0430\u043a\u0436\u0435 Alpine Linux-\u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 Docker-\u043e\u0431\u0440\u0430\u0437\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 <a href=\"https:\/\/musl.libc.org\" rel=\"noopener noreferrer nofollow\">\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 musl libc<\/a>.<\/p>\n<p>\u0418\u0437\u0432\u0435\u0441\u0442\u043d\u043e, \u0447\u0442\u043e PHP <a href=\"https:\/\/gitlab.alpinelinux.org\/alpine\/aports\/-\/issues\/14381\" rel=\"noopener noreferrer nofollow\">\u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442<\/a> \u0441 \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439 \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u043e\u0439 GNU libc, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u0440\u0438 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 \u0432 ZTS \u0440\u0435\u0436\u0438\u043c\u0435 (\u043f\u043e\u0442\u043e\u043a\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f FrankenPHP.<\/p>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, <a href=\"https:\/\/github.com\/php\/php-src\/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl\" rel=\"noopener noreferrer nofollow\">\u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u0440\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 musl<\/a>.<\/p>\n<\/blockquote>\n<p><strong>\u0410\u0434\u043c\u0438\u043d\u043a\u0430:<\/strong><\/p>\n<p>\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Filament \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432, \u0442\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0435\u0435 \u043b\u0435\u0433\u043a\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f, \u043a\u0440\u0430\u0441\u0438\u0432\u0430\u044f \u043e\u0431\u043e\u043b\u043e\u0447\u043a\u0430 \u0438 \u0441 \u0435\u0435 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043a\u0440\u0430\u0441\u0438\u0432\u043e \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0430\u0434\u043c\u0438\u043d\u043a\u0443<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0435\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445, \u0442\u043e \u043e\u043d\u0430 \u043e\u0441\u043e\u0431\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d\u0430 \u0441\u0438\u043b\u044c\u043d\u043e \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u0442<\/p>\n<p><strong>\u0410\u043f\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f:<\/strong><\/p>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u0432\u0430\u0436\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u043d\u0430\u0441, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0447\u0430\u0441\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u0440\u0443\u0447\u043a\u0438 \u043a\u0435\u0439\u0441\u043e\u0434\u0430\u0442\u0435\u043b\u044f\u043c \u0438 \u0442\u0430\u043a\u0436\u0435 \u043e\u0442\u0433\u043e\u0440\u043e\u0434\u0438\u0442\u044c \u0441\u0435\u0431\u044f \u043e\u0442 \u043b\u0438\u0448\u043d\u0438\u0445 \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0435\u0440\u043e\u0432, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0432 \u0442\u043e\u043c \u0438\u043b\u0438 \u0438\u043d\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435<\/p>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u043b\u0443\u0447\u0448\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f laravel, \u044d\u0442\u043e &#8212; <a href=\"https:\/\/scribe.knuckles.wtf\/laravel\/\" rel=\"noopener noreferrer nofollow\">https:\/\/scribe.knuckles.wtf\/laravel\/<\/a><\/p>\n<p><strong>\u041f\u043b\u044e\u0441\u044b \u0434\u0430\u043d\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0440\u0443\u0447\u0435\u043a<\/p>\n<\/li>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f query params<\/p>\n<\/li>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f url params<\/p>\n<\/li>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f body params<\/p>\n<\/li>\n<li>\n<p>\u0422\u043e\u043d\u043a\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0430<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u044f \u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0443\u043a\u0430\u0437\u044b\u0442\u044c \u044f\u0432\u043d\u043e \u0442\u0435 \u0438\u043b\u0438 \u0438\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 <\/p>\n<\/li>\n<\/ul>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 Laravel Request \u0438 Resource  &#8212; \u0442\u043e \u0435\u0441\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b laravel<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u0435 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e DTO \u043e\u0442 <a href=\"https:\/\/spatie.be\/docs\/laravel-data\/v4\/introduction\" rel=\"noopener noreferrer nofollow\">https:\/\/spatie.be\/docs\/laravel-data\/v4\/introduction<\/a><\/p>\n<p>\u0422\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0440\u0443\u0447\u043a\u0430\u043c\u0438 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/p>\n<p><strong>\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433:<\/strong><\/p>\n<p>\u0412 \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442, \u044f \u0445\u043e\u0442\u0435\u043b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0439 \u0441\u0442\u0435\u043a &#8212; grafana, loki, prometeus, promtail<br \/>\u041d\u043e \u043f\u043e\u0442\u043e\u043c \u043f\u043e\u043d\u044f\u043b \u0447\u0442\u043e \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0443\u0441\u0442\u0430\u044f \u0442\u0440\u0430\u0442\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439, \u043d\u043e \u0437\u0430\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 <a href=\"https:\/\/github.com\/henrygd\/beszel\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/henrygd\/beszel<\/a><\/p>\n<p>\u0414\u043b\u044f \u043c\u0435\u043d\u044f \u043e\u043d \u043f\u043e\u0434\u043e\u0448\u0435\u043b \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e &#8212; \u0442\u0430\u043a \u043a\u0430\u043a \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0438 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/p>\n<p><strong>\u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f:<\/strong><\/p>\n<p>\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0438 \u0433\u043e\u043b\u043e\u0432\u043d\u0430\u044f \u0431\u043e\u043b\u044c, \u0447\u0442\u043e \u043c\u044b \u0441 \u043a\u0430\u0436\u0434\u044b\u043c \u0445\u0430\u043a\u0430\u0442\u043e\u043d\u043e\u043c \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0438\u0441\u0430\u043b\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u043b\u0438 \u0441 \u0444\u0440\u043e\u043d\u0442\u043e\u043c<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0431\u044b\u043b\u043e \u043f\u0440\u0438\u043d\u044f\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441\u0440\u0430\u0437\u0443 \u0432 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e + \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0435\u0441\u0442\u044c \u0440\u043e\u043b\u0435\u0432\u043a\u0430 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0440\u043e\u043b\u044c &#8212; \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u0442\u043e\u043b\u044c\u043a\u043e \u043a \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c, \u043a\u0430\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0434\u0430\u0448\u0431\u043e\u0440\u0434 Horizon<\/p>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u0440 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438:<\/strong><\/p>\n<pre><code class=\"php\">&lt;?php  declare(strict_types=1);  namespace App\\Services\\Controllers;  use Illuminate\\Validation\\ValidationException; use Illuminate\\Support\\Facades\\Hash; use App\\DTO\\User\\UserAuthShowDTO; use App\\DTO\\Auth\\AuthRegisterDTO; use App\\DTO\\Auth\\AuthLoginDTO; use App\\Models\\User;  final class AuthService {     \/** @return array&lt;string, mixed&gt; *\/     public function register(AuthRegisterDTO $authRegisterDTO): array     {         $user = User::query()-&gt;create([             'name'     =&gt; $authRegisterDTO-&gt;name,             'role'     =&gt; $authRegisterDTO-&gt;role,             'email'    =&gt; $authRegisterDTO-&gt;email,             'password' =&gt; Hash::make($authRegisterDTO-&gt;password),         ]);          return UserAuthShowDTO::from($user)-&gt;toArray();     }      \/**      * @return array&lt;string, mixed&gt;      * @throws ValidationException      *\/     public function login(AuthLoginDTO $authLoginDTO): array     {         $user = User::query()-&gt;where('email', $authLoginDTO-&gt;email)-&gt;firstOrFail();          if (! Hash::check($authLoginDTO-&gt;password, $user-&gt;password)) {             throw ValidationException::withMessages(['bad credentials']);         }          return UserAuthShowDTO::from($user)-&gt;toArray();     } }<\/code><\/pre>\n<p><strong>\u0412\u0435\u0431\u0441\u043e\u043a\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440:<\/strong><\/p>\n<p>\u041d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u043c \u0445\u0430\u043a\u0430\u0442\u043e\u043d\u0435 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u044b \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0437\u0430\u0431\u0435\u0433\u0430 \u0432 real time<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0430\u043a\u0436\u0435 \u0440\u0435\u0448\u0438\u043b \u0435\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0448\u0430\u0431\u043b\u043e\u043d, \u0447\u0442\u043e\u0431 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u043d\u0430 \u044d\u0442\u043e \u043d\u0435 \u0442\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f<\/p>\n<p><strong>\u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u044d\u0442\u0430\u043f \u0434\u043b\u044f \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441\u0442\u043e\u0438\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c:<\/strong><\/p>\n<ul>\n<li>\n<p>Centrifugo<\/p>\n<\/li>\n<li>\n<p>Larvel reverb<\/p>\n<\/li>\n<\/ul>\n<p>\u0421\u0430\u043c\u043e\u0435 \u043b\u0443\u0447\u0448\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0430 \u043f\u0445\u043f \u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u043e\u0432 &#8212; \u044d\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Centrifugo, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0438 \u0445\u043e\u0440\u043e\u0448\u043e \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442 \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432<\/p>\n<p>\u041d\u043e \u0443 \u043c\u0435\u043d\u044f \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u043d\u0430 \u0445\u0430\u043a\u0430\u0442\u043e\u043d\u0435 \u0431\u044b\u0441\u0442\u0440\u043e \u0435\u0433\u043e \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0438 \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0441 Laravel<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u0440\u0435\u0448\u0438\u043b \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0432 \u0441\u0431\u043e\u0440\u043a\u0443 Laravel Reverb, \u043d\u0435\u043f\u043b\u043e\u0445\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 &#8212; \u0442\u0430\u043a \u0436\u0435 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c\u0441\u044f \u0431\u043b\u0430\u0433\u043e\u0434\u0440\u044f \u043e\u0447\u0435\u0440\u0434\u044f\u043c, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u043e\u0432<\/p>\n<p><strong>GitHub Actions:<\/strong><\/p>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0439\u043f\u043b\u0430\u043d\u043e\u0432 \u0431\u044b\u043b\u0430 \u0441\u0430\u043c\u043e\u0439 \u043b\u0435\u0433\u043a\u043e\u0439, \u0442\u0430\u043a \u043a\u0430\u043a \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 &#8212; \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u043a\u0430\u0436\u0443, \u0447\u0442\u043e \u0443 \u043c\u0435\u043d\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c<\/p>\n<pre><code class=\"yaml\">name: DEPLOY AND BUILD on:   push:     branches: [\"main\"]   pull_request:     branches: [\"main\"]  jobs:   coding-standard:     name: Coding Standard     runs-on: ubuntu-latest     steps:       - uses: actions\/checkout@v2        - name: Setup PHP         uses: shivammathur\/setup-php@v2         with:           php-version: '8.4'           coverage: none        - name: Get composer cache directory         id: composer-cache         run: |           echo \"dir=$(composer config cache-files-dir)\" &gt;&gt; $GITHUB_OUTPUT        - name: Cache composer dependencies         uses: actions\/cache@v4         with:           path: ${{ steps.composer-cache.outputs.dir }}           key: ${{ runner.os }}-composer-${{ hashFiles('**\/composer.lock') }}           restore-keys: |             ${{ runner.os }}-composer-        - name: Install dependencies         run: composer install --no-progress --no-suggest --prefer-dist --no-interaction --ignore-platform-reqs        - name: Check coding style         run: composer cs-check        - name: Check code rector         run: composer cs-rector        - name: Perform a static analysis of the code base         run: .\/vendor\/bin\/phpstan analyse --memory-limit=2G        - name: Test         run: php artisan test   deploy:     runs-on: [ ubuntu-latest ]     environment: deniskorbakov     needs: coding-standard     if: github.ref == 'refs\/heads\/main'     steps:       - uses: actions\/checkout@v4.2.2       - name: Push to server         uses: appleboy\/ssh-action@master         with:           host: ${{ secrets.SERVER_IP }}           username: ${{ secrets.SERVER_USERNAME }}           password: ${{ secrets.SERVER_PASSWORD }}           script: |             cd ${{ secrets.PROJECT_PATH }}             make update-project <\/code><\/pre>\n<p>\u0412 \u043e\u0434\u043d\u043e\u0439 \u0434\u0436\u043e\u0431\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0442\u044c\u0441\u044f \u043a\u043e\u0434 \u0441\u0442\u0430\u0442 \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u0430\u043c\u0438 \u0438 \u0442\u0430\u043a\u0436\u0435 \u0434\u0435\u043f\u043b\u043e\u0438\u0442\u044c\u0441\u044f \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0435\u0441\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0441\u043b\u0438\u0442\u044b \u0432 \u043c\u0435\u0439\u043d \u0432\u0435\u0442\u043a\u0443<\/p>\n<p>\u042f \u0440\u0435\u0448\u0438\u043b \u0432\u0441\u0435 \u043d\u0443\u0436\u043d\u044b\u0435 \u043c\u043d\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u0432 makefile, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u043f\u0438\u0441\u0430\u0442\u044c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0434\u043b\u0438\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0444\u0438\u0433, \u0434\u0430 \u0438 \u0432 \u0446\u0435\u043b\u043e\u043c \u043d\u0435\u043f\u043b\u043e\u0445\u0430\u044f \u0438\u0434\u0435\u044f \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u043d\u0443\u0436\u043d\u044b\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u0435\u043f\u043b\u043e\u0435\u043c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u0443\u044e \u043f\u0440\u043e\u0441\u043b\u043e\u0439\u043a\u0443<\/p>\n<p><strong>Makefile:<\/strong><\/p>\n<pre><code class=\"bash\">include .env  # \u043d\u0430\u0431\u043e\u0440 \u043a\u043e\u043c\u0430\u043d\u0434 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435 update-project: pull composer-install db-migrate build-front rm-images build-prod doc-generate restart  # \u043d\u0430\u0431\u043e\u0440 \u043a\u043e\u043c\u0430\u043d\u0434 \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e init: build composer-install build-front key-generate storage-link db-migrate seed doc-generate restart build-wait  # \u043d\u0430\u0431\u043e\u0440 \u043a\u043e\u043c\u0430\u043d\u0434 \u0434\u043b\u044f \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u043d\u0430 \u043f\u0440\u043e\u0434\u0435 init-prod: build-prod composer-install build-front key-generate storage-link db-migrate seed doc-generate restart build-prod  build: @echo \"Building containers\" @docker compose --env-file .env up -d --build build-wait: @echo \"Building containers\" @docker compose --env-file .env up -d --build --wait up: @echo \"Starting containers\" @docker compose --env-file .env up -d --remove-orphans build-prod: @echo \"Building containers\" @docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env up -d --wait --build up-prod: @echo \"Starting containers\" @docker compose -f docker-compose.yml -f docker-compose.prod.yml --env-file .env up -d --wait --remove-orphans shell: @docker exec -it $$(docker ps -q -f name=php.${APP_NAMESPACE}) \/bin\/bash code-check: @echo \"Perform a static analysis of the code base\" @DOCKER_CLI_HINTS=false docker exec -it $$(docker ps -q -f name=php.${APP_NAMESPACE}) vendor\/bin\/phpstan analyse --memory-limit=2G @echo \"Perform a code rector\" @DOCKER_CLI_HINTS=false docker exec -it $$(docker ps -q -f name=php.${APP_NAMESPACE}) composer cs-rector @echo \"Perform a code style check\" @DOCKER_CLI_HINTS=false docker exec -it $$(docker ps -q -f name=php.${APP_NAMESPACE}) composer cs-check rector-fix: @echo \"Fix code with rector\" @DOCKER_CLI_HINTS=false docker exec -it $$(docker ps -q -f name=php.${APP_NAMESPACE}) composer cs-rector-fix code-baseline: @echo \"Perform phpstan generate-baseline\" @DOCKER_CLI_HINTS=false docker exec -it $$(docker ps -q -f name=php.${APP_NAMESPACE}) vendor\/bin\/phpstan analyse --generate-baseline --memory-limit=2G composer-install: @echo \"Running composer install\" @docker exec -i $$(docker ps -q -f name=php.${APP_NAMESPACE}) composer install --ignore-platform-reqs db-migrate: @echo \"Running database migrations\" @docker exec -i $$(docker ps -q -f name=php.${APP_NAMESPACE}) php artisan migrate --force build-front: @echo \"Building admin frontend for production\" @docker exec -i $$(docker ps -q -f name=php.${APP_NAMESPACE}) npm i @docker exec -i $$(docker ps -q -f name=php.${APP_NAMESPACE}) npm run build pull: @echo \"Updating project from git and rebuild\" @git pull rm-images: @echo \"Delete extra images\" @docker system prune -f key-generate: @echo \"Key generate\" @docker exec -i $$(docker ps -q -f name=php.${APP_NAMESPACE}) php artisan key:generate storage-link: @echo \"Storage Link\" @docker exec -i $$(docker ps -q -f name=php.${APP_NAMESPACE}) php artisan storage:link seed: @echo \"Db Seed\" @docker exec -i $$(docker ps -q -f name=php.${APP_NAMESPACE}) php artisan db:seed doc-generate: @echo \"Key generate\" @docker exec -i $$(docker ps -q -f name=php.${APP_NAMESPACE}) php artisan scribe:generate restart: @echo \"restart container\" @docker restart php.${APP_NAMESPACE}   <\/code><\/pre>\n<p><strong>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435:<\/strong><\/p>\n<p>\u0412 \u0446\u0435\u043b\u043e\u043c \u0432\u0441\u0451\u00a0\u2014 \u0432\u00a0\u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u0430\u043b \u043f\u0440\u043e\u00a0\u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b, \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u0447\u0442\u043e\u2011\u0442\u043e \u0440\u0430\u0441\u043a\u0440\u044b\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u043e, \u043d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u043e\u0431\u00a0\u044d\u0442\u043e\u043c \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439.<\/p>\n<p><a href=\"https:\/\/github.com\/deniskorbakov\/laravel-frankenphp-template\" rel=\"noopener noreferrer nofollow\">\u0414\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0434\u0435\u0442\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0448\u0443 \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u0432 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439<\/a>.<\/p>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044e \u0432\u0441\u0435\u0445, \u043a\u0442\u043e \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043b \u044d\u0442\u043e\u0442 \u043f\u043e\u0441\u0442. \u042d\u0442\u043e \u043c\u043e\u044f \u043f\u0435\u0440\u0432\u0430\u044f \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u043d\u0430 \u0425\u0430\u0431\u0440\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0435 \u0441\u0443\u0434\u0438\u0442\u0435 \u0441\u0442\u0440\u043e\u0433\u043e, \u043f\u043e\u0441\u0442\u0430\u0440\u0430\u043b\u0441\u044f \u0440\u0430\u0441\u043a\u0440\u044b\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u0430\u043c\u044b\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/902066\/\"> https:\/\/habr.com\/ru\/articles\/902066\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u0445 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e, \u043c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0414\u0435\u043d\u0438\u0441, \u044f PHP \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a <\/p>\n<p>\u042f \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u044e \u043d\u0430 \u0445\u0430\u043a\u0430\u0442\u043e\u043d\u0430\u0445 \u0437\u0430\u00a0\u043a\u043e\u043c\u0430\u043d\u0434\u0443 \u00ab\u0436\u044b\u0431\u0438\u0439\u0440\u044b\u0440\u00bb\u00a0\u2014 <a href=\"https:\/\/%D0%B6%D1%8B%D0%B1%D0%B8%D0%B9%D1%80%D1%8B%D1%80.%D1%80%D1%84\/\" rel=\"noopener noreferrer nofollow\">https:\/\/\u0436\u044b\u0431\u0438\u0439\u0440\u044b\u0440.\u0440\u0444\/<\/a> \u0438 \u0443\u00a0\u043d\u0430\u0441\u00a0\u0431\u044b\u043b\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u0447\u0442\u043e\u00a0\u043d\u0435\u00a0\u0431\u044b\u043b\u043e \u0433\u043e\u0442\u043e\u0432\u043e\u0433\u043e \u0448\u0430\u0431\u043b\u043e\u043d\u0430, \u0441\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0441\u043f\u043e\u043a\u043e\u0439\u043d\u043e \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0442\u044c \u0438 \u0437\u0430\u043d\u043e\u0432\u043e \u043d\u0435\u00a0\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442\u00a0\u0436\u0435 \u043a\u043e\u0434<\/p>\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u0430 \u0442\u043e\u043c\u0443, \u043a\u0430\u043a\u00a0\u044f \u043f\u0438\u0441\u0430\u043b \u044d\u0442\u043e\u0442 \u0448\u0430\u0431\u043b\u043e\u043d, \u0441\u00a0\u043a\u0430\u043a\u0438\u043c\u0438 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438 \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0438 \u0432\u00a0\u0446\u0435\u043b\u043e\u043c \u0435\u0441\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435\u00a0\u0436\u0435\u043b\u0430\u043d\u0438\u0435, \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u043d\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u043e\u0439<\/p>\n<p>\u0425\u043e\u0447\u0443 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e\u00a0\u044d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0435\u00a0\u044d\u0442\u0430\u043b\u043e\u043d\u043d\u043e\u0435 \u0438 \u0431\u0443\u0434\u0443 \u0436\u0434\u0430\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u043a\u0440\u0438\u0442\u0438\u043a\u0438<\/p>\n<blockquote>\n<p><a href=\"https:\/\/github.com\/deniskorbakov\/laravel-frankenphp-template\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0448\u0430\u0431\u043b\u043e\u043d\u0430<\/a><\/p>\n<p><a href=\"https:\/\/github.com\/deniskorbakov\" rel=\"noopener noreferrer nofollow\">\u043c\u043e\u0439 github<\/a><\/p>\n<p>\u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u0434\u043b\u044f \u0441\u0432\u044f\u0437\u0438: <a class=\"mention\" href=\"\/users\/Deniskorbakov\">@Deniskorbakov<\/a><\/p>\n<\/blockquote>\n<p><strong>\u0427\u0442\u043e \u0435\u0441\u0442\u044c \u0432 \u0433\u043e\u0442\u043e\u0432\u043e\u043c \u0448\u0430\u0431\u043b\u043e\u043d\u0435:<\/strong><\/p>\n<ul>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u044b\u0439 \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u0442\u043e\u0447\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 FrankenPHP<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u043d\u044f\u0442\u043d\u043e \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0435 \u0432 \u0434\u043e\u043a\u0435\u0440\u0435 + multi stage \u043f\u043e\u0434 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0438 \u043f\u0440\u043e\u0434<\/p>\n<\/li>\n<li>\n<p>\u0410\u0434\u043c\u0438\u043d\u043a\u0430<\/p>\n<\/li>\n<li>\n<p>\u0410\u043f\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/p>\n<\/li>\n<li>\n<p>\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433 \u0441\u0438\u0441\u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p>\u041b\u043e\u0433\u0438\u043a\u0430 \u0431\u0430\u0437\u043e\u0432\u043e\u0439 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e &#8212; \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e \u043f\u043e\u0447\u0442\u0435<\/p>\n<\/li>\n<li>\n<p>\u0412\u0435\u0431 \u0441\u043e\u043a\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0435\u043d pipline &#8212; GitHub Actions<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d FrankenPHP:<\/strong><\/p>\n<p>\u0411\u044b\u043b\u043e \u0436\u0435\u043b\u0430\u043d\u0438\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u0442\u043e\u0447\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0432\u044b\u0431\u043e\u0440 \u043f\u0430\u043b \u043d\u0430 RoadRunner \u0438 FrankenPHP<\/p>\n<p>FrankenPHP \u0431\u044b\u043b \u0432\u044b\u0431\u0440\u0430\u043d \u0432\u043c\u0435\u0441\u0442\u043e RoadRunner, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0435\u0433\u043e \u043b\u0435\u0433\u0447\u0435 \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0438 \u0432\u043e \u043c\u043d\u043e\u0433\u0438\u0445 \u0431\u0435\u043d\u0447\u043c\u0430\u0440\u043a\u0430\u0445 \u043e\u043d \u043d\u0435 \u0443\u0441\u0442\u0443\u043f\u0430\u0435\u0442 RR<\/p>\n<p><strong>\u0421\u0442\u0435\u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439:<\/strong><\/p>\n<ul>\n<li>\n<p>Laravel 12 <\/p>\n<\/li>\n<li>\n<p>FrankenPHP<\/p>\n<\/li>\n<li>\n<p>Docker\/Docker-compose<\/p>\n<\/li>\n<li>\n<p>Redis<\/p>\n<\/li>\n<li>\n<p>PostgreSQL<\/p>\n<\/li>\n<li>\n<p>Laravel Reverb &#8212; \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440<\/p>\n<\/li>\n<li>\n<p>Horizon &#8212; \u043e\u0432\u0431\u0435\u0440\u0442\u043a\u0430 \u0434\u043b\u044f \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439<\/p>\n<\/li>\n<li>\n<p>PhpStan\/PhpCodesniffer\/Rector &#8212; \u0441\u0442\u0430\u0442 \u0430\u043d\u0430\u043b\u0438\u0437\u0430\u0442\u043e\u0440\u044b<\/p>\n<\/li>\n<li>\n<p>Filament &#8212; \u0430\u0434\u043c\u0438\u043d\u043a\u0430<\/p>\n<\/li>\n<li>\n<p>Beszel &#8212; \u043b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u044b\u0439 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433<\/p>\n<\/li>\n<li>\n<p>Scribe &#8212; \u0430\u043f\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f<\/p>\n<\/li>\n<li>\n<p>Traefik<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0447\u043d\u0451\u043c \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430 + docker:<\/strong><\/p>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Laravel Octane + FrankenPHP<\/p>\n<blockquote>\n<p>Laravel Octane &#8212; \u044d\u0442\u043e \u043f\u0440\u043e\u0432\u0430\u0439\u0434\u0435\u0440 \u0434\u043b\u044f \u043e\u0431\u0441\u043b\u0443\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u0432 \u043d\u0430 RR, Swoole, Franken<\/p>\n<p>\u0442\u0430\u043a \u0436\u0435 \u0443 laravel Octane \u0445\u043e\u0440\u043e\u0448\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u043f\u043e \u043a\u0430\u0436\u0434\u043e\u043c\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0443 + \u0441\u043a\u0430\u0437\u0430\u043d\u043e \u043a\u0430\u043a \u0440\u0430\u0437\u0432\u0435\u0440\u043d\u0443\u0442\u044c \u044d\u0442\u043e \u0432\u0441\u0451 \u0434\u0435\u043b\u043e \u043d\u0430 \u043f\u0440\u043e\u0434\u0435<\/p>\n<\/blockquote>\n<p>\u041f\u043e\u043a\u0430\u0436\u0443 Dockerfile \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432 \u0438\u0442\u043e\u0433\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f<\/p>\n<pre><code class=\"yaml\">FROM dunglas\/frankenphp:1.4 AS base  RUN apt-get update \\     &amp;&amp; DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \\     git \\     unzip \\     librabbitmq-dev \\     libpq-dev \\     supervisor  RUN install-php-extensions \\     gd \\     pcntl \\     opcache \\     pdo \\     pdo_pgsql \\     pgsql \\     redis \\     zip  WORKDIR \/app  COPY --from=composer:2.8 \/usr\/bin\/composer \/usr\/local\/bin\/composer  COPY --from=node:23 \/usr\/local\/lib\/node_modules \/usr\/local\/lib\/node_modules COPY --from=node:23 \/usr\/local\/bin\/node \/usr\/local\/bin\/node  RUN ln -s \/usr\/local\/lib\/node_modules\/npm\/bin\/npm-cli.js \/usr\/local\/bin\/npm  FROM base AS dev  COPY .\/.docker\/supervisor\/supervisord.dev.conf \/etc\/supervisor\/conf.d\/supervisord.conf  CMD [\"\/usr\/bin\/supervisord\", \"-n\", \"-c\", \"\/etc\/supervisor\/conf.d\/supervisord.conf\"]  FROM base AS prod  COPY .\/.docker\/supervisor\/supervisord.prod.conf \/etc\/supervisor\/conf.d\/supervisord.conf  CMD [\"\/usr\/bin\/supervisord\", \"-n\", \"-c\", \"\/etc\/supervisor\/conf.d\/supervisord.conf\"]<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u0434\u043e\u043a\u0435\u0440 \u0444\u0430\u0439\u043b \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439, \u0438\u0437 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0433\u043e \u0447\u0442\u043e \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0442\u044c \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f multi-stage \u0434\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f \u043f\u0440\u043e\u0434\u0430<\/p>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439, \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u0430, \u043a\u0440\u043e\u043d \u0437\u0430\u0434\u0430\u0447 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0447\u0435\u0440\u0435\u0437 supervisor<\/p>\n<p><strong>\u041a\u043e\u043d\u0444\u0438\u0433:<\/strong><\/p>\n<pre><code class=\"bash\">[supervisord] user=root nodaemon=true logfile=\/dev\/stdout logfile_maxbytes=0 pidfile=\/var\/run\/supervisord.pid  [program:octane] command=php \/app\/artisan octane:frankenphp --watch autostart=true autorestart=true stdout_events_enabled=true stderr_events_enabled=true stdout_logfile=\/dev\/stdout stdout_logfile_maxbytes=0 stderr_logfile=\/dev\/stderr stderr_logfile_maxbytes=0  [program:horizon] command=php \/app\/artisan horizon autostart=true autorestart=true stdout_events_enabled=true stderr_events_enabled=true stdout_logfile=\/dev\/stdout stdout_logfile_maxbytes=0 stderr_logfile=\/dev\/stderr stderr_logfile_maxbytes=0  [program:schedule] command=php \/app\/artisan schedule:run autostart=true autorestart=true stdout_events_enabled=true stderr_events_enabled=true stdout_logfile=\/dev\/stdout stdout_logfile_maxbytes=0 stderr_logfile=\/dev\/stderr stderr_logfile_maxbytes=0  [program:reverb] command=php \/app\/artisan reverb:start autostart=true autorestart=true stdout_events_enabled=true stderr_events_enabled=true stdout_logfile=\/dev\/stdout stdout_logfile_maxbytes=0 stderr_logfile=\/dev\/stderr stderr_logfile_maxbytes=0 <\/code><\/pre>\n<p>\u0422\u0430\u043a \u0436\u0435 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e \u0441\u0442\u043e\u0438\u0442 traefik, \u043e\u043d \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 \u043a\u0430\u043a \u0440\u0435\u0432\u0435\u0440\u0441 \u043f\u0440\u043e\u043a\u0441\u0438 &#8212; \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u043f\u0435\u0440\u0435\u0430\u0434\u0440\u0435\u0441\u0430\u0446\u0438\u044e \u043f\u043e \u043f\u043e\u0440\u0442\u0430\u043c<\/p>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u0440:<\/strong><\/p>\n<pre><code class=\"yaml\">  traefik:     image: traefik:v2.10     container_name: traefik.${APP_NAMESPACE}     command:       - --api.insecure=true       - --providers.docker=true       - --entrypoints.web.address=:80     ports:       - \"80:80\"       - \"8080:8080\"     volumes:       - \/var\/run\/docker.sock:\/var\/run\/docker.sock     networks:       - app   php:     build:       context: .       dockerfile: .docker\/php\/Dockerfile       target: dev     volumes:       - .:\/app     labels:       - \"traefik.enable=true\"       - \"traefik.http.routers.${APP_NAMESPACE}.rule=Host(`${APP_HOST:-localhost}`)\"       - \"traefik.http.services${APP_NAMESPACE}.loadbalancer.server.port=${APP_PORT:-8000}\"<\/code><\/pre>\n<p><strong>\u0425\u043e\u0447\u0443 \u043f\u043e\u0434\u043c\u0435\u0442\u0438\u0442\u044c \u0434\u0432\u0430 \u0433\u043b\u0430\u0432\u043d\u044b\u0445 \u043d\u044e\u0430\u043d\u0441\u0430:<\/strong><\/p>\n<ul>\n<li>\n<p>Hot reload \u0440\u0435\u0436\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>Alpine \u043e\u0431\u0440\u0430\u0437\u044b<\/p>\n<\/li>\n<\/ul>\n<p><strong>\u041f\u0440\u043e Hot reload:<\/strong><\/p>\n<p>Franken \u043c\u043e\u0436\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 \u0434\u0432\u0443\u0445 \u0440\u0435\u0436\u0438\u043c\u0430\u0445<\/p>\n<p>1) Prod \u0440\u0435\u0436\u0438\u043c &#8212; \u043a\u043e\u0433\u0434\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438 \u043d\u0430\u0434\u043e \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440, \u0447\u0442\u043e\u0431 \u043e\u043d \u0438\u0437\u043c\u0435\u043d\u0438\u043b \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 (\u043f\u0440\u043e\u0441\u0442\u0430\u044f \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432)<\/p>\n<p>2)  Dev \u0440\u0435\u0436\u0438\u043c &#8212; \u043a\u043e\u0433\u0434\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u0434\u0442\u044f\u0433\u0438\u0432\u0430\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u0441\u0430\u043c \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 (\u0434\u0430\u043d\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043c\u0435\u0434\u043b\u0435\u043d\u0435\u0435 \u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442 \u0434\u043b\u044f \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438)<\/p>\n<p>\u041f\u0440\u0438 Dev \u0440\u0435\u0436\u0438\u043c\u0435 \u043d\u0430\u0434\u043e \u0447\u0442\u043e\u0431 \u0443 \u0432\u0430\u0441 \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0431\u044b\u043b\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430 Node js \u0442\u0430\u043a \u043a\u0430\u043a \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u043e\u0432 \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u0435\u0442 js \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <\/p>\n<p><strong>\u041f\u0440\u043e alpine \u043e\u0431\u0440\u0430\u0437\u044b:<\/strong><\/p>\n<p>\u0412 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0447\u0435\u0442\u043a\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f, \u043f\u043e\u0447\u0435\u043c\u0443 \u0438\u0445 \u043d\u0435 \u0441\u0442\u043e\u0438\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c:<\/p>\n<blockquote>\n<p>\u0421\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c, \u0430 \u0442\u0430\u043a\u0436\u0435 Alpine Linux-\u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 Docker-\u043e\u0431\u0440\u0430\u0437\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 <a href=\"https:\/\/musl.libc.org\" rel=\"noopener noreferrer nofollow\">\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 musl libc<\/a>.<\/p>\n<p>\u0418\u0437\u0432\u0435\u0441\u0442\u043d\u043e, \u0447\u0442\u043e PHP <a href=\"https:\/\/gitlab.alpinelinux.org\/alpine\/aports\/-\/issues\/14381\" rel=\"noopener noreferrer nofollow\">\u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u0435\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442<\/a> \u0441 \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u043e\u0439 \u043f\u043e \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044e \u0441 \u0442\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u043e\u0439 GNU libc, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u0440\u0438 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 \u0432 ZTS \u0440\u0435\u0436\u0438\u043c\u0435 (\u043f\u043e\u0442\u043e\u043a\u043e\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c), \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f FrankenPHP.<\/p>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, <a href=\"https:\/\/github.com\/php\/php-src\/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl\" rel=\"noopener noreferrer nofollow\">\u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u043f\u0440\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 musl<\/a>.<\/p>\n<\/blockquote>\n<p><strong>\u0410\u0434\u043c\u0438\u043d\u043a\u0430:<\/strong><\/p>\n<p>\u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Filament \u0434\u043b\u044f \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432, \u0442\u0430\u043a \u043a\u0430\u043a \u0443 \u043d\u0435\u0435 \u043b\u0435\u0433\u043a\u0430\u044f \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f, \u043a\u0440\u0430\u0441\u0438\u0432\u0430\u044f \u043e\u0431\u043e\u043b\u043e\u0447\u043a\u0430 \u0438 \u0441 \u0435\u0435 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u043e \u0438 \u043a\u0440\u0430\u0441\u0438\u0432\u043e \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0430\u0434\u043c\u0438\u043d\u043a\u0443<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0435\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445, \u0442\u043e \u043e\u043d\u0430 \u043e\u0441\u043e\u0431\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d\u0430 \u0441\u0438\u043b\u044c\u043d\u043e \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u0442<\/p>\n<p><strong>\u0410\u043f\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f:<\/strong><\/p>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u0432\u0430\u0436\u043d\u044b\u0445 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u043d\u0430\u0441, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0447\u0430\u0441\u0442\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u0440\u0443\u0447\u043a\u0438 \u043a\u0435\u0439\u0441\u043e\u0434\u0430\u0442\u0435\u043b\u044f\u043c \u0438 \u0442\u0430\u043a\u0436\u0435 \u043e\u0442\u0433\u043e\u0440\u043e\u0434\u0438\u0442\u044c \u0441\u0435\u0431\u044f \u043e\u0442 \u043b\u0438\u0448\u043d\u0438\u0445 \u0432\u043e\u043f\u0440\u043e\u0441\u043e\u0432 \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u0435\u0440\u043e\u0432, \u0447\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u0432 \u0442\u043e\u043c \u0438\u043b\u0438 \u0438\u043d\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435<\/p>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u043b\u0443\u0447\u0448\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u043b\u044f laravel, \u044d\u0442\u043e &#8212; <a href=\"https:\/\/scribe.knuckles.wtf\/laravel\/\" rel=\"noopener noreferrer nofollow\">https:\/\/scribe.knuckles.wtf\/laravel\/<\/a><\/p>\n<p><strong>\u041f\u043b\u044e\u0441\u044b \u0434\u0430\u043d\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435:<\/strong><\/p>\n<ul>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0440\u0443\u0447\u0435\u043a<\/p>\n<\/li>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f query params<\/p>\n<\/li>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f url params<\/p>\n<\/li>\n<li>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f body params<\/p>\n<\/li>\n<li>\n<p>\u0422\u043e\u043d\u043a\u0430\u044f \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0430<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0430\u0446\u0438\u044f \u0442\u0435\u043c\u044b<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0443\u043a\u0430\u0437\u044b\u0442\u044c \u044f\u0432\u043d\u043e \u0442\u0435 \u0438\u043b\u0438 \u0438\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0430 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432 <\/p>\n<\/li>\n<\/ul>\n<p>\u0410\u0432\u0442\u043e\u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 Laravel Request \u0438 Resource  &#8212; \u0442\u043e \u0435\u0441\u0442\u044c \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u043a\u043b\u0430\u0441\u0441\u044b laravel<\/p>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0432 \u0448\u0430\u0431\u043b\u043e\u043d\u0435 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e DTO \u043e\u0442 <a href=\"https:\/\/spatie.be\/docs\/laravel-data\/v4\/introduction\" rel=\"noopener noreferrer nofollow\">https:\/\/spatie.be\/docs\/laravel-data\/v4\/introduction<\/a><\/p>\n<p>\u0422\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0440\u0443\u0447\u043a\u0430\u043c\u0438 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u0441\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/p>\n<p><strong>\u041c\u043e\u043d\u0438\u0442\u043e\u0440\u0438\u043d\u0433:<\/strong><\/p>\n<p>\u0412 \u044d\u0442\u043e\u0442 \u043f\u0440\u043e\u0435\u043a\u0442, \u044f \u0445\u043e\u0442\u0435\u043b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u0439 \u0441\u0442\u0435\u043a &#8212; grafana, loki, prometeus, promtail<br \/>\u041d\u043e \u043f\u043e\u0442\u043e\u043c \u043f\u043e\u043d\u044f\u043b \u0447\u0442\u043e \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043f\u0443\u0441\u0442\u0430\u044f \u0442\u0440\u0430\u0442\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438 \u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b \u043f\u0440\u043e\u0441\u0442\u0435\u043d\u044c\u043a\u0438\u0439, \u043d\u043e \u0437\u0430\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439 <a href=\"https:\/\/github.com\/henrygd\/beszel\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/henrygd\/beszel<\/a><\/p>\n<p>\u0414\u043b\u044f \u043c\u0435\u043d\u044f \u043e\u043d \u043f\u043e\u0434\u043e\u0448\u0435\u043b \u0438\u0434\u0435\u0430\u043b\u044c\u043d\u043e &#8212; \u0442\u0430\u043a \u043a\u0430\u043a \u043a\u0440\u0430\u0441\u0438\u0432\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u0438 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430<\/p>\n<p><strong>\u0411\u0430\u0437\u043e\u0432\u0430\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f:<\/strong><\/p>\n<p>\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0438 \u0433\u043e\u043b\u043e\u0432\u043d\u0430\u044f \u0431\u043e\u043b\u044c, \u0447\u0442\u043e \u043c\u044b \u0441 \u043a\u0430\u0436\u0434\u044b\u043c \u0445\u0430\u043a\u0430\u0442\u043e\u043d\u043e\u043c \u0437\u0430\u043d\u043e\u0432\u043e \u043f\u0438\u0441\u0430\u043b\u0438 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u043b\u0438 \u0441 \u0444\u0440\u043e\u043d\u0442\u043e\u043c<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0431\u044b\u043b\u043e \u043f\u0440\u0438\u043d\u044f\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0441\u0440\u0430\u0437\u0443 \u0432 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e + \u0432\u0435\u0440\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439<\/p>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0435\u0441\u0442\u044c \u0440\u043e\u043b\u0435\u0432\u043a\u0430 \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0430 \u0440\u043e\u043b\u044c &#8212; \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a, \u043f\u0440\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u0442\u043e\u043b\u044c\u043a\u043e \u043a \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c, \u043a\u0430\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0434\u0430\u0448\u0431\u043e\u0440\u0434 Horizon<\/p>\n<p><strong>\u041f\u0440\u0438\u043c\u0435\u0440 \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438:<\/strong><\/p>\n<pre><code class=\"php\">&lt;?php  declare(strict_types=1);  namespace App\\Services\\Controllers;  use Illuminate\\Validation\\ValidationException; use Illuminate\\Support\\Facades\\Hash; use App\\DTO\\User\\UserAuthShowDTO; use App\\DTO\\Auth\\AuthRegisterDTO; use App\\DTO\\Auth\\AuthLoginDTO; use App\\Models\\User;  final class AuthService {     \/** @return array&lt;string, mixed&gt; *\/     public function register(AuthRegisterDTO $authRegisterDTO): array     {         $user = User::query()-&gt;create([             'name'     =&gt; $authRegisterDTO-&gt;name,             'role'     =&gt; $authRegisterDTO-&gt;role,             'email'    =&gt; $authRegisterDTO-&gt;email,             'password' =&gt; Hash::make($authRegisterDTO-&gt;password),         ]);          return UserAuthShowDTO::from($user)-&gt;toArray();     }      \/**      * @return array&lt;string, mixed&gt;      * @throws ValidationException      *\/     public function login(AuthLoginDTO $authLoginDTO): array     {         $user = User::query()-&gt;where('email', $authLoginDTO-&gt;email)-&gt;firstOrFail();          if (! Hash::check($authLoginDTO-&gt;password, $user-&gt;password)) {             throw ValidationException::withMessages(['bad credentials']);         }          return UserAuthShowDTO::from($user)-&gt;toArray();     } }<\/code><\/pre>\n<p><strong>\u0412\u0435\u0431\u0441\u043e\u043a\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440:<\/strong><\/p>\n<p>\u041d\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u043c \u0445\u0430\u043a\u0430\u0442\u043e\u043d\u0435 \u043d\u0430\u043c \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u044b \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0437\u0430\u0431\u0435\u0433\u0430 \u0432 real time<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0442\u0430\u043a\u0436\u0435 \u0440\u0435\u0448\u0438\u043b \u0435\u0433\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u0448\u0430\u0431\u043b\u043e\u043d, \u0447\u0442\u043e\u0431 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u043d\u0430 \u044d\u0442\u043e \u043d\u0435 \u0442\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u0440\u0435\u043c\u044f<\/p>\n<p><strong>\u041d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u044d\u0442\u0430\u043f \u0434\u043b\u044f \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0441\u0442\u043e\u0438\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c:<\/strong><\/p>\n<ul>\n<li>\n<p>Centrifugo<\/p>\n<\/li>\n<li>\n<p>Larvel reverb<\/p>\n<\/li>\n<\/ul>\n<p>\u0421\u0430\u043c\u043e\u0435 \u043b\u0443\u0447\u0448\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043d\u0430 \u043f\u0445\u043f \u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u043e\u0432 &#8212; \u044d\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Centrifugo, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u0438 \u0445\u043e\u0440\u043e\u0448\u043e \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442 \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432<\/p>\n<p>\u041d\u043e \u0443 \u043c\u0435\u043d\u044f \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u043d\u0430 \u0445\u0430\u043a\u0430\u0442\u043e\u043d\u0435 \u0431\u044b\u0441\u0442\u0440\u043e \u0435\u0433\u043e \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0438 \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u0441 Laravel<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u0440\u0435\u0448\u0438\u043b \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0432 \u0441\u0431\u043e\u0440\u043a\u0443 Laravel Reverb, \u043d\u0435\u043f\u043b\u043e\u0445\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u0435\u0441\u043b\u0438 \u0432\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 &#8212; \u0442\u0430\u043a \u0436\u0435 \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0442\u044c\u0441\u044f \u0431\u043b\u0430\u0433\u043e\u0434\u0440\u044f \u043e\u0447\u0435\u0440\u0434\u044f\u043c, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u043e\u0432<\/p>\n<p><strong>GitHub Actions:<\/strong><\/p>\n<p>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0430\u0439\u043f\u043b\u0430\u043d\u043e\u0432 \u0431\u044b\u043b\u0430 \u0441\u0430\u043c\u043e\u0439 \u043b\u0435\u0433\u043a\u043e\u0439, \u0442\u0430\u043a \u043a\u0430\u043a \u0432 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442\u0435 \u043e\u0447\u0435\u043d\u044c \u043c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 &#8212; \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u043a\u0430\u0436\u0443, \u0447\u0442\u043e \u0443 \u043c\u0435\u043d\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c<\/p>\n<pre><code class=\"yaml\">name: DEPLOY AND BUILD on:   push:     branches: [\"main\"]   pull_request:     branches: [\"main\"]  jobs:   coding-standard:     name: Coding Standard     runs-on: ubuntu-latest     steps:       - uses: actions\/checkout@v2        - name: Setup PHP         uses: shivammathur\/setup-php@v2         with:<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-456498","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/456498","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=456498"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/456498\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=456498"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=456498"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=456498"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}