diff --git a/hw19/.docker/nginx/Dockerfile b/hw19/.docker/nginx/Dockerfile new file mode 100644 index 000000000..d19ea279d --- /dev/null +++ b/hw19/.docker/nginx/Dockerfile @@ -0,0 +1,5 @@ +FROM nginx + +WORKDIR /var/www/html + +COPY ngnix.conf /etc/nginx/nginx.conf diff --git a/hw19/.docker/nginx/ngnix.conf b/hw19/.docker/nginx/ngnix.conf new file mode 100644 index 000000000..291af686f --- /dev/null +++ b/hw19/.docker/nginx/ngnix.conf @@ -0,0 +1,25 @@ +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + server { + listen 80; + server_name localhost; + root /var/www/html/public/; + + location / { + index index.php; + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_pass php-fpm:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + } +} diff --git a/hw19/.docker/php-fpm/Dockerfile b/hw19/.docker/php-fpm/Dockerfile new file mode 100644 index 000000000..6e76f71c9 --- /dev/null +++ b/hw19/.docker/php-fpm/Dockerfile @@ -0,0 +1,11 @@ +FROM php:8.2-fpm + +RUN apt-get update && apt-get install -y \ + libzip-dev \ + && docker-php-ext-install zip sockets + +RUN chown -R www-data:www-data /var/www/html + +WORKDIR /var/www/html + +CMD ["php-fpm"] diff --git a/hw19/README.md b/hw19/README.md new file mode 100644 index 000000000..94fb65b7c --- /dev/null +++ b/hw19/README.md @@ -0,0 +1,12 @@ +# Работа с очередью + +## Отправка данных +1. Для генерации банковской выписки за указанные даты перейдите в браузере на главную страницу http://localhost/ +2. Выберите нужный интервал дат +3. Нажмите кнопку отправить +4. После чего форма вас переадресует на страницу обработчика формы /formHandler, в котором даты отправятся через RabbitMQ Producer в очередь + +## Получение данных +1. Чтобы считать данную очередь, вам нужно зайти в консоль и запуcтить команду php bin/console rabbitmq:consumer +2. Теперь в консоли вы можете видеть отправленные из браузерной формы даты +3. Также в консольной команде после вывода данных на экран происходит отправка письма. diff --git a/hw19/docker-compose.yml b/hw19/docker-compose.yml new file mode 100644 index 000000000..ee7740850 --- /dev/null +++ b/hw19/docker-compose.yml @@ -0,0 +1,36 @@ +version: "3" +services: + nginx: + container_name: nginx-otusphp + build: ./.docker/nginx + ports: + - "80:80" + volumes: + - ./www:/var/www/html + depends_on: + - php-fpm + networks: + - otusphp + + php-fpm: + container_name: php-fpm-otusphp + build: ./.docker/php-fpm/ + volumes: + - ./www:/var/www/html + depends_on: + - rabbitmq + networks: + - otusphp + + rabbitmq: + container_name: rabbitmq-otusphp + image: rabbitmq:3.10.7-management + ports: + - 15672:15672 + - 5672:5672 + networks: + - otusphp + +networks: + otusphp: + driver: bridge diff --git a/hw19/www/bin/console b/hw19/www/bin/console new file mode 100755 index 000000000..d11f5c590 --- /dev/null +++ b/hw19/www/bin/console @@ -0,0 +1,16 @@ +#!/usr/bin/env php +add(new ConsumerCommand()); +try { + $application->run(); +} catch (Exception $e) { + throw new \Exception($e->getMessage()); +} diff --git a/hw19/www/composer.json b/hw19/www/composer.json new file mode 100644 index 000000000..561bc0388 --- /dev/null +++ b/hw19/www/composer.json @@ -0,0 +1,20 @@ +{ + "name": "shabanov/otusphp", + "autoload": { + "psr-4": { + "Shabanov\\Otusphp\\": "src/" + } + }, + "authors": [ + { + "name": "Vyacheslav Shabanov", + "email": "saveliy@mail.ru" + } + ], + "require": { + "php-amqplib/php-amqplib": "^3.6", + "symfony/console": "^7.0", + "symfony/mailer": "^7.0", + "phpmailer/phpmailer": "^6.9" + } +} diff --git a/hw19/www/public/index.php b/hw19/www/public/index.php new file mode 100644 index 000000000..f434cc343 --- /dev/null +++ b/hw19/www/public/index.php @@ -0,0 +1,13 @@ +getMessage()); +} diff --git a/hw19/www/src/App.php b/hw19/www/src/App.php new file mode 100644 index 000000000..e32e3cd04 --- /dev/null +++ b/hw19/www/src/App.php @@ -0,0 +1,15 @@ +run(); + } +} diff --git a/hw19/www/src/Command/ConsumerCommand.php b/hw19/www/src/Command/ConsumerCommand.php new file mode 100644 index 000000000..d97934906 --- /dev/null +++ b/hw19/www/src/Command/ConsumerCommand.php @@ -0,0 +1,87 @@ +setName('rabbitmq:consumer') + ->setDescription('RabbitMQ consumer обработчик формы'); + } + + protected function initialize(InputInterface $input, OutputInterface $output): void + { + $this->connect = new $this->connectClient(); + $this->channel = $this->connect->getClient(); + } + protected function execute(InputInterface $input, OutputInterface $output): int + { + $callback = function ($msg) use ($output) { + /** + * Выведим в консоль поступившую строку + */ + $output->writeln('[x] ' . $msg->body . ''); + /** + * Отправим строку на Email + */ + $this->sendEmail($msg->body); + }; + + $this->channel->basic_consume( + $this->queue, + '', + false, + true, + false, + false, + $callback + ); + + while ($this->channel->is_consuming()) { + $this->channel->wait(); + } + + $this->channel->close(); + $this->connect->close(); + + return Command::SUCCESS; + } + + protected function sendEmail(string $body): void + { + $mail = new \PHPMailer(true); + + try { + $mail->isSMTP(); + $mail->Host = 'smtp.yandex.ru'; + $mail->SMTPAuth = true; + $mail->Username = 'saveliy'; + $mail->Password = 'password'; + $mail->Port = 465; + + $mail->setFrom('sender@yandex.ru', 'Sender'); + $mail->addAddress('recipient@yandex.ru', 'Recipient'); + + $mail->Subject = 'Новое сообщение'; + $mail->Body = $body; + + $mail->send(); + } catch (Exception $e) { + echo 'Ошибка отправки письма: ' . $mail->ErrorInfo; + } + } +} diff --git a/hw19/www/src/Connection/ConnectionInterface.php b/hw19/www/src/Connection/ConnectionInterface.php new file mode 100644 index 000000000..a851cd355 --- /dev/null +++ b/hw19/www/src/Connection/ConnectionInterface.php @@ -0,0 +1,10 @@ +connect = new AMQPStreamConnection('rabbitmq', 5672, 'guest', 'guest'); + } + public function getClient(): AMQPChannel|AbstractChannel + { + return $this->connect->channel(); + } + + /** + * @throws Exception + */ + public function close(): void + { + $this->connect->close(); + } +} diff --git a/hw19/www/src/Controller/PageController.php b/hw19/www/src/Controller/PageController.php new file mode 100644 index 000000000..5d6c7a77f --- /dev/null +++ b/hw19/www/src/Controller/PageController.php @@ -0,0 +1,24 @@ +show(); + } + + public function formHandler(): void + { + if (!empty($_REQUEST['send']) && !empty($_REQUEST['date_from'])) { + $message = 'Date from: ' . $_REQUEST['date_from'] . ' Date to: ' . $_REQUEST['date_to']; + (new RabbitMqProducer())->send($message); + echo (new FormSuccessRender())->show(); + } + } +} diff --git a/hw19/www/src/Producer/RabbitMqProducer.php b/hw19/www/src/Producer/RabbitMqProducer.php new file mode 100644 index 000000000..49314ff86 --- /dev/null +++ b/hw19/www/src/Producer/RabbitMqProducer.php @@ -0,0 +1,46 @@ +connect = new $this->connectClient(); + $this->channel = $this->connect->getClient(); + } + + public function send(string $message): void + { + $this->channel->queue_declare($this->queue, false, true, false, false); + $this->channel->exchange_declare($this->exchange, AMQPExchangeType::DIRECT, false, true, false); + $this->channel->queue_bind($this->queue, $this->exchange); + + $this->messagePublish($message); + + $this->channel->close(); + $this->connect->close(); + } + + private function messagePublish(string $message): void + { + $message = new AMQPMessage($message, [ + 'content_type' => 'text/plain', + 'delivery_mode' => AMQPMessage::DELIVERY_MODE_NON_PERSISTENT + ]); + $this->channel->basic_publish($message, $this->exchange); + } +} diff --git a/hw19/www/src/Render/Error404Render.php b/hw19/www/src/Render/Error404Render.php new file mode 100644 index 000000000..156e6e506 --- /dev/null +++ b/hw19/www/src/Render/Error404Render.php @@ -0,0 +1,19 @@ + + +

404 Not Found

+ +
+

Генерация банковской выписки за указанные даты:

+ От + По +

+
+ +

Спасибо за вашу заявку. Скоро на email вам поступит отчет

+ 'main', + '/formHandler' => 'formHandler', + ]; + public function __construct(private string $url) + {} + + public function run(): void + { + $action = self::MAP[$this->url] ?? null; + if ($action !== null) { + $controller = new PageController(); + if (method_exists($controller, $action)) { + $controller->$action(); + return; + } + } + + http_response_code(404); + echo (new Error404Render())->show(); + } +}