diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..62c893550 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ \ No newline at end of file diff --git a/README.md b/README.md index e16b2a49b..7e5be472c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,44 @@ # PHP_2023 -https://otus.ru/lessons/razrabotchik-php/?utm_source=github&utm_medium=free&utm_campaign=otus +###Описание/Пошаговая инструкция выполнения домашнего задания: + +Консольный чат на сокетах +Создать логику, размещаемую в двух php-контейнерах (server и client), объединённых общим volume. +Скрипты запускаются в режиме прослушивания STDIN и обмениваются друг с другом вводимыми сообщениями через unix-сокеты. + +- сервер поднимается всегда первым +- клиент ожидает ввод из STDIN и отправляет сообщения серверу +- сервер выводит полученное сообщение в STDOUT и отправляет клиенту подтверждение (например, "Received 24 bytes") +- клиент выводит полученное подтверждение в STDOUT и начинает новую итерацию цикла + +###Критерии оценки: +1. Конструкции @ и die неприемлемы. Вместо них используйте исключения +2. Принимается только Unix-сокет +3. Код здесь и далее мы пишем с применением ООП +4. Код здесь и далее должен быть конфигурируем через файлы настроек типа config.ini +5. Обратите внимание на паттерн FrontController (он же - единая точка доступа). Все приложения, которые Вы создаёте здесь и далее должны вызываться через один файл, в котором есть ТОЛЬКО +6. Точка входа - app.php +7. Сервер и клиент запускаются командами +php app.php server +php app.php client +В app.php только строки +require_once('/path/to/composer/autoload.php'); +try { +$app = new App(); +$app->run(); +} +catch(Exception $e){ +} +8. Логика чтения конфигураций и работы с сокетами - только в классах. + + + +Порядок действий для запуска app + +1. docker-compose up --build -d +2. docker-compose run server composer install +3. docker-compose run server php app.php server +4. docker-compose run server php app.php client +5. exit + + diff --git a/hw6/.gitignore b/hw6/.gitignore new file mode 100644 index 000000000..2eea525d8 --- /dev/null +++ b/hw6/.gitignore @@ -0,0 +1 @@ +.env \ No newline at end of file diff --git a/hw6/cli/Dockerfile b/hw6/cli/Dockerfile new file mode 100644 index 000000000..0ba3f23ae --- /dev/null +++ b/hw6/cli/Dockerfile @@ -0,0 +1,17 @@ +FROM php:8.2-cli + +RUN apt-get update \ + && apt-get install -y \ + libxml2-dev \ + libssl-dev \ + && docker-php-ext-install sockets + +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer + +COPY . /var/www/application.local + +WORKDIR /var/www/application.local + +# Команда для запуска сервера при старте контейнера +#CMD ["php", "-S", "0.0.0.0:8000", "-t", "/var/www/application.local"] + diff --git a/hw6/docker-compose.yaml b/hw6/docker-compose.yaml new file mode 100644 index 000000000..83d6c0e7f --- /dev/null +++ b/hw6/docker-compose.yaml @@ -0,0 +1,34 @@ +version: "3" + +services: + server: + build: + context: ./cli + dockerfile: Dockerfile + image: hw6/server + container_name: server + volumes: + - ./www:/var/www + - ./socket:/var/www/application.local/socket/ + networks: + - app-network + command: php /var/www/application.local/app.php server + + client: + build: + context: ./cli + dockerfile: Dockerfile + image: hw6/client + container_name: client + volumes: + - ./www:/var/www + - ./socket:/var/www/application.local/socket/ + networks: + - app-network + +networks: + app-network: + driver: bridge + +volumes: + socket: diff --git a/hw6/result/2024-04-04_15-30-02.png b/hw6/result/2024-04-04_15-30-02.png new file mode 100644 index 000000000..c0e95a9e0 Binary files /dev/null and b/hw6/result/2024-04-04_15-30-02.png differ diff --git a/hw6/result/2024-04-04_15-30-24.png b/hw6/result/2024-04-04_15-30-24.png new file mode 100644 index 000000000..7f8861145 Binary files /dev/null and b/hw6/result/2024-04-04_15-30-24.png differ diff --git a/hw6/www/.gitignore b/hw6/www/.gitignore new file mode 100644 index 000000000..49ce3c193 --- /dev/null +++ b/hw6/www/.gitignore @@ -0,0 +1 @@ +/vendor \ No newline at end of file diff --git a/hw6/www/application.local/app.php b/hw6/www/application.local/app.php new file mode 100644 index 000000000..6bb800956 --- /dev/null +++ b/hw6/www/application.local/app.php @@ -0,0 +1,14 @@ +run(); +} catch (Exception $e) { + echo $e->getMessage(); +} diff --git a/hw6/www/application.local/config/config.ini b/hw6/www/application.local/config/config.ini new file mode 100644 index 000000000..846fd828d --- /dev/null +++ b/hw6/www/application.local/config/config.ini @@ -0,0 +1,2 @@ +[socket] +file = /var/www/application.local/socket/socket_hw6.sock \ No newline at end of file diff --git a/hw6/www/application.local/src/App.php b/hw6/www/application.local/src/App.php new file mode 100644 index 000000000..aabb3227f --- /dev/null +++ b/hw6/www/application.local/src/App.php @@ -0,0 +1,45 @@ +mode = $argv[1] ?? ''; + + if (!in_array($this->mode, ['server', 'client'])) { + throw new InvalidArgumentException('Invalid mode. Use "server" or "client".'); + } + } + + public function run() + { + $config = parse_ini_file('config/config.ini', true); + $file = $config['socket']['file']; + + $this->socket = new Socket($file); + + switch ($this->mode) { + case 'server': + $server = new Server($this->socket); + $server->start(); + break; + case 'client': + $client = new Client($this->socket); + $client->start(); + break; + } + } +} diff --git a/hw6/www/application.local/src/Client.php b/hw6/www/application.local/src/Client.php new file mode 100644 index 000000000..1c811ab53 --- /dev/null +++ b/hw6/www/application.local/src/Client.php @@ -0,0 +1,36 @@ +socket = $socket; + } + + public function start() + { + $this->socket->connect(); + + echo "To exit, type 'exit'\n"; + + while (true) { + $message = readline("Enter message: "); + $this->socket->write($message); + + if ($message === 'exit') { + $this->socket->write($message); + $this->socket->close(); + break; + } + + $confirmation = $this->socket->read(); + echo "Server confirmation: {$confirmation}\n"; + } + } +} diff --git a/hw6/www/application.local/src/Server.php b/hw6/www/application.local/src/Server.php new file mode 100644 index 000000000..25e1bf861 --- /dev/null +++ b/hw6/www/application.local/src/Server.php @@ -0,0 +1,35 @@ +socket = $socket; + } + + public function start() + { + $this->socket->bind(); + $this->socket->listen(); + $this->socket->accept(); + + while ($this->isRunning) { + $message = $this->socket->receive(); + + if ($message === 'exit') { + $this->isRunning = false; + $this->socket->close(); + } else { + echo "Received: {$message}\n"; + $this->socket->write("Received " . strlen($message) . " bytes"); + } + } + } +} diff --git a/hw6/www/application.local/src/Socket.php b/hw6/www/application.local/src/Socket.php new file mode 100644 index 000000000..a583e9512 --- /dev/null +++ b/hw6/www/application.local/src/Socket.php @@ -0,0 +1,74 @@ +socket = socket_create(AF_UNIX, SOCK_STREAM, 0); + $this->path = $path; + } + + public function read() + { + $result = socket_read($this->socket, 2048); + + if ($result === false) { + $error = socket_last_error(); + echo "Socket read error: " . socket_strerror($error); + $this->close(); + } + return $result; + } + + public function write($data) + { + socket_write($this->socket, $data, strlen($data)); + } + + public function listen() + { + socket_listen($this->socket, self::BACKLOG_SIZE); + } + + public function accept() + { + $_socket = socket_accept($this->socket); + + if ($_socket === false) { + $error = socket_last_error(); + throw new \RuntimeException("Socket accept error: " . socket_strerror($error)); + } + + $this->socket = $_socket; + } + + public function connect() + { + socket_connect($this->socket, $this->path); + } + + public function bind() + { + socket_bind($this->socket, $this->path); + } + + public function close() + { + socket_close($this->socket); + } + + public function receive(): string + { + socket_recv($this->socket, $message, self::MAX_SIZE_MESSAGE, 0); + return $message ?? ''; + } +} diff --git a/hw6/www/composer.json b/hw6/www/composer.json new file mode 100644 index 000000000..4c0948b1a --- /dev/null +++ b/hw6/www/composer.json @@ -0,0 +1,19 @@ +{ + "name": "alisa/hw6", + "type": "project", + "autoload": { + "psr-4": { + "App\\": "application.local/src/" + } + }, + "authors": [ + { + "name": "Alisa", + "email": "kolosovaalisa@gmail.com" + } + ], + "require": { + "ext-sockets": "*", + "ext-readline": "*" + } +} diff --git a/hw6/www/composer.lock b/hw6/www/composer.lock new file mode 100644 index 000000000..cb2f0f6cd --- /dev/null +++ b/hw6/www/composer.lock @@ -0,0 +1,21 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "f600857e61aafc6f1435264899138f9c", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "ext-sockets": "*", + "ext-readline": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +}