diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..43b709b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..b69d353 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,35 @@ +# Name of workflow +name: PHP CI + +# Trigger the workflow on push or pull request +on: + - push + - pull_request + +jobs: + # build – произвольно выбранное имя задания + # их может быть больше одного + build: + # The type of machine to run the job on + runs-on: ubuntu-latest + + steps: # список шагов, которые надо выполнить + # экшен — выполняет какую-то задачу + # checkout — клонирует репозиторий + # Check-out repository under GitHub workspace + # https://github.com/actions/checkout + - uses: actions/checkout@v4 + # Step's name + - name: Setup PHP + # Action gives to setup the PHP environment to test application + # https://github.com/shivammathur/setup-php + uses: shivammathur/setup-php@v2 + with: + # Specify the PHP version + php-version: '8.3' + # Install project + - name: Install project + run: make install + # Run linter + - name: Run linter + run: make lint diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0cc04d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/vendor/ +.idea +.DS_Store +.vscode diff --git a/Description.md b/Description.md new file mode 100644 index 0000000..edbbbf3 --- /dev/null +++ b/Description.md @@ -0,0 +1,64 @@ +## Задача, которую решает код + +Есть список животных: +- Bird +- Dog +- Cat +- Elephant +- Rhino +- Lion +- Squirrel + +Каждое животное занимает какой-то объем: +- Bird = 0.25 +- Dog = 0.4 +- Cat = ? +- Elephant = 0.6 +- Rhino = 0.6 +- Lion = ? +- Squirrel = ? + +А так же список помещений: +- Hall +- Bedroom +- Bathroom +- Balcony + +У каждого помещения есть вместимость (объем), которое задается при создании каждого типа помещения, т.е. может меняться. + +> Необходимо разместить список животных в списке помещений по определенной логике. +> Определенные типы животных должны быть размещены только в определенных типах помещений, а не в любых. + +Функция принимает на вход массив животных и массив помещений, в которых этих животных надо разместить: +``` +arrangeAnimalsAmongRooms(Animal[] animals, Room[] rooms) +``` + +Какие животные в каких помещениях должны быть размещены: +- Bird => Hall +- Dog => Bedroom +- Cat => ? +- Elephant => Balcony +- Rhino => Bedroom +- Lion => ? +- Squirrel => ? + +## Замечания после изучения кода +- Не все типы животных обрабатываются. Нет условий для Cat, Lion, Squirrel. Т.е. они никогда не будут размещены +- Как следствие из предыдущего пункта, невозможно понять какой объем занимают Cat, Lion, Squirrel и в какие помещения они должны быть размещены +- После размещения некоторых типов животных, например Dog и Rhino, выбрасывается исключение, что останавливает выполнение программы и размещение остальных животных из массива не будет произведено +- При размещении животных, `capacity` у помещений проверяется некорректно, т.к. оно сравнивается лишь с 0, хотя после размещения capacity может уйти в минус +- При размещении Elephant в консоль выводится сообщение `A new elephant just arrived`, хотя при размещении других животных никаких сообщений не выводится, что странно +- При размещении животных Dog, Rhino присутствует `break`, т.о. эти животные будут размещены только в одном помещении нужного типа. А логика размещения животных Bird, Elephant не содержит `break`, т.о. получится, что одно и то же животное будет одновременно размещено в разных помещениях одного типа, что физически невозможно +- При размещении Dog, вне зависимости от того нашлось помещение для собаки или нет, то выбрасывается исключение с текстом `Dog(s) can be arranged`, говорящее о том, что с собакой можно разместиться, хотя это может быть не так. Это из-за того, что забыли сделать `foundRoom = true;`. И сам текст исключения не корректный +- При размещении Rhino текст исключения `Rhinos(s) can be arranged` не корректный. Должно быть написано, что носороги НЕ могут быть размещены +- Перебор массивов объектов лучше делать через foreach, тогда можно будет не вычислять длину этих массивов, а просто перебирать все элементы +- Так же не понятно какой итоговый результат мы получаем после вызова функции и как это проверить. Нам же надо наверняка разместить именно всех животных. Если так, то тогда выбрасывание исключений оправдано. + +## Каким должен быть код +- Универсальным и легко расширяемым, соответствующий принципам SOLID, без дублирований +- Мы должны иметь возможность легко и быстро добавлять как новые типы животных так и новые виды помещений, без изменения ранее написанного кода +- Логика, свойства и параметры каждого вида животного и помещений должны быть инкапсули́рованы только в этих объектах +- Т.к. каждый вид животного, в свою очередь, может быть разного размера, то логично, что он может занимать и разный объем в помещении, поэтому сделаем возможность указывать объем каждого животного при его создании +- Так же нам нужна связь жесткая животного с помещением, в котором его можно размещать. Поэтому этот признак так же будет указан у каждого вида животного. +- Еще хорошо бы написать Unit-тесты diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..542a29f --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +install: + composer install + +run: + php public/index.php + +validate: + composer validate + +lint: + composer exec -v phpcs src + vendor/bin/phpstan analyse + +lint-fix: + composer exec -v phpcbf src + +.PHONY: tests diff --git a/README.md b/README.md index f4124f7..342d7cc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,22 @@ +### Project tests and linter statuses: +[![Main](https://github.com/NONstop5/omcode_test/actions/workflows/main.yml/badge.svg)](https://github.com/NONstop5/omcode_test/actions) + # Тестовое в компанию Omcode ## Задание - Изучить предоставленный код на Java, понять по коду какую задачу решал разработчик. - Переписать сначала код на PHP в исходном виде - Предложить более удачную реализацию, при необходимости + +### Requirements +- PHP >= 8.3 +- Composer >= 2 +- make >= 4 + +### Installation +> `git clone git@github.com:NONstop5/omcode_test.git` +> +> `make install` + +### Launch +> `make run` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..f273180 --- /dev/null +++ b/composer.json @@ -0,0 +1,25 @@ +{ + "name": "igor/omcode_test", + "description": "omcode_test project", + "type": "project", + "license": "MIT", + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "authors": [ + { + "name": "IgorIlyaguev", + "email": "non_2002@mail.ru" + } + ], + "require": { + "php": ">=8.3" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^3.12", + "symfony/var-dumper": "^7.2", + "phpstan/phpstan": "^2.1" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..19f7913 --- /dev/null +++ b/composer.lock @@ -0,0 +1,326 @@ +{ + "_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": "29b44e6598d6d06403b22462ec575dd1", + "packages": [], + "packages-dev": [ + { + "name": "phpstan/phpstan", + "version": "2.1.11", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8ca5f79a8f63c49b2359065832a654e1ec70ac30", + "reference": "8ca5f79a8f63c49b2359065832a654e1ec70ac30", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2025-03-24T13:45:00+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.12.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "ea16a1f3719783345febd3aab41beb55c8c84bfd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ea16a1f3719783345febd3aab41beb55c8c84bfd", + "reference": "ea16a1f3719783345febd3aab41beb55c8c84bfd", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-04-04T12:57:55+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "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.31.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": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v7.2.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "82b478c69745d8878eb60f9a049a4d584996f73a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/82b478c69745d8878eb60f9a049a4d584996f73a", + "reference": "82b478c69745d8878eb60f9a049a4d584996f73a", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<6.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0", + "twig/twig": "^3.12" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "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": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v7.2.3" + }, + "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": "2025-01-17T11:39:41+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=8.3" + }, + "platform-dev": [], + "plugin-api-version": "2.6.0" +} diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 0000000..f0c2ce3 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,13 @@ + + + PSR12 + + + + + + + + + vendor/* + diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..3e639cd --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,4 @@ +parameters: + level: 9 + paths: + - src diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..1dca589 --- /dev/null +++ b/public/index.php @@ -0,0 +1,35 @@ +place($animals, $rooms); +} catch (Exception $e) { + print_r($e->getMessage() . PHP_EOL); +} diff --git a/src/Animal/AnimalAbstract.php b/src/Animal/AnimalAbstract.php new file mode 100644 index 0000000..e4487ab --- /dev/null +++ b/src/Animal/AnimalAbstract.php @@ -0,0 +1,26 @@ +capacity = $capacity; + } + + public function getCapacity(): float + { + return $this->capacity; + } + + abstract public function getAllowedRoom(): RoomEnum; + + abstract public function getType(): AnimalEnum; +} diff --git a/src/Animal/AnimalEnum.php b/src/Animal/AnimalEnum.php new file mode 100644 index 0000000..9c0e4d2 --- /dev/null +++ b/src/Animal/AnimalEnum.php @@ -0,0 +1,16 @@ +getType() === $animal->getAllowedRoom()) { + if ($room->getCapacity() >= $animal->getCapacity()) { + $room->reduceCapacity($animal->getCapacity()); + $isAnimalArranged = true; + + print_r( + sprintf( + 'OK: A new %s just arrived in %s', + $animal->getType()->value, + $room->getType()->value + ) . PHP_EOL + ); + + break; + } + } + } + + if (!$isAnimalArranged) { + throw new Exception( + sprintf( + 'Error: %s can not be arranged!', + $animal->getType()->value + ) + ); + } + } + } +} diff --git a/src/Room/Balcony.php b/src/Room/Balcony.php new file mode 100644 index 0000000..86cac2d --- /dev/null +++ b/src/Room/Balcony.php @@ -0,0 +1,13 @@ +capacity = $capacity; + } + + public function getCapacity(): float + { + return $this->capacity; + } + + public function reduceCapacity(float $capacity): void + { + $this->capacity -= $capacity; + } + + public function getType(): RoomEnum + { + return static::getRoomType(); + } + + abstract public static function getRoomType(): RoomEnum; +} diff --git a/src/Room/RoomEnum.php b/src/Room/RoomEnum.php new file mode 100644 index 0000000..25ca2f0 --- /dev/null +++ b/src/Room/RoomEnum.php @@ -0,0 +1,13 @@ +