diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..9bdb99403 --- /dev/null +++ b/.env.example @@ -0,0 +1,33 @@ +#### path ####################### +CONTROLLERS_BASE_PATH=App/Http/Controllers +VIEW_BASE_PATH=resources/Views +APP_LOG_PATH=log/application.log + +#### framework ################## +DEFAULT_CONTROLLER_NAME=Default +DEFAULT_CONTROLLER_METHOD=run +DEFAULT_SCRIPT_NAME=index.php +DEFAULT_VIEW_NAME=index + +### sender ###################### +NOTIFICATOR=Screen + +#### postgres ################### +PGSQL_DB_HOST=postgres +PGSQL_DB_PORT=5432 +PGSQL_DB_NAME=postgres +PGSQL_DB_USER=postgres +PGSQL_DB_PASSWORD=password +PGSQL_DRIVER=pgsql +PGSQL_PG_DRIVER=pg_connect + +#### redis ####################### +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_TIMEOUT=0 +REDIS_RESERVED=as +REDIS_RETRY_INTERVAL=0 +REDIS_READ_TIMEOUT=0.0 + +#### Project ##################### +PROBABILITY_PRODUCT_DEFECT=50% \ No newline at end of file diff --git a/FastFood.postman_collection.json b/FastFood.postman_collection.json new file mode 100644 index 000000000..86b6d8917 --- /dev/null +++ b/FastFood.postman_collection.json @@ -0,0 +1,174 @@ +{ + "info": { + "_postman_id": "37a40668-f911-498c-a89a-e041408532b2", + "name": "FastFood", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Sandwich", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "product[size]", + "value": "Маленькая", + "type": "text" + }, + { + "key": "product[type]", + "value": "Ржаная", + "type": "text" + }, + { + "key": "sauces[mayonnaise]", + "value": "Московский провансаль", + "type": "text" + }, + { + "key": "sauces[tabasco]", + "value": "Слабый", + "type": "text" + } + ] + }, + "url": { + "raw": "{{host}}/FastFood/Sandwich", + "host": [ + "{{host}}" + ], + "path": [ + "FastFood", + "Sandwich" + ] + } + }, + "response": [] + }, + { + "name": "Burger", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "product[size]", + "value": "Большой", + "type": "text" + }, + { + "key": "product[type]", + "value": "Без кунжута", + "type": "text" + }, + { + "key": "sauces[mayonnaise]", + "value": "Ряба оливковый", + "type": "text" + }, + { + "key": "sauces[tabasco]", + "value": "Классический", + "type": "text" + }, + { + "key": "ingredients[steak]", + "value": "Сильная прожарка", + "type": "text" + }, + { + "key": "ingredients[onion]", + "value": "Зеленый репчатый", + "type": "text" + }, + { + "key": "ingredients[pepper]", + "value": "Болгарский", + "type": "text" + } + ] + }, + "url": { + "raw": "{{host}}/FastFood/Burger", + "host": [ + "{{host}}" + ], + "path": [ + "FastFood", + "Burger" + ] + } + }, + "response": [] + }, + { + "name": "HotDog", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "product[size]", + "value": "Средний", + "type": "text" + }, + { + "key": "product[type]", + "value": "Булка с разрезом", + "type": "text" + }, + { + "key": "sauces[mayonnaise]", + "value": "Ряба классический", + "type": "text" + }, + { + "key": "sauces[tabasco]", + "value": "Острый", + "type": "text" + }, + { + "key": "ingredients[sausage]", + "value": "Сливочная", + "type": "text" + }, + { + "key": "ingredients[onion]", + "value": "Зеленый. Перья", + "type": "text" + }, + { + "key": "ingredients[pepper]", + "value": "Черный", + "type": "text" + } + ] + }, + "url": { + "raw": "{{host}}/FastFood/HotDog", + "host": [ + "{{host}}" + ], + "path": [ + "FastFood", + "HotDog" + ] + } + }, + "response": [] + } + ], + "variable": [ + { + "key": "host", + "value": "homework.otus" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 000000000..0e82b65f6 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# Домашняя работа по Паттернам + +### Требования +1. PHP >=8.0 +2. Composer +3. Набор серверов (установленные сервера Redis) + +### Используются библиотеки +1. "vlucas/phpdotenv": "^5.1", +2. "monolog/monolog": "2.x-dev", +3. "illuminate/container": "9.x-dev" + +### Задание +1. Абстрактная фабрика будет отвечать за генерацию базового продукта-прототипа: бургер, сэндвич или хот-дог +2. При готовке каждого типа продукта Декоратор будет добавлять составляющие к базовому продукту либо по рецепту, либо по пожеланию клиента (салат, лук, перец и т.д.) +3. Наблюдатель подписывается на статус приготовления и отправляет оповещения о том, что изменился статус приготовления продукта. +4. Прокси используется для навешивание пре и пост событий на процесс готовки. Например, если бургер не соответствует стандарту, пост событие утилизирует его. +5. Стратегия будет отвечать за то, что нужно приготовить. +6. Все сущности должны по максимуму генерироваться через DI. + +### Описание проекта +Реализованные паттерны: +1) Абстрактная фабрика +[app/Services/Factories/ProductFactory](app/Services/Factories/ProductFactory) - абстрактная фабрика. +AbstractProductFactory.php - класс, используемый в качестве интерфейса для фабрики +[BurgerFactory.php](app/Services/Factories/ProductFactory/BurgerFactory.php) - реальная фабрика для Бургеров, +[HotDogFactory.php](app/Services/Factories/ProductFactory/HotDogFactory.php) - реальная фабрика для Хот-догов, +[SandwichFactory.php](app/Services/Factories/ProductFactory/SandwichFactory.php) - реальная фабрика для Сэндвичей + +2) Декоратор + [IIngredient.php](app/Services/Factories/ProductFactory/IIngredient.php) - контракт для Ингредиентов + [ISauce.php](app/Services/Factories/ProductFactory/ISauce.php) - контракт для Соусов + В каждой реальной фабрике например BurgerFactory.php есть класс реализованный абстрактный метод `createIngredients()` +На основании слияния массива с рецептом (набор ингредиентов запрашивается в статическом методе `BurgerRecipe::getIngredient()`) +и пожелания заказчика (`customIngredientsList[]`) формируется Декоратор состоящий из нужных ингредиентов. +В дальнейшем используются методы из базового класса [IIngredient.php](app/Services/Products/Ingredients/Ingredient.php), +которые проходят по всей цепочке ингредиентов. Так же работает и механизм для Соусов. + +3) Наблюдатель +[Observer](app/Services/Observer) +Определяем подписчика на события [ProductObserver.php](app/Services/Observer/ProductObserver.php), +где реализовываем метод реакции на событие `update(SplSubject $object)`. Вызов этого метода для подписчиков происходит из базового издателя +например [Ingredients.php](app/Services/Products/Ingredients/Ingredient.php), в котором реализован интерфейс SplSubject. +В нем так же для всех дочерних классов формируются события с помощью вызова метода `$this->notify()` +В свою очередь метод `notify()` передается всем дочерним классам конкретных ингредиентов входящих в декоратор. +Список подписчиков хранится в свойстве `protected SplObjectStorage $observerList;` и слушатели подключаются в [ProductOrder.php](app/Services/Orders/ProductOrder.php) + `$this->ingredients->attach($this->observer);` + +4) Заместитель +Используя обертку [ProxyProductOrder.php](./app/Services/Orders/ProxyProductOrder.php) создается заказ, +в котором вызываются методы основного класса [ProductOrder.php](./app/Services/Orders/ProductOrder.php) +и осуществляется анализ созданного продукта синтетическим методом определения качества продукта на основе параметра 'PROBABILITY_PRODUCT_DEFECT' из файла .env +определяющего вероятность. Базово установлено 50% - что продукт будет надлежащего качества. + +5) Стратегия +[Strategy](./app/Services/Strategy) +С помощью данного паттерна выбирается стратегия приготовления стейков для бургеров разной прожарки. В классе Steak.php определен метод `setStrategy()`. +Вызов этого метода осуществляется из заказа `ProductOrder.php`. В момент когда вызывается метод prepare() для стейка, +то в нем вызывается метод $this->strategy->prepare(); $this->strategy - хранит класс который должен быть вызван при нужной стратегии. +Таких классов три: +[SteakHard.php](./app/Services/Strategy/CookingTechnology/SteakHard.php) +[SteakMiddle.php](./app/Services/Strategy/CookingTechnology/SteakMiddle.php) +[SteakSoft.php](./app/Services/Strategy/CookingTechnology/SteakSoft.php) + +[Helpers/Recipes/](app/Services/Helpers/Recipes) - рецепты продуктов +[Orders](app/Services/Orders) - Заказ продукта, в котором формируется продукт из составляющих + +Создание заказа осуществляется в классе [App\Services\Orders\ProductOrder](app/Services/Orders/ProductOrder.php), +через заместитель [App\Services\Orders\ProxyProductOrder](app/Services/Orders/ProxyProductOrder.php), +в который передается фабрика продукта на основе абстрактного класса[ App\Services\Factories\ProductFactory\AbstractProductFactory.php](app/Services/Factories/ProductFactory/AbstractProductFactory.php). +В заказе формируется составляющие продукта c помощью public function createProduct(). Готовится заказ prepareProduct(). И выводится результат getProduct() +Для каждого продукта реализован свой метод в контроллере [FastFoodController.php](app/Http/Controllers/FastFoodController.php) +В них происходит связывание реальной фабрики с абстрактным классом и определение метода оповещения о состоянии заказа для использования контейнера DI +Для этого используется метод `bind()` в базовом контроллере [BaseController](app/Http/Controllers/BaseController.php). +и дальше создается модель использую контейнер из пакета 'illuminate/container' +$this->model = $this->container->make(FastFoodModel::class); \ No newline at end of file diff --git a/app/Console/Routes/ConsoleRouter.php b/app/Console/Routes/ConsoleRouter.php new file mode 100644 index 000000000..e2a150710 --- /dev/null +++ b/app/Console/Routes/ConsoleRouter.php @@ -0,0 +1,32 @@ + '/Controller/Method', + default => '', + }; + } +} \ No newline at end of file diff --git a/app/Console/start.php b/app/Console/start.php new file mode 100644 index 000000000..a4bb09665 --- /dev/null +++ b/app/Console/start.php @@ -0,0 +1,12 @@ +send($ex->getCode(), $ex->getMessage()); +} \ No newline at end of file diff --git a/app/Exceptions/ErrorCodes.php b/app/Exceptions/ErrorCodes.php new file mode 100644 index 000000000..b0031941c --- /dev/null +++ b/app/Exceptions/ErrorCodes.php @@ -0,0 +1,42 @@ + 700, + InvalidProductOrderException::class => 701, + ViewLoaderException::class => 900, + ]; + + /** + * @param string $codeName + * @return int + */ + public static function getCode(string $codeName = ''): int + { + return self::$errorCodes[$codeName] ?? 0; + } + + /** + * @param int $code + * @return string|false + */ + public static function getMessage(int $code): string|false + { + return array_search($code, self::$errorCodes); + } +} \ No newline at end of file diff --git a/app/Exceptions/ILogged.php b/app/Exceptions/ILogged.php new file mode 100644 index 000000000..ce87da006 --- /dev/null +++ b/app/Exceptions/ILogged.php @@ -0,0 +1,10 @@ +message = "Router error: "; + ApplicationLogger::addLog(Logger::ERROR, $this->message . $message); + parent::__construct($this->message, $code, $previous); + } +} \ No newline at end of file diff --git a/app/Exceptions/Router/InvalidRouteMethodException.php b/app/Exceptions/Router/InvalidRouteMethodException.php new file mode 100644 index 000000000..0b6f61201 --- /dev/null +++ b/app/Exceptions/Router/InvalidRouteMethodException.php @@ -0,0 +1,24 @@ + "mixed", + 'port' => "mixed", + 'dbname' => "mixed", + 'user' => "mixed", + 'pass' => "mixed" + ])] + public static function getConnectionConfigPostgres(): array + { + return [ + 'host' => $_ENV['PGSQL_DB_HOST'], + 'port' => $_ENV['PGSQL_DB_PORT'], + 'dbname' => $_ENV['PGSQL_DB_NAME'], + 'user' => $_ENV['PGSQL_DB_USER'], + 'pass' => $_ENV['PGSQL_DB_PASSWORD'], + ]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php new file mode 100644 index 000000000..f707866b6 --- /dev/null +++ b/app/Http/Controllers/BaseController.php @@ -0,0 +1,133 @@ +response = $response; + $this->viewBasePath = $_ENV['VIEW_BASE_PATH'] ?? self::VIEW_BASE_PATH; + $this->container = $container; + } + + /** + * Связывает Абстрактную фабрику с конкретным классом в DI контейнере + * + * @param string $productName + * @param string $productSize + */ + public function bind(string $productName, string $productSize): void + { + $productName = mb_strtolower($productName); + AppServiceProvider::bindProductFactory($this->container, $productName, $productSize); + AppServiceProvider::bindNotificator($this->container); + } + + /** + * Загружает View для контроллера. + * + * @param string $viewName + * @throws ViewLoaderException + */ + protected function loadView(string $viewName = ''): void + { + if (empty($viewName)) { + $viewName = $this->getCurrentControllerName() . '/' . $_ENV['DEFAULT_VIEW_NAME']; + } + $path = $_SERVER['DOCUMENT_ROOT'] . '/' . $this->viewBasePath; + $app = new ViewsLoader($this->data); + $app->load($path . '/' . $viewName . '.php'); + } + + /** + * Возвращает параметры пришедшие в запросе + * + * @return array + */ + protected function getParameters(): array + { + return $this->parameters = $_REQUEST ?? []; + } + + /** + * Возвращает имя текущего контроллера + * + * @return string + */ + private function getCurrentControllerName(): string + { + return str_replace( + 'Controller', + '', + (new \ReflectionClass($this))->getShortName() + ); + } + + /** + * + * @param array $result + */ + protected function xhrSendResult(array $result): void + { + $this->response->send(StatusCodes::OK, $this->responseMsg, ['title' => $this->title, 'info' => $result]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/DefaultController.php b/app/Http/Controllers/DefaultController.php new file mode 100644 index 000000000..c63bc0792 --- /dev/null +++ b/app/Http/Controllers/DefaultController.php @@ -0,0 +1,40 @@ +data['title'] = 'FastFood'; + } + + /** + * Обработчик маршрута по умолчанию + */ + public function run(): void + { + //TODO + } +} \ No newline at end of file diff --git a/app/Http/Controllers/FastFoodController.php b/app/Http/Controllers/FastFoodController.php new file mode 100644 index 000000000..2c54971b4 --- /dev/null +++ b/app/Http/Controllers/FastFoodController.php @@ -0,0 +1,109 @@ +data['title'] = 'FastFood'; + } + + /** + * Обработчик маршрута /FastFood/Burger + * + * @throws ViewLoaderException + * @throws BindingResolutionException + */ + public function burger(): void + { + $ingredients = $this->getParameters()['ingredients'] ?? []; + $sauces = $this->getParameters()['sauces'] ?? []; + $product = $this->getParameters()['product'] ?? ''; + + $this->bind('Burger', $product['size'] ?? ''); + $this->model = $this->container->make(FastFoodModel::class); + $burger = $this->model + ->createProduct(customBaseType:$product['type'] ?? '', customIngredients:$ingredients, customSauces:$sauces) + ->prepareProduct() + ->getProduct(); + + $this->data['burger'] = $burger; + $this->data['sandwich'] = ''; + $this->data['hotDog'] = ''; + $this->loadView('Template/header'); + $this->loadView('Default/index'); + $this->loadView('Template/footer'); + } + + /** + * Обработчик маршрута /FastFood/HotDog + * + * @throws BindingResolutionException + * @throws ViewLoaderException + */ + public function hotDog(): void + { + $ingredients = $this->getParameters()['ingredients'] ?? []; + $sauces = $this->getParameters()['sauces'] ?? []; + $product = $this->getParameters()['product'] ?? ''; + + $this->bind('HotDog', $product['size'] ?? ''); + $this->model = $this->container->make(FastFoodModel::class); + $hotDog = $this->model + ->createProduct(customBaseType:$product['type'] ?? '', customIngredients:$ingredients, customSauces:$sauces) + ->prepareProduct() + ->getProduct(); + + $this->data['burger'] = ''; + $this->data['sandwich'] = ''; + $this->data['hotDog'] = $hotDog; + $this->loadView('Template/header'); + $this->loadView('Default/index'); + $this->loadView('Template/footer'); + } + + /** + * Обработчик маршрута /FastFood/Sandwich + * + * @throws BindingResolutionException + * @throws ViewLoaderException + */ + public function sandwich(): void + { + $ingredients = $this->getParameters()['ingredients'] ?? []; + $sauces = $this->getParameters()['sauces'] ?? []; + $product = $this->getParameters()['product'] ?? ''; + + $this->bind('Sandwich', $product['size'] ?? ''); + $this->model = $this->container->make(FastFoodModel::class); + $sandwich = $this->model + ->createProduct(customBaseType:$product['type'] ?? '', customIngredients:$ingredients, customSauces:$sauces) + ->prepareProduct() + ->getProduct(); + + $this->data['burger'] = ''; + $this->data['sandwich'] = $sandwich; + $this->data['hotDog'] = ''; + $this->loadView('Template/header'); + $this->loadView('Default/index'); + $this->loadView('Template/footer'); + } +} \ No newline at end of file diff --git a/app/Http/Request/Request.php b/app/Http/Request/Request.php new file mode 100644 index 000000000..15e456201 --- /dev/null +++ b/app/Http/Request/Request.php @@ -0,0 +1,23 @@ + 200, + '400 Bad Request' => 400, + '404 Not found' => 404, + '500 Internal server Error' => 500, + ]; + + /** + * @param string $codeName + * @return int + */ + public static function getCode(string $codeName = ''): int + { + return self::$responseCodes[$codeName] ?? 0; + } + + /** + * @param int $code + * @return string|false + */ + public static function getMessage(int $code): string|false + { + $msg = array_search($code, self::$responseCodes); + return ($msg !== false) + ? $msg + : array_search(self::OK, self::$responseCodes); + } +} \ No newline at end of file diff --git a/app/Http/Response/IResponse.php b/app/Http/Response/IResponse.php new file mode 100644 index 000000000..01350a737 --- /dev/null +++ b/app/Http/Response/IResponse.php @@ -0,0 +1,8 @@ +prepareResponse($code, $message, $data); + extract($result); + echo sprintf("status: %s, code: %d, message: %s, data=" . print_r($data, true), $status, $code, $message); + } +} \ No newline at end of file diff --git a/app/Http/Response/ResponseHttp.php b/app/Http/Response/ResponseHttp.php new file mode 100644 index 000000000..93235becc --- /dev/null +++ b/app/Http/Response/ResponseHttp.php @@ -0,0 +1,28 @@ +prepareResponse($code, $message, $data); + print_r($result); + } +} \ No newline at end of file diff --git a/app/Http/Response/ResponseSelector.php b/app/Http/Response/ResponseSelector.php new file mode 100644 index 000000000..915774a92 --- /dev/null +++ b/app/Http/Response/ResponseSelector.php @@ -0,0 +1,27 @@ +getType(); + return match ($requestType) { + AppConst::REQUEST_TYPE_HTTP => new ResponseHttp(), + AppConst::REQUEST_TYPE_XHR => new ResponseXhr(), + AppConst::REQUEST_TYPE_CLI => new ResponseCli(), + }; + } +} \ No newline at end of file diff --git a/app/Http/Response/ResponseXhr.php b/app/Http/Response/ResponseXhr.php new file mode 100644 index 000000000..c3746f1b6 --- /dev/null +++ b/app/Http/Response/ResponseXhr.php @@ -0,0 +1,22 @@ +prepareResponse($code, $message, $data); + header('Content-Type: application/json'); + echo json_encode($result); + } +} \ No newline at end of file diff --git a/app/Http/Response/Traits/HasUtils.php b/app/Http/Response/Traits/HasUtils.php new file mode 100644 index 000000000..74e6939b1 --- /dev/null +++ b/app/Http/Response/Traits/HasUtils.php @@ -0,0 +1,23 @@ +pushHandler(new StreamHandler($logFile)); + $log->addRecord($level, $message, $context); + } +} \ No newline at end of file diff --git a/app/Models/FastFoodModel.php b/app/Models/FastFoodModel.php new file mode 100644 index 000000000..553e10a56 --- /dev/null +++ b/app/Models/FastFoodModel.php @@ -0,0 +1,38 @@ +order = $productOrder; + } + + public function createProduct(string $customBaseType = '', array $customIngredients = [], array $customSauces = []): IProductOrder + { + return $this->order + ->getOrder() + ->createProduct($customBaseType, $customIngredients, $customSauces); + } + + public function prepareProduct(ProductOrder $productOrder): ProductOrder + { + return $productOrder->prepareProduct(); + } + + public function getProduct(ProductOrder $productOrder): string + { + return $productOrder->getProduct(); + } +} \ No newline at end of file diff --git a/app/Models/IModel.php b/app/Models/IModel.php new file mode 100644 index 000000000..9c7bcdd60 --- /dev/null +++ b/app/Models/IModel.php @@ -0,0 +1,8 @@ +bind( + AbstractProductFactory::class, + function () use ($productName, $productSize) { + $factory = match ($productName) { + 'burger' => 'App\Services\Factories\ProductFactory\BurgerFactory', + 'sandwich' => 'App\Services\Factories\ProductFactory\SandwichFactory', + 'hotdog' => 'App\Services\Factories\ProductFactory\HotDogFactory', + default => 'FactoryDoesNotPresent' + }; + return new $factory($productSize); + } + ); + } + + /** + * Назначает провайдера уведомляющего об изменениях в процессе приготовления + * + * @param Container $container + */ + static public function bindNotificator(Container $container): void + { + $container->bind( + INotificator::class, + match ($_ENV['NOTIFICATOR']) { + 'email' => 'emailSender::Class', + 'sms' => 'smsSender::Class', + default => ScreenNotificator::class + } + ); + } +} \ No newline at end of file diff --git a/app/Services/Factories/ProductFactory/AbstractProductBase.php b/app/Services/Factories/ProductFactory/AbstractProductBase.php new file mode 100644 index 000000000..aa346cb90 --- /dev/null +++ b/app/Services/Factories/ProductFactory/AbstractProductBase.php @@ -0,0 +1,187 @@ + 'Готова', + 'wait' => 'Создан заказ', + 'prepare' => 'Готовится' + ]; + + const PRODUCT_BASE_NAME = ''; + + const COMPONENT_NAME = 'ОСНОВА'; + + + protected string $status = ''; + protected string $name = ''; + protected string $type = ''; + protected string $size = ''; + protected SplObjectStorage $observerList; + + + public function __construct() + { + $this->observerList = new SplObjectStorage(); + $this->name = static::PRODUCT_BASE_NAME; + } + + /** + * @return string + */ + public function getProductBase(): string + { + return $this->name . " '" . $this->getType() . "', размер: " . $this->size . " - " . $this->status . PHP_EOL; + } + + /** + * @return string + */ + public function getInfo(): string + { + return self::COMPONENT_NAME . ": " . $this->getProductBase(); + } + + /** + * @return AbstractProductBase + */ + public function addToRecipe(): AbstractProductBase + { + $this->setStatusWait(); + return $this; + } + + /** + * @return AbstractProductBase + */ + public function setStatusReady(): AbstractProductBase + { + // TODO установить в базе статус готов + $this->status = static::PRODUCT_BASE_STATUS['ready']; + $this->notify(); + return $this; + } + + /** + * @return AbstractProductBase + */ + public function setStatusWait(): AbstractProductBase + { + $this->status = self::PRODUCT_BASE_STATUS['wait']; + $this->notify(); + return $this; + } + + /** + * @return AbstractProductBase + */ + public function setStatusPrepare(): AbstractProductBase + { + $this->status = self::PRODUCT_BASE_STATUS['prepare']; + $this->notify(); + return $this; + } + + /** + * @return AbstractProductBase + */ + public function prepare(): AbstractProductBase + { + // TODO отправить на готовку + // TODO установить в базе статус приготовления + $this->setStatusPrepare(); + return $this; + } + + /** + * @return string + */ + public function getSize(): string + { + return $this->size; + } + + /** + * @param string $size + * @return AbstractProductBase + */ + public function setSize(string $size): AbstractProductBase + { + $this->size = $size; + return $this; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return AbstractProductBase + */ + public function setType(string $type): AbstractProductBase + { + $this->type = $type; + return $this; + } + + /** + * @return string + */ + public function getStatus(): string + { + return $this->status; + } + + /** + * @return string + */ + public function getName(): string + { + return $this->name; + } + + + /** + * @return array + */ + #[ArrayShape(['name' => "string", 'type' => "string", 'status' => "string"])] + public function toArray(): array + { + return [ + 'name' => $this->name, + 'type' => $this->type, + 'status' => $this->status + ]; + } + + public function attach(SplObserver $observer) + { + $this->observerList->attach($observer); + return $this; + } + + public function detach(SplObserver $observer) + { + $this->observerList->detach($observer); + return $this; + } + + public function notify() + { + foreach ($this->observerList as $observer) { + $observer->update($this); + } + } +} \ No newline at end of file diff --git a/app/Services/Factories/ProductFactory/AbstractProductFactory.php b/app/Services/Factories/ProductFactory/AbstractProductFactory.php new file mode 100644 index 000000000..9319831cc --- /dev/null +++ b/app/Services/Factories/ProductFactory/AbstractProductFactory.php @@ -0,0 +1,11 @@ +size = $size; + } + + /** + * @param string $type + * @return AbstractProductBase + */ + public function createBase(string $type): AbstractProductBase + { + if (empty($type)) { + $type = BurgerRecipe::getBase(); + } + return new BurgerBase($this->size, $type); + } + + /** + * @param array $customIngredientsList + * @return IIngredient + */ + public function createIngredients(array $customIngredientsList): IIngredient + { + $ingredientsList = array_unique(array_merge(BurgerRecipe::getIngredient(), $customIngredientsList)); + $ingredient = new Ingredient(); + foreach ($ingredientsList as $item => $type) { + $ingredient = match ($item) { + 'onion' => (new Onion($ingredient))->setType($type), + 'pepper' => (new Pepper($ingredient))->setType($type), + 'steak' => (new Steak($ingredient))->setType($type)->setStrategy(), + 'salad' => (new Salad($ingredient))->setType($type), + 'cheese' => (new Cheese($ingredient))->setType($type), + default => $ingredient + }; + } + return $ingredient; + } + + /** + * @param array $customSaucesList + * @return ISauce + */ + public function createSauces(array $customSaucesList): ISauce + { + $saucesList = array_unique(array_merge(BurgerRecipe::getSauces(), $customSaucesList)); + $sauce = new Sauce(); + foreach ($saucesList as $item => $type) { + $sauce = match ($item) { + 'ketchup' => (new Ketchup($sauce))->setType($type), + 'mayonnaise' => (new Mayonnaise($sauce))->setType($type), + 'tabasco' => (new Tabasco($sauce))->setType($type), + default => $sauce + }; + } + return $sauce; + } +} \ No newline at end of file diff --git a/app/Services/Factories/ProductFactory/HotDogFactory.php b/app/Services/Factories/ProductFactory/HotDogFactory.php new file mode 100644 index 000000000..b3404126a --- /dev/null +++ b/app/Services/Factories/ProductFactory/HotDogFactory.php @@ -0,0 +1,89 @@ +size = $size; + } + + /** + * @param string $type + * @return AbstractProductBase + */ + public function createBase(string $type): AbstractProductBase + { + if (empty($type)) { + $type = HotDogRecipe::getBase(); + } + return new HotDogBase($this->size, $type); + } + + /** + * @param array $customIngredientsList + * @return IIngredient + */ + public function createIngredients(array $customIngredientsList): IIngredient + { + $ingredientsList = array_unique(array_merge(HotDogRecipe::getIngredient(), $customIngredientsList)); + $ingredient = new Ingredient(); + foreach ($ingredientsList as $item => $type) { + $ingredient = match ($item) { + 'onion' => (new Onion($ingredient))->setType($type), + 'pepper' => (new Pepper($ingredient))->setType($type), + 'sausage' => (new Sausage($ingredient))->setType($type), + 'cheese' => (new Cheese($ingredient))->setType($type), + default => $ingredient + }; + } + return $ingredient; + } + + /** + * @param array $customSaucesList + * @return ISauce + */ + public function createSauces(array $customSaucesList): ISauce + { + $saucesList = array_unique(array_merge(HotDogRecipe::getSauces(), $customSaucesList)); + $sauce = new Sauce(); + foreach ($saucesList as $item => $type) { + $sauce = match ($item) { + 'ketchup' => (new Ketchup($sauce))->setType($type), + 'mayonnaise' => (new Mayonnaise($sauce))->setType($type), + 'tabasco' => (new Tabasco($sauce))->setType($type), + default => $sauce + }; + } + return $sauce; + } +} \ No newline at end of file diff --git a/app/Services/Factories/ProductFactory/IIngredient.php b/app/Services/Factories/ProductFactory/IIngredient.php new file mode 100644 index 000000000..f6f8f9527 --- /dev/null +++ b/app/Services/Factories/ProductFactory/IIngredient.php @@ -0,0 +1,11 @@ +size = $size; + } + + /** + * @param string $type + * @return AbstractProductBase + */ + public function createBase(string $type): AbstractProductBase + { + if (empty($type)) { + $type = SandwichRecipe::getBase(); + } + return new SandwichBase($this->size, $type); + } + + /** + * @param array $customIngredientsList + * @return IIngredient + */ + public function createIngredients(array $customIngredientsList): IIngredient + { + $ingredientsList = array_unique(array_merge(SandwichRecipe::getIngredient(), $customIngredientsList)); + $ingredient = new Ingredient(); + foreach ($ingredientsList as $item => $type) { + $ingredient = match ($item) { + 'onion' => (new Onion($ingredient))->setType($type), + 'pepper' => (new Pepper($ingredient))->setType($type), + 'cutlet' => (new Cutlet($ingredient))->setType($type), + 'salad' => (new Salad($ingredient))->setType($type), + 'cheese' => (new Cheese($ingredient))->setType($type), + default => $ingredient + }; + } + return $ingredient; + } + + /** + * @param array $customSaucesList + * @return ISauce + */ + public function createSauces(array $customSaucesList): ISauce + { + $saucesList = array_unique(array_merge(SandwichRecipe::getSauces(), $customSaucesList)); + $sauce = new Sauce(); + foreach ($saucesList as $item => $type) { + $sauce = match ($item) { + 'ketchup' => (new Ketchup($sauce))->setType($type), + 'mayonnaise' => (new Mayonnaise($sauce))->setType($type), + 'tabasco' => (new Tabasco($sauce))->setType($type), + default => $sauce + }; + } + return $sauce; + } +} \ No newline at end of file diff --git a/app/Services/Helpers/Recipes/BurgerRecipe.php b/app/Services/Helpers/Recipes/BurgerRecipe.php new file mode 100644 index 000000000..4cb2dc32b --- /dev/null +++ b/app/Services/Helpers/Recipes/BurgerRecipe.php @@ -0,0 +1,56 @@ + 'Средняя прожарка', + 'cheese' => 'Чеддер', + 'onion' => 'Зеленый репчатый', + ]; + + private static array $sauces = [ + 'ketchup' => 'Heinz', + 'mayonnaise' => 'Московский провансаль', + ]; + + + /** + * @return string + */ + public static function getSize(): string + { + return self::$size; + } + + /** + * @return string + */ + public static function getBase(): string + { + return self::$base; + } + + /** + * @return array + */ + public static function getIngredient(): array + { + return self::$ingredients; + } + + /** + * @return array + */ + public static function getSauces(): array + { + return self::$sauces; + } +} \ No newline at end of file diff --git a/app/Services/Helpers/Recipes/HotDogRecipe.php b/app/Services/Helpers/Recipes/HotDogRecipe.php new file mode 100644 index 000000000..d840b539c --- /dev/null +++ b/app/Services/Helpers/Recipes/HotDogRecipe.php @@ -0,0 +1,53 @@ + 'Молочная', + 'cheese' => 'Гауда', + ]; + + private static array $sauces = [ + 'ketchup' => 'Heinz', + 'mayonnaise' => 'Оливковый', + ]; + + + /** + * @return string + */ + public static function getSize(): string + { + return self::$size; + } + + /** + * @return string + */ + public static function getBase(): string + { + return self::$base; + } + + /** + * @return array + */ + public static function getIngredient(): array + { + return self::$ingredients; + } + + /** + * @return array + */ + public static function getSauces(): array + { + return self::$sauces; + } +} \ No newline at end of file diff --git a/app/Services/Helpers/Recipes/SandwichRecipe.php b/app/Services/Helpers/Recipes/SandwichRecipe.php new file mode 100644 index 000000000..e40c6e753 --- /dev/null +++ b/app/Services/Helpers/Recipes/SandwichRecipe.php @@ -0,0 +1,55 @@ + 'Говядина', + 'onion' => 'Красный', + 'cheese' => 'Чеддер', + ]; + + private static array $sauces = [ + 'ketchup' => 'Heinz', + ]; + + + /** + * @return string + */ + public static function getSize(): string + { + return self::$size; + } + + /** + * @return string + */ + public static function getBase(): string + { + return self::$base; + } + + /** + * @return array + */ + public static function getIngredient(): array + { + return self::$ingredients; + } + + /** + * @return array + */ + public static function getSauces(): array + { + return self::$sauces; + } +} \ No newline at end of file diff --git a/app/Services/Notifications/ScreenNotificator.php b/app/Services/Notifications/ScreenNotificator.php new file mode 100644 index 000000000..049b79cec --- /dev/null +++ b/app/Services/Notifications/ScreenNotificator.php @@ -0,0 +1,18 @@ +$message"; + } +} \ No newline at end of file diff --git a/app/Services/Observer/INotificator.php b/app/Services/Observer/INotificator.php new file mode 100644 index 000000000..4c3d7d7ef --- /dev/null +++ b/app/Services/Observer/INotificator.php @@ -0,0 +1,8 @@ +notificator = $notificator; + } + + /** + * @param AbstractProductBase|SplSubject $subject + */ + public function update(AbstractProductBase|SplSubject $subject) + { + $message = $subject->getInfo() . PHP_EOL; + $this->notificator->send($message); + } +} \ No newline at end of file diff --git a/app/Services/Orders/IProductOrder.php b/app/Services/Orders/IProductOrder.php new file mode 100644 index 000000000..860cfa878 --- /dev/null +++ b/app/Services/Orders/IProductOrder.php @@ -0,0 +1,11 @@ +factory = $factory; + $this->observer = $observer; + } + + /** + * @return $this + */ + public function getOrder(): ProductOrder + { + //todo сформировать заказ + return $this; + } + + /** + * @return $this + */ + public function createProduct(string $baseType = '', array $ingredientsList = [], array $saucesList =[]): ProductOrder + { + return $this->createBase($baseType) + ->createIngredients($ingredientsList) + ->createSauces($saucesList); + } + + /** + * @param string $type + * @return $this + */ + public function createBase(string $type): ProductOrder + { + $this->base = $this->factory->createBase($type); + $this->base->attach($this->observer); + $this->base->addToRecipe(); + return $this; + } + + /** + * @param array $ingredientsList + * @return $this + */ + private function createIngredients(array $ingredientsList): ProductOrder + { + $this->ingredients = $this->factory->createIngredients($ingredientsList); + $this->ingredients->attach($this->observer); + $this->ingredients->addToRecipe(); + return $this; + } + + /** + * @param array $sauces + * @return $this + */ + private function createSauces(array $sauces): ProductOrder + { + $this->sauces = $this->factory->createSauces($sauces); + $this->sauces->attach($this->observer); + $this->sauces->addToRecipe(); + return $this; + } + + /** + * @return $this + */ + public function prepareProduct(): self + { + $this->base + ->prepare() + ->setStatusReady(); + $this->ingredients + ->prepare() + ->addToProduct() + ->toArray(); + $this->sauces + ->addToProduct() + ->toArray(); + return $this; + } + + /** + * @return string + */ + public function getProduct(): string + { + $result = $this->base->getProductBase(); + $result .= "Ингредиенты: " . $this->ingredients->getIngredients(); + $result .= "Соусы: " . $this->sauces->getSauces(); + return $result; + } +} \ No newline at end of file diff --git a/app/Services/Orders/ProxyProductOrder.php b/app/Services/Orders/ProxyProductOrder.php new file mode 100644 index 000000000..405267a0e --- /dev/null +++ b/app/Services/Orders/ProxyProductOrder.php @@ -0,0 +1,58 @@ +productOrder = $productOrder; + } + + /** + * @return IProductOrder + */ + public function getOrder(): IProductOrder + { + return $this; + } + + /** + * @return string + * @throws InvalidProductOrderException + */ + public function getProduct(): string + { + if ($this->checkQuality()) { + return $this->productOrder->getProduct(); + } else { + // TODO: произвести изменения в базе + throw new InvalidProductOrderException($this->productOrder->getProduct() . " УТИЛИЗИРОВАН!", ErrorCodes::getCode(InvalidProductOrderException::class)); + } + } + + public function createProduct(string $baseType = '', array $ingredientsList = [], array $saucesList = []): IProductOrder + { + $this->productOrder->createProduct($baseType, $ingredientsList, $saucesList); + return $this; + } + + public function prepareProduct(): ProxyProductOrder + { + $this->productOrder->prepareProduct(); + return $this; + } + + private function checkQuality(): bool + { + return (float)$_ENV['PROBABILITY_PRODUCT_DEFECT'] === 0.0 || (bool)rand(0, (100 / (float)$_ENV['PROBABILITY_PRODUCT_DEFECT']) - 1); + } +} \ No newline at end of file diff --git a/app/Services/Products/Base/BurgerBase.php b/app/Services/Products/Base/BurgerBase.php new file mode 100644 index 000000000..0889a554a --- /dev/null +++ b/app/Services/Products/Base/BurgerBase.php @@ -0,0 +1,23 @@ +size = $size; + $this->type = $type; + parent::__construct(); + } +} \ No newline at end of file diff --git a/app/Services/Products/Base/HotDogBase.php b/app/Services/Products/Base/HotDogBase.php new file mode 100644 index 000000000..6af3c49a8 --- /dev/null +++ b/app/Services/Products/Base/HotDogBase.php @@ -0,0 +1,23 @@ +size = $size; + $this->type = $type; + parent::__construct(); + } +} \ No newline at end of file diff --git a/app/Services/Products/Base/SandwichBase.php b/app/Services/Products/Base/SandwichBase.php new file mode 100644 index 000000000..1646141e4 --- /dev/null +++ b/app/Services/Products/Base/SandwichBase.php @@ -0,0 +1,23 @@ +size = $size; + $this->type = $type; + parent::__construct(); + } +} \ No newline at end of file diff --git a/app/Services/Products/Ingredients/Cheese.php b/app/Services/Products/Ingredients/Cheese.php new file mode 100644 index 000000000..def133cc7 --- /dev/null +++ b/app/Services/Products/Ingredients/Cheese.php @@ -0,0 +1,51 @@ +ingredient = $ingredient; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return Cheese + */ + public function setType(string $type): Cheese + { + $this->type = $type; + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $this->ingredientsList = $this->ingredient->toArray(); + $this->ingredientsList['Cheese'] = parent::ingredientToArray(); + return $this->ingredientsList; + } +} \ No newline at end of file diff --git a/app/Services/Products/Ingredients/Cutlet.php b/app/Services/Products/Ingredients/Cutlet.php new file mode 100644 index 000000000..c9cdec062 --- /dev/null +++ b/app/Services/Products/Ingredients/Cutlet.php @@ -0,0 +1,50 @@ +ingredient = $ingredient; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return Cutlet + */ + public function setType(string $type): Cutlet + { + $this->type = $type; + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $this->ingredientsList = $this->ingredient->toArray(); + $this->ingredientsList['Cutlet'] = parent::ingredientToArray(); + return $this->ingredientsList; + } +} \ No newline at end of file diff --git a/app/Services/Products/Ingredients/Ingredient.php b/app/Services/Products/Ingredients/Ingredient.php new file mode 100644 index 000000000..cc0228e76 --- /dev/null +++ b/app/Services/Products/Ingredients/Ingredient.php @@ -0,0 +1,224 @@ + 'Добавлен', + 'wait' => 'Необходимо добавить', + 'prepare' => 'Готовится' + ]; + + const INGREDIENT_NAME = ''; + + const COMPONENT_NAME = 'ИНГРЕДИЕНТ'; + + /** + * Набор ингредиентов для продукта + * ['название ингредиента' => 'Статус'] + * + * Например: + * ['Onion' => 'В составе'] + * + * @var array + */ + protected array $ingredientsList = []; + + /** + * Список подключенных слушателей + * + * @var SplObjectStorage + */ + protected SplObjectStorage $observerList; + + protected string $status = ''; + protected string $name = ''; + protected string $type = ''; + + /** + * Объект для обертывания (Decorator) + * + * @var IIngredient|null + */ + protected ?IIngredient $ingredient = null; + + + public function __construct() + { + $this->observerList = new SplObjectStorage(); + $this->name = static::INGREDIENT_NAME; + } + + /** + * Возвращает все ингредиенты + * + * @return string + */ + public function getIngredients(): string + { + $last = array_key_last($this->ingredientsList); + return array_reduce( + array_map( + static fn($key, $item) => ($key !== $last) + ? $item['name'] . " '" . $item['type'] . "' - " . $item['status'] . ", " + : $item['name'] . " '" . $item['type'] . "' - " . $item['status'] . PHP_EOL, + array_keys($this->ingredientsList), $this->ingredientsList + ), + static fn($carry, $item) => $carry .= $item, + '' + ); + } + + /** + * Возвращает информацию о конкретном текущем ингредиенте + * + * @return string + */ + public function getInfo(): string + { + return self::COMPONENT_NAME . ": " . $this->name . " '" . $this->type . "' - " . $this->status; + } + + /** + * @return IIngredient + */ + public function addToRecipe(): IIngredient + { + if (is_null($this->ingredient)) { + return $this; + } + $this->ingredient->addToRecipe(); + $this->setStatusWait(); + return $this; + } + + /** + * @return IIngredient + */ + public function addToProduct(): IIngredient + { + if (is_null($this->ingredient)) return $this; + $this->ingredient->addToProduct(); + $this->setStatusReady(); + return $this; + } + + /** + * @return IIngredient + */ + public function setStatusReady(): IIngredient + { + // TODO установить в базе статус готов + $this->status = static::INGREDIENT_STATUS['ready']; + $this->notify(); + return $this; + } + + /** + * @return IIngredient + */ + public function setStatusWait(): IIngredient + { + $this->status = self::INGREDIENT_STATUS['wait']; + $this->notify(); + return $this; + } + + /** + * @return IIngredient + */ + public function setStatusPrepare(): IIngredient + { + $this->status = self::INGREDIENT_STATUS['prepare']; + $this->notify(); + return $this; + } + + /** + * @return IIngredient + */ + public function prepare(): IIngredient + { + if (is_null($this->ingredient)) return $this; + $this->ingredient->prepare(); + // TODO отправить на готовку + // TODO установить в базе статус приготовления + $this->setStatusPrepare(); + sleep(0); + return $this; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return IIngredient + */ + public function setType(string $type): IIngredient + { + $this->type = $type; + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + return []; + } + + /** + * @return array + */ + #[ArrayShape(['name' => "string", 'type' => "string", 'status' => "string"])] + public function ingredientToArray(): array + { + return [ + 'name' => $this->name, + 'type' => $this->type, + 'status' => $this->status + ]; + } + + public function attach(SplObserver $observer) + { + if (is_null($this->ingredient)) { + return $this; + } + $this->ingredient->attach($observer); + $this->observerList->attach($observer); + return $this; + } + + public function detach(SplObserver $observer) + { + if (is_null($this->ingredient)) { + return $this; + } + $this->ingredient->detach($observer); + $this->observerList->detach($observer); + return $this; + } + + public function notify() + { + foreach ($this->observerList as $observer) { + $observer->update($this); + } + } +} \ No newline at end of file diff --git a/app/Services/Products/Ingredients/Onion.php b/app/Services/Products/Ingredients/Onion.php new file mode 100644 index 000000000..58d4126bd --- /dev/null +++ b/app/Services/Products/Ingredients/Onion.php @@ -0,0 +1,50 @@ +ingredient = $ingredient; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return Onion + */ + public function setType(string $type): Onion + { + $this->type = $type; + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $this->ingredientsList = $this->ingredient->toArray(); + $this->ingredientsList['Onion'] = parent::ingredientToArray(); + return $this->ingredientsList; + } +} \ No newline at end of file diff --git a/app/Services/Products/Ingredients/Pepper.php b/app/Services/Products/Ingredients/Pepper.php new file mode 100644 index 000000000..9a1419484 --- /dev/null +++ b/app/Services/Products/Ingredients/Pepper.php @@ -0,0 +1,33 @@ +ingredient = $ingredient; + } + + + /** + * @return array + */ + public function toArray(): array + { + $this->ingredientsList = $this->ingredient->toArray(); + $this->ingredientsList['Pepper'] = parent::ingredientToArray(); + return $this->ingredientsList; + } +} \ No newline at end of file diff --git a/app/Services/Products/Ingredients/Salad.php b/app/Services/Products/Ingredients/Salad.php new file mode 100644 index 000000000..f16ef9b61 --- /dev/null +++ b/app/Services/Products/Ingredients/Salad.php @@ -0,0 +1,51 @@ +ingredient = $ingredient; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return Salad + */ + public function setType(string $type): Salad + { + $this->type = $type; + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $this->ingredientsList = $this->ingredient->toArray(); + $this->ingredientsList['Salad'] = parent::ingredientToArray(); + return $this->ingredientsList; + } +} \ No newline at end of file diff --git a/app/Services/Products/Ingredients/Sausage.php b/app/Services/Products/Ingredients/Sausage.php new file mode 100644 index 000000000..624ebae64 --- /dev/null +++ b/app/Services/Products/Ingredients/Sausage.php @@ -0,0 +1,51 @@ +ingredient = $ingredient; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return Sausage + */ + public function setType(string $type): Sausage + { + $this->type = $type; + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $this->ingredientsList = $this->ingredient->toArray(); + $this->ingredientsList['Sausage'] = parent::ingredientToArray(); + return $this->ingredientsList; + } +} \ No newline at end of file diff --git a/app/Services/Products/Ingredients/Steak.php b/app/Services/Products/Ingredients/Steak.php new file mode 100644 index 000000000..d8eb23bf8 --- /dev/null +++ b/app/Services/Products/Ingredients/Steak.php @@ -0,0 +1,89 @@ +ingredient = $ingredient; + } + + /** + * @return $this + * @throws InvalidSteakStrategyException + */ + public function prepare(): Steak + { + parent::prepare(); + if (!$this->strategy) { + throw new InvalidSteakStrategyException("Не выбрана степень прожарки", ErrorCodes::getCode(InvalidSteakStrategyException::class)); + } + echo $this->strategy->prepare(); + return $this; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return Steak + */ + public function setType(string $type): Steak + { + $this->type = $type; + return $this; + } + + /** + * @return $this + */ + public function setStrategy(): Steak + { + $this->strategy = match ($this->type) { + self::MIDDLE_ROASTING => new SteakMiddle(), + self::SOFT_ROASTING => new SteakSoft(), + self::STRONG_ROASTING => new SteakHard(), + default => null, + }; + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $this->ingredientsList = $this->ingredient->toArray(); + $this->ingredientsList['Steak'] = parent::ingredientToArray(); + return $this->ingredientsList; + } +} \ No newline at end of file diff --git a/app/Services/Products/Sauces/Ketchup.php b/app/Services/Products/Sauces/Ketchup.php new file mode 100644 index 000000000..ffa2dd3f3 --- /dev/null +++ b/app/Services/Products/Sauces/Ketchup.php @@ -0,0 +1,51 @@ +sauce = $sauce; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return Ketchup + */ + public function setType(string $type): Ketchup + { + $this->type = $type; + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $this->saucesList = $this->sauce->toArray(); + $this->saucesList['Ketchup'] = parent::sauceToArray(); + return $this->saucesList; + } +} \ No newline at end of file diff --git a/app/Services/Products/Sauces/Mayonnaise.php b/app/Services/Products/Sauces/Mayonnaise.php new file mode 100644 index 000000000..ff54eb902 --- /dev/null +++ b/app/Services/Products/Sauces/Mayonnaise.php @@ -0,0 +1,51 @@ +sauce = $sauce; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return Mayonnaise + */ + public function setType(string $type): Mayonnaise + { + $this->type = $type; + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $this->saucesList = $this->sauce->toArray(); + $this->saucesList['Mayonnaise'] = parent::sauceToArray(); + return $this->saucesList; + } +} \ No newline at end of file diff --git a/app/Services/Products/Sauces/Sauce.php b/app/Services/Products/Sauces/Sauce.php new file mode 100644 index 000000000..6b175e8bc --- /dev/null +++ b/app/Services/Products/Sauces/Sauce.php @@ -0,0 +1,179 @@ + 'Добавлен', + 'wait' => 'Необходимо добавить', + ]; + + const SAUCE_NAME = ''; + + /** + * Набор ингредиентов для продукта + * ['название ингредиента' => 'Статус'] + * + * Например: + * ['Onion' => 'В составе'] + * + * @var array + */ + protected array $saucesList = []; + + /** + * Объект для обертывания (Decorator) + * + * @var ISauce|null + */ + protected ?ISauce $sauce = null; + + /** + * Список подключенных слушателей + * + * @var SplObjectStorage + */ + private SplObjectStorage $observerList; + + protected string $status = ''; + protected string $name = ''; + protected string $type = ''; + + + public function __construct() + { + $this->observerList = new SplObjectStorage(); + $this->name = static::SAUCE_NAME; + } + + /** + * @return string + */ + public function getSauces(): string + { + $last = array_key_last($this->saucesList); + return array_reduce( + array_map( + static fn($key, $item) => ($key !== $last) + ? $item['name'] . " '" . $item['type'] . "' - " . $item['status'] . ", " + : $item['name'] . " '" . $item['type'] . "' - " . $item['status'] . PHP_EOL, + array_keys($this->saucesList), $this->saucesList + ), + static fn($carry, $item) => $carry .= $item, + '' + ); + } + + /** + * Возвращает информацию о конкретном текущем ингредиенте + * + * @return string + */ + public function getInfo(): string + { + return self::COMPONENT_NAME . ": " . $this->name . " '" . $this->type . "' - " . $this->status; + } + + /** + * @return self + */ + public function addToRecipe(): self + { + if (is_null($this->sauce)) { + return $this; + } + $this->sauce->addToRecipe(); + $this->setStatusWait(); + return $this; + } + + /** + * @return self + */ + public function addToProduct(): self + { + if (is_null($this->sauce)) return $this; + $this->sauce->addToProduct(); + $this->setStatusReady(); + return $this; + } + + /** + * @return ISauce + */ + public function setStatusReady(): ISauce + { + $this->status = self::SAUCE_STATUS['ready']; + $this->notify(); + return $this; + } + + /** + * @return ISauce + */ + public function setStatusWait(): ISauce + { + $this->status = self::SAUCE_STATUS['wait']; + $this->notify(); + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + return []; + } + + /** + * @return array + */ + #[ArrayShape(['name' => "string", 'type' => "string", 'status' => "string"])] + public function sauceToArray(): array + { + return [ + 'name' => $this->name, + 'type' => $this->type, + 'status' => $this->status + ]; + } + + public function attach(SplObserver $observer) + { + if (is_null($this->sauce)) { + return $this; + } + $this->sauce->attach($observer); + $this->observerList->attach($observer); + return $this; + } + + public function detach(SplObserver $observer) + { + if (is_null($this->sauce)) { + return $this; + } + $this->sauce->detach($observer); + $this->observerList->detach($observer); + return $this; + } + + public function notify() + { + foreach ($this->observerList as $observer) { + $observer->update($this); + } + } +} \ No newline at end of file diff --git a/app/Services/Products/Sauces/Tabasco.php b/app/Services/Products/Sauces/Tabasco.php new file mode 100644 index 000000000..d186e4302 --- /dev/null +++ b/app/Services/Products/Sauces/Tabasco.php @@ -0,0 +1,51 @@ +sauce = $sauce; + } + + /** + * @return string + */ + public function getType(): string + { + return $this->type; + } + + /** + * @param string $type + * @return Tabasco + */ + public function setType(string $type): Tabasco + { + $this->type = $type; + return $this; + } + + /** + * @return array + */ + public function toArray(): array + { + $this->saucesList = $this->sauce->toArray(); + $this->saucesList['Tabasco'] = parent::sauceToArray(); + return $this->saucesList; + } +} \ No newline at end of file diff --git a/app/Services/Strategy/CookingTechnology/ISteakStrategy.php b/app/Services/Strategy/CookingTechnology/ISteakStrategy.php new file mode 100644 index 000000000..d88199e95 --- /dev/null +++ b/app/Services/Strategy/CookingTechnology/ISteakStrategy.php @@ -0,0 +1,9 @@ +load(); +//$dbConf = require __DIR__ . '/../config/database.php'; diff --git a/composer.json b/composer.json new file mode 100644 index 000000000..340775d19 --- /dev/null +++ b/composer.json @@ -0,0 +1,25 @@ +{ + "name": "vladi/home-work", + "description": "Patterns for fast-food", + "type": "project", + "require": { + "php": ">=8.0", + "vlucas/phpdotenv": "^5.1", + "monolog/monolog": "2.x-dev", + "illuminate/container": "9.x-dev" + }, + "authors": [ + { + "name": "NightWizard75", + "email": "vladimir@valyakin.ru" + } + ], + "autoload": { + "psr-4": { + "App\\" : "app", + "Resources\\" : "resources", + "Routes\\" : "routes" + } + }, + "minimum-stability": "dev" +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 000000000..15887b660 --- /dev/null +++ b/composer.lock @@ -0,0 +1,836 @@ +{ + "_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": "121f450491db6817fcd6d4a0a08e28e5", + "packages": [ + { + "name": "graham-campbell/result-type", + "version": "1.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "cce288e91826d6d33d76b57f1ad4bdc3f3a8c1d6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/cce288e91826d6d33d76b57f1ad4bdc3f3a8c1d6", + "reference": "cce288e91826d6d33d76b57f1ad4bdc3f3a8c1d6", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "phpoption/phpoption": "^1.7.3" + }, + "require-dev": { + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.7" + }, + "default-branch": true, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "graham@alt-three.com" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/1.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2021-01-25T20:12:13+00:00" + }, + { + "name": "illuminate/container", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/illuminate/container.git", + "reference": "03d661c82297b59fb0715d75f00b16cc1872c8c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/container/zipball/03d661c82297b59fb0715d75f00b16cc1872c8c9", + "reference": "03d661c82297b59fb0715d75f00b16cc1872c8c9", + "shasum": "" + }, + "require": { + "illuminate/contracts": "^9.0", + "php": "^8.0.2", + "psr/container": "^1.1.1|^2.0.1" + }, + "provide": { + "psr/container-implementation": "1.0" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Container\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Container package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-09-29T09:16:52+00:00" + }, + { + "name": "illuminate/contracts", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "13f68fbfc0996dd224a420f0266064c2d309ce2b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/13f68fbfc0996dd224a420f0266064c2d309ce2b", + "reference": "13f68fbfc0996dd224a420f0266064c2d309ce2b", + "shasum": "" + }, + "require": { + "php": "^8.0.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2021-10-12T13:12:37+00:00" + }, + { + "name": "monolog/monolog", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "f2156cdd55f960432c332d4da1cd53c7fccf075f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f2156cdd55f960432c332d4da1cd53c7fccf075f", + "reference": "f2156cdd55f960432c332d4da1cd53c7fccf075f", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "psr/log": "^1.0.1" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9 || ^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7", + "graylog2/gelf-php": "^1.4.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpspec/prophecy": "^1.6.1", + "phpstan/phpstan": "^0.12.91", + "phpunit/phpunit": "^8.5", + "predis/predis": "^1.1", + "rollbar/rollbar": "^1.3", + "ruflin/elastica": ">=0.90 <7.0.1", + "swiftmailer/swiftmailer": "^5.3|^6.0" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/main" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2021-08-17T12:13:55+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525", + "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525", + "shasum": "" + }, + "require": { + "php": "^5.5.9 || ^7.0 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Graham Campbell", + "email": "graham@alt-three.com" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.7.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2020-07-20T17:29:33+00:00" + }, + { + "name": "psr/container", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "2ae37329ee82f91efadc282cc2d527fd6065a5ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/2ae37329ee82f91efadc282cc2d527fd6065a5ef", + "reference": "2ae37329ee82f91efadc282cc2d527fd6065a5ef", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.1" + }, + "time": "2021-03-24T13:40:57+00:00" + }, + { + "name": "psr/log", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, + "time": "2021-05-03T11:20:27+00:00" + }, + { + "name": "psr/simple-cache", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "66e27efc65ddef47d3008c243a235ab9359b5754" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/66e27efc65ddef47d3008c243a235ab9359b5754", + "reference": "66e27efc65ddef47d3008c243a235ab9359b5754", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/master" + }, + "time": "2021-10-06T11:02:22+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-02-19T12:13:01+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", + "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-05-27T12:26:48+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "dev-main", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", + "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-07-28T13:41:28+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "b83be80cedb862eeb030370cd7939f8333b1c496" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/b83be80cedb862eeb030370cd7939f8333b1c496", + "reference": "b83be80cedb862eeb030370cd7939f8333b1c496", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.1", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.7.4", + "symfony/polyfill-ctype": "^1.17", + "symfony/polyfill-mbstring": "^1.17", + "symfony/polyfill-php80": "^1.17" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.14 || ^9.5.1" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.3-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "graham@alt-three.com", + "homepage": "https://gjcampbell.co.uk/" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://vancelucas.com/" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/master" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2021-05-23T15:53:44+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": { + "monolog/monolog": 20, + "illuminate/container": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.0" + }, + "platform-dev": [], + "plugin-api-version": "2.1.0" +} diff --git a/cs.sh.save b/cs.sh.save new file mode 100644 index 000000000..220ac4847 --- /dev/null +++ b/cs.sh.save @@ -0,0 +1,17 @@ +#!/bin/sh + +EXPECTED_CHECKSUM="$(wget -q -O - https://composer.github.io/installer.sig)" +php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" +ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" + +if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ] +then + >&2 echo 'ERROR: Invalid installer checksum' + rm composer-setup.php + exit 1 +fi + +php composer-setup.php --quiet --/usr/bin /usr/bin --filename composer +RESULT=$? +rm composer-setup.php +exit $RESULT diff --git a/index.php b/index.php new file mode 100644 index 000000000..6fd199e66 --- /dev/null +++ b/index.php @@ -0,0 +1 @@ + +