{"id":19568007,"url":"https://github.com/gomzyakov/larabook","last_synced_at":"2025-04-27T02:32:40.253Z","repository":{"id":262055790,"uuid":"646102788","full_name":"gomzyakov/larabook","owner":"gomzyakov","description":"Практические советы для начинающих PHP разработчиков на Laravel","archived":false,"fork":false,"pushed_at":"2024-11-10T07:47:07.000Z","size":6,"stargazers_count":0,"open_issues_count":9,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-10T08:23:31.357Z","etag":null,"topics":["clean-architecture","clean-code","clean-code-php","junior-developer","junior-programer","laravel-clean-architecture","php","php-clean-architecture"],"latest_commit_sha":null,"homepage":"http://gomzyakov.github.io/larabook/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gomzyakov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-27T09:44:43.000Z","updated_at":"2024-11-10T07:47:57.000Z","dependencies_parsed_at":"2024-11-10T08:24:20.213Z","dependency_job_id":"7ac6ca41-6d69-4b49-a598-86f8e557e66a","html_url":"https://github.com/gomzyakov/larabook","commit_stats":null,"previous_names":["gomzyakov/larabook"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gomzyakov%2Flarabook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gomzyakov%2Flarabook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gomzyakov%2Flarabook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gomzyakov%2Flarabook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gomzyakov","download_url":"https://codeload.github.com/gomzyakov/larabook/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224056494,"owners_count":17248323,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clean-architecture","clean-code","clean-code-php","junior-developer","junior-programer","laravel-clean-architecture","php","php-clean-architecture"],"created_at":"2024-11-11T05:42:33.317Z","updated_at":"2024-11-11T05:42:34.286Z","avatar_url":"https://github.com/gomzyakov.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Практические советы для начинающих PHP разработчиков на Laravel\n\nПривет! :wave:\n\nМеня зовут [Александр Гомзяков](https://github.com/gomzyakov) и я инженер-программист, занимающийся созданием высококачественных веб-сайтов и приложений. 🧑‍💻\n\nЯ работаю в компании [SpectrumData](https://spectrumdata.ru) которая занимается создаем невероятных сервисов, предоставляющих информацию о [автомобилях](https://avtocod.ru), [объектах недвижимости](https://egrn-reestro.ru) (и [многом другом](https://spectrumdata.ru/solutions)).\n\nНиже я постарался очень коротко описать всё то, с чем ты обязательно столкнёшься разрабатывая [Laravel](https://github.com/laravel/laravel)-проекты на [PHP](https://www.php.net).\n\nНе каждый из этих советов должен *строго* соблюдаться, и с ещё меньшим количеством принципов будут согласны абсолютно все разработчики. Это лишь рекомендации и некоторая отправная точка для начинающих [Laravel](https://github.com/laravel/laravel)-разработчиков.\n\nПриятного и продуктивного программирования!\n\n\u003cbr\u003e\n\n## Вопросы и предложения\n\nНе стесняйтесь писать, если у вас есть вопросы или предложения. Сделать это можно прямо здесь, в [issues](https://github.com/gomzyakov/junior-php-developer/issues), или написав мне на электронную почту [alexander.gomzyakov@gmail.com](mailto:alexander.gomzyakov@gmail.com)\n\n\u003cbr\u003e\n\n## Содержание\n\n\u003c!-- TODO Сделай сортировку пунктов от простого к сложному --\u003e\n\n### [Чистый код](#Чистый-код)\n- [Следуйте принятым в команде соглашениям](#Следуйте-принятым-в-команде-соглашениям)\n- [Не нужно создавать сущности без необходимости в них](#Не-нужно-создавать-сущности-без-необходимости-в-них)\n- [Убирайте мёртвый код](#Убирайте-мёртвый-код)\n- [Именование сущностей](#Именование-сущностей)\n- [Используйте значимые и произносимые имена](#Используйте-значимые-и-произносимые-имена)\n- [Используйте одно и тоже название для переменных с одинаковым назначением](#Используйте-одно-и-тоже-название-для-переменных-с-одинаковым-назначением)\n- [Не используйте слишком общие слова в названиях](#Не-используйте-слишком-общие-слова-в-названиях)\n- [Используйте имена, по которым удобно искать](#Используйте-имена-по-которым-удобно-искать)\n- [Используйте пояснительные переменные и константы](#Используйте-пояснительные-переменные-и-константы)\n- [Избегайте глубокой вложености](#Избегайте-глубокой-вложености)\n- [Не добавляйте ненужный контекст](#Не-добавляйте-ненужный-контекст)\n- [Используйте контроль типов](#Используйте-контроль-типов)\n- [Сравнение](#Сравнение)\n- [Используйте идентичное сравнение](#Используйте-идентичное-сравнение)\n- [Оператор объединения с null](#Оператор-объединения-с-null)\n- [Функции](#Функции)\n- [Аргументы функций (в идеале два или меньше)](#Аргументы-функций-в-идеале-два-или-меньше)\n- [Имена функций должны быть говорящими](#Имена-функций-должны-быть-говорящими)\n- [Не используйте флаги в качестве параметров функций](#Не-используйте-флаги-в-качестве-параметров-функций)\n- [Избегайте побочных эффектов](#Избегайте-побочных-эффектов)\n- [Не пишите в глобальные функции](#Не-пишите-в-глобальные-функции)\n- [Инкапсулирование условных выражений](#Инкапсулирование-условных-выражений)\n- [Избегайте негативных условных выражений](#Избегайте-негативных-условных-выражений)\n- [Избегайте условных выражений](#Избегайте-условных-выражений)\n- [Объекты и структуры данных](#Объекты-и-структуры-данных)\n- [Используйте инкапсуляцию объектов](#Используйте-инкапсуляцию-объектов)\n- [У объектов должны быть private/protected компоненты](#У-объектов-должны-быть-privateprotected-компоненты)\n- [Классы](#Классы)\n- [Композиция лучше наследования](#Композиция-лучше-наследования)\n- [Избегать Текучий интерфейс (Fluent interface)](#Избегать-Текучий-интерфейс-fluent-interface)\n\n### [Архитектурные концепции](#Архитектурные-концепции)\n- [Не повторяйся (Don’t repeat yourself, DRY)](#Не-повторяйся-dont-repeat-yourself-dry)\n- [KISS](#KISS)\n- MVC\n- [SOLID](#solid)\n- [S: Принцип единственной ответственности](#Принцип-единственной-ответственности)\n- [O: Принцип открытости/закрытости](#Принцип-открытостизакрытости)\n- [L: Принцип подстановки Барбары Лисков](#Принцип-подстановки-Барбары-Лисков)\n- [I: Принцип разделения интерфейса](#Принцип-разделения-интерфейса)\n- [D: Принцип инверсии зависимостей](#Принцип-инверсии-зависимостей)\n\n### [Паттерны проектирования](#Паттерны-проектирования)\n- [Не используйте шаблон Одиночка (Singleton)](#Не-используйте-шаблон-Одиночка-singleton)\n\n### Laravel\n- *Текст раздела ещё не написан*\n\n### GIT\n- *Текст раздела ещё не написан*\n\n### Тестирование\n- *Текст раздела ещё не написан*\n\n### [Рекомендую почитать](#Рекомендую-почитать)\n\n\u003cbr\u003e\n\n# Чистый код\n\nЗдесь описаны общие принципы разработки ПО, многие из которых описаны в книге [*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882) Роберта Мартина. Это руководство по созданию читабельного, многократно используемого и пригодного для рефакторинга кода на PHP.\n\n\u003cbr\u003e\n\n## Следуйте принятым в команде соглашениям\n\n\u003c!-- TODO Расписать --\u003e\n\u003c!-- В одиночку большие проекты не пишутся --\u003e\n\u003c!-- Нет ничего хуже когда каждый сам за себя --\u003e\n\u003c!-- Вопрос отчасти решается автоматикой --\u003e\n\u003eТекст раздела ещё не написан.\n\n\u003cbr\u003e\n\n## Не нужно создавать сущности без необходимости в них\n\nВсегда задумывайтесь над тем, какую **выгоду вы получите** выделив дополнительный класс или метод. Выделяя в отдельный метод пару строк, которые, при этом, еще и нигде больше не повторяются, вы можете только запутать и усложнить свой код.\n\n**Плохо:**\n\n```php\n\ndefine('FOO_TITLE', 'Сумма ');\n\nfunction sumOfInteger(integer $a, integer $b): integer\n{\n    return $a + $b;\n}\n\necho FOO_TITLE . sumOfInteger(13, 42);\n```\n\n**Хорошо:**\n\n```php\n$first_number  = 13;\n$second_number = 42;\n\necho FOO_TITLE . ($first_number + $second_number);\n```\n\nЭтот принцип также часто называют [бритвой Оккама](https://ru.wikipedia.org/wiki/Бритва_Оккама).\n\n\u003cbr\u003e\n\n# Убирайте мёртвый код\n\nОн плох так же, как и дублирующий код. Не нужно держать его в кодовой базе. Если что-то не вызывается, избавьтесь от этого! Если что, мёртвый код можно будет достать из истории версий.\n\n\u003c!-- TODO Переписать --\u003e\n\n**Плохо:**\n\n```php\nfunction oldRequestModule(string $url): void\n{\n    // ...\n}\n\nfunction newRequestModule(string $url): void\n{\n    // ...\n}\n\n$request = newRequestModule($requestUrl);\ninventoryTracker('apples', $request, 'www.inventory-awesome.io');\n```\n\n**Хорошо:**\n\n```php\nfunction requestModule(string $url): void\n{\n    // ...\n}\n\n$request = requestModule($requestUrl);\ninventoryTracker('apples', $request, 'www.inventory-awesome.io');\n```\n\n\u003cbr\u003e\n\n## Именование сущностей\n\n\u003c!-- TODO Общие советы по названию переменных --\u003e\n\n\u003cbr/\u003e\n\n## Используйте значимые и произносимые имена\n\nЭто касается как имен переменных, так и классов/констант.\n\n**Плохо:**\n\n```php\n$ymdstr = $moment-\u003eformat('y-m-d');\n```\n\n**Хорошо:**\n\n```php\n$current_date = $moment-\u003eformat('y-m-d');\n```\n\n\u003cbr/\u003e\n\n## Используйте одно и тоже название для переменных с одинаковым назначением\n\n**Плохо:**\n\n```php\n$user_info;\n$user_data;\n$user_record;\n$user_profile;\n```\n\n**Хорошо:**\n\n```php\n$user;\n```\n\n## Не используйте слишком общие слова в названиях\n\nЧасто можно встретить названия типа `UsersManager` или `UsersHandler`, которые предполагают что класс производит некоторые действия с сущностью `User`. Слова `Manager` или `Handler` не дают *никакой* дополнительной информации, не подсказывают что делает такой класс, за что конкретно отвечает.\n\nСтарайтесь избегать в названиях классов и переменных слов:\n\n- `Manager`\n- `Handler`\n\n\n\u003c!-- TODO Дополнить --\u003e\n\n\u003cbr/\u003e\n\n\n## Используйте имена, по которым удобно искать\n\nМы прочитаем больше кода, чем когда-либо напишем. Поэтому важно писать такой код, который будет читабелен и удобен для поиска. Но давая переменным имена, бесполезные для понимания нашей программы, мы мешаем будущим читателям. Используйте такие имена, по которым удобно искать.\n\n**Плохо:**\n\n```php\n$result = $serializer-\u003eserialize($data, 448);\n```\n\n**Хорошо:**\n\n```php\n$json = $serializer-\u003eserialize($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);\n```\n\nddd\n\u003cbr/\u003e\n\n## Используйте пояснительные переменные и константы\n\nВместо [магических чисел](https://ru.wikipedia.org/wiki/Магическое_число_(программирование)), используйте хорошо именованные переменные и константы.\n\n**Плохо:**\n\n```php\nclass User\n{\n    public function calculateSalary(float $float): float // что передадут в переменной $float?\n    {\n        return $float * 1.23; // Что такое 1.23?\n    }\n}\n\n// ...\n\n$salary = $user-\u003ecalculateSalary(345.0); // Почему 345.0?\n```\n\n**Хорошо:**\n\n```php\nclass User\n{\n    // Региональный коэффициент\n    private const REGIONAL_RATION = 1.23;\n\n    public function calculateSalary(float $base_salary): float\n    {\n        return $base_salary * self::REGIONAL_RATION;\n    }\n}\n\n// ...\n\n$salary = $user-\u003ecalculateSalary($base_salary = 345.0);\n```\n\n\u003cbr/\u003e\n\n## Избегайте глубокой вложености\n\nСлишком много `if/else` утверждений может сделать ваш код трудно понимаемым:\n\n```php\nfunction isShopOpen($day): bool\n{\n    if ($day) {\n        if (is_string($day)) {\n            $day = strtolower($day);\n            if ($day === 'friday') {\n                return true;\n            } elseif ($day === 'saturday') {\n                return true;\n            } elseif ($day === 'sunday') {\n                return true;\n            } else {\n                return false;\n            }\n        } else {\n            return false;\n        }\n    } else {\n        return false;\n    }\n}\n```\n\nПо возможности, старайтесь вернуть значение как можно раньше, не допуская бесполезной вложенности:\n\n\n```php\nfunction isShopOpen(string $day): bool\n{\n    if (empty($day)) {\n        return false;\n    }\n\n$opening_days = ['friday', 'saturday', 'sunday'];\n\nreturn in_array(strtolower($day), $opening_days, true);\n}\n```\n\n\u003c!-- TODO Пояснение, что пересекается с принципом \"Fail Fast\" --\u003e\n\n\u003cbr/\u003e\n\n## Не добавляйте ненужный контекст\n\nЕсли имя вашего класса/объекта с чем-то у вас ассоциируется, не проецируйте эту ассоциацию на имя переменной.\n\n**Плохо:**\n\n```php\nclass Car\n{\n    public $car_make;\n    public $car_model;\n    public $car_color;\n\n    //...\n}\n```\n\n**Хорошо:**\n\n```php\nclass Car\n{\n    public $make;\n    public $model;\n    public $color;\n\n    //...\n}\n```\n\n\u003cbr/\u003e\n\n### Используйте контроль типов\n\n**Не хорошо:**\n\nЭто не хорошо потому, что переменная `$breweryName` может быть `NULL`.\n\n```php\nfunction createMicrobrewery($breweryName = 'Hipster Brew Co.'): void\n{\n    // ...\n}\n```\n\n**Лучше:**\n\nЭто решение менее понятно, чем предыдущая версия, но лучше контролирует значение переменной.\n\n```php\nfunction createMicrobrewery($name = null): void\n{\n    $breweryName = $name ?: 'Hipster Brew Co.';\n    // ...\n}\n```\n\n**Хорошо:**\n\nВы можете использовать [контроль типов](https://www.php.net/manual/ru/language.types.declarations.php) и быть увереным, что переменная `$breweryName` никогда не будет `NULL`.\n\n```php\nfunction createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void\n{\n    // ...\n}\n```\n\n\u003c!-- ### Избегайте проверки типов (часть 2) --\u003e\n\u003c!-- TODO Переработать текст ниже --\u003e\n\nPHP не типизирован, т. е. ваши функции могут принимать аргументы любых типов. Иногда такая свобода даже мешает и возникает соблазн выполнять проверку типов в функциях. Но есть много способов этого избежать. Первое, что нужно учитывать, это согласованные API.\n\n**Плохо:**\n\n```php\nfunction travelToTexas($vehicle): void\n{\n    if ($vehicle instanceof Bicycle) {\n        $vehicle-\u003epedalTo(new Location('texas'));\n    } elseif ($vehicle instanceof Car) {\n        $vehicle-\u003edriveTo(new Location('texas'));\n    }\n}\n```\n\n**Хорошо:**\n\n```php\nfunction travelToTexas(Vehicle $vehicle): void\n{\n    $vehicle-\u003etravelTo(new Location('texas'));\n}\n```\n\n\n\nЕсли вы работаете с базовыми примитивами (вроде строковых, целочисленных) и массивами, то не можете использовать полиморфизм. Но если кажется, что вам всё ещё нужна проверка типов и вы используете PHP 7+, то примените [объявление типов](https://www.php.net/manual/ru/language.types.declarations.php) или строгий режим (strict mode). Это даст вам статичную типизацию поверх стандартного PHP-синтаксиса. Проблема ручной проверки типов в том, что её качественное выполнение подразумевает такое многословие, что полученная искусственная \"типобезопасность\" не компенсирует потери читабельности кода. Сохраняйте чистоту своего PHP, пишите хорошие тесты и проводите качественные ревизии кода. Или делайте всё это, но со строгим объявлением PHP-типов или в строгом режиме.\n\n**Плохо:**\n\n```php\nfunction combine($val1, $val2): int\n{\n    if (!is_numeric($val1) || !is_numeric($val2)) {\n        throw new \\Exception('Must be of type Number');\n    }\n\n    return $val1 + $val2;\n}\n```\n\n**Хорошо:**\n\n```php\nfunction combine(int $val1, int $val2): int\n{\n    return $val1 + $val2;\n}\n```\n\n\u003cbr\u003e\n\n## Сравнение\n\n\u003c!-- TODO --\u003e\n\n\u003cbr\u003e\n\n## Используйте идентичное сравнение\n\n**Не хорошо:**\n\nПри использовании простого сравнения, string будет преобразован в integer.\n\n```php\n$a = '42';\n$b = 42;\n\nif ($a != $b) {\n    // The expression will always pass\n}\n```\n\nСравнение `$a != $b` возвращает `FALSE`, но на самом деле это `TRUE`!\nСтрока `42` отличается от строки числа `42`.\n\n**Хорошо:**\n\nИспользуя [идентичное сравнение](https://www.php.net/manual/ru/language.operators.comparison.php), будет сравнивать тип и значение.\n\n```php\n$a = '42';\n$b = 42;\n\nif ($a !== $b) {\n    // The expression is verified\n}\n```\n\nСравнение `$a !== $b` возвращает `TRUE`.\n\n\n\u003cbr\u003e\n\n## Оператор объединения с null\n\n\u003c!-- TODO Под вопросом, стоит ли это выносить в принцыпы --\u003e\n\nОператор объединения с null (`??`), являющийся синтаксическим сахаром для достаточно распространенного действия, когда совместно используются тернарный оператор и функция `isset()`. Он возвращает первый операнд, если он задан и не равен `NULL`, а в обратном случае возвращает второй операнд.\n\n**Не хорошо:**\n\n```php\nif (isset($_GET['name'])) {\n    $name = $_GET['name'];\n} elseif (isset($_POST['name'])) {\n    $name = $_POST['name'];\n} else {\n    $name = 'nobody';\n}\n```\n\n**Хорошо:**\n\n```php\n$name = $_GET['name'] ?? $_POST['name'] ?? 'nobody';\n```\n\n\u003cbr\u003e\n\n## Функции\n\n\u003c!-- TODO --\u003e\n\n\u003cbr\u003e\n\n## Аргументы функций (в идеале два или меньше)\n\n\u003c!-- TODO Переиеновать пункт --\u003e\n\nКрайне важно ограничивать количество параметров функций, потому что это упрощает тестирование. Больше трёх аргументов ведёт к \"комбинаторному взрыву\", когда вам нужно протестировать кучу разных случаев применительно к каждому аргументу.\n\nИдеальный вариант — вообще без аргументов. Один-два тоже нормально, но трёх нужно избегать. Если их получается больше, то нужно объединять, чтобы уменьшить количество. Обычно если у вас больше двух аргументов, то функция делает слишком много. В тех случаях, когда это не так, чаще всего в качестве аргумента достаточно использовать более высокоуровневый объект.\n\n**Плохо:**\n\n```php\nclass Questionnaire\n{\n    public function __construct(\n    string $firstname,\n    string $lastname,\n    string $patronymic,\n    string $region,\n    string $district,\n    string $city,\n    string $phone,\n    string $email\n    ) {\n        // ...\n    }\n}\n```\n\n**Хорошо:**\n\n```php\nclass Name\n{\n    private $firstname;\n    private $lastname;\n    private $patronymic;\n\n    public function __construct(string $firstname, string $lastname, string $patronymic)\n    {\n        $this-\u003efirstname = $firstname;\n        $this-\u003elastname = $lastname;\n        $this-\u003epatronymic = $patronymic;\n    }\n\n    // getters ...\n}\n\nclass City\n{\n    private $region;\n    private $district;\n    private $city;\n\n    public function __construct(string $region, string $district, string $city)\n    {\n        $this-\u003eregion = $region;\n        $this-\u003edistrict = $district;\n        $this-\u003ecity = $city;\n    }\n\n    // getters ...\n}\n\nclass Contact\n{\n    private $phone;\n    private $email;\n\n    public function __construct(string $phone, string $email)\n    {\n        $this-\u003ephone = $phone;\n        $this-\u003eemail = $email;\n    }\n\n    // getters ...\n}\n\nclass Questionnaire\n{\n    public function __construct(Name $name, City $city, Contact $contact)\n    {\n        // ...\n    }\n}\n```\n\n\u003cbr\u003e\n\n## Имена функций должны быть говорящими\n\n\u003c!-- TODO Пересекается с \"Используйте значимые и произносимые имена\" --\u003e\n\n**Плохо:**\n\n```php\nclass Email\n{\n    //...\n\n    public function handle(): void\n    {\n        mail($this-\u003eto, $this-\u003esubject, $this-\u003ebody);\n    }\n}\n\n$message = new Email(...);\n// What is this? A handle for the message? Are we writing to a file now?\n$message-\u003ehandle();\n```\n\n**Хорошо:**\n\n```php\nclass Email\n{\n    //...\n\n    public function send(): void\n    {\n        mail($this-\u003eto, $this-\u003esubject, $this-\u003ebody);\n    }\n}\n\n$message = new Email(...);\n$message-\u003esend(); // Понятно, что отправляем email\n```\n\n\u003cbr\u003e\n\n## Не используйте флаги в качестве параметров функций\n\n\u003c!-- TODO Переписать --\u003e\n\nФлаги говорят вашим пользователям, что функции делают больше одной вещи. А они должны делать что-то одно. Разделяйте свои функции, если они идут по разным ветвям кода в соответствии с булевой логикой.\n\n**Плохо:**\n\n```php\nfunction createFile(string $name, bool $temp = false): void\n{\n    if ($temp) {\n        touch('./temp/'.$name);\n    } else {\n        touch($name);\n    }\n}\n```\n\n**Хорошо:**\n\n```php\nfunction createFile(string $name): void\n{\n    touch($name);\n}\n\nfunction createTempFile(string $name): void\n{\n    touch('./temp/'.$name);\n}\n```\n\n\u003cbr\u003e\n\n## Избегайте побочных эффектов\n\n\u003c!-- TODO Переписать --\u003e\n\nФункция может привносить побочные эффекты, если она не только получает значение и возвращает другое значение/значения, но и делает что-то ещё. Побочным эффектом может быть запись в файл, изменение глобальной переменной или случайная отправка всех ваших денег незнакомому человеку.\n\nНо иногда побочные эффекты бывают нужны. Например, та же запись в файл. Лучше делать это централизованно. Не выбирайте несколько функций и классов, которые пишут в какой-то один файл, используйте для этого единый сервис. Единственный.\n\nГлавная задача — избежать распространённых ошибок вроде общего состояния для нескольких объектов без какой-либо структуры; использования изменяемых типов данных, которые могут быть чем-то перезаписаны; отсутствия централизованной обработки побочных эффектов. Если вы сможете это сделать, то будете счастливее подавляющего большинства других программистов.\n\n**Плохо:**\n\n```php\n// Global variable referenced by following function.\n// If we had another function that used this name, now it'd be an array and it could break it.\n$name = 'Ryan McDermott';\n\nfunction splitIntoFirstAndLastName(): void\n{\n    global $name;\n\n    $name = explode(' ', $name);\n}\n\nsplitIntoFirstAndLastName();\n\nvar_dump($name); // ['Ryan', 'McDermott'];\n```\n\n**Хорошо:**\n\n```php\nfunction splitIntoFirstAndLastName(string $name): array\n{\n    return explode(' ', $name);\n}\n\n$name = 'Ryan McDermott';\n$newName = splitIntoFirstAndLastName($name);\n\nvar_dump($name); // 'Ryan McDermott';\nvar_dump($newName); // ['Ryan', 'McDermott'];\n```\n\n\u003cbr\u003e\n\n## Не пишите в глобальные функции\n\n\u003c!-- TODO Rename item \u0026 rewrite --\u003e\n\u003c!-- TODO Переписать --\u003e\n\nЗасорение глобалами — дурная привычка в любом языке, потому что вы можете конфликтовать с другой библиотекой, а пользователи вашего API не будут об этом знать, пока не получат исключение в production. Приведу пример: вам нужен конфигурационный массив? Вы пишете глобальную функцию вроде `config()`, но она может конфликтовать с другой библиотекой, пытающейся делать то же самое.\n\n**Плохо:**\n\n```php\nfunction config(): array\n{\n    return  [\n    'foo' =\u003e 'bar',\n    ];\n}\n```\n\n**Хорошо:**\n\n```php\nclass Configuration\n{\n    private $configuration = [];\n\n    public function __construct(array $configuration)\n    {\n        $this-\u003econfiguration = $configuration;\n    }\n\n    public function get(string $key): ?string\n    {\n        // null coalescing operator\n        return $this-\u003econfiguration[$key] ?? null;\n    }\n}\n```\n\nЗагрузите конфигурацию и создайте экземпляр класса `Configuration`.\n\n```php\n$configuration = new Configuration([\n'foo' =\u003e 'bar',\n]);\n```\n\nИ теперь вы должны использовать экземпляр класса `Configuration` в своем приложении.\n\n\u003cbr\u003e\n\n## Инкапсулирование условных выражений\n\n\u003c!-- TODO --\u003e\n\n**Плохо:**\n\n```php\nif ($article-\u003estate === 'published') {\n    // ...\n}\n```\n\n**Хорошо:**\n\n```php\nif ($article-\u003eisPublished()) {\n    // ...\n}\n```\n\n\u003cbr\u003e\n\n## Избегайте негативных условных выражений\n\n**Плохо:**\n\n```php\nfunction isDOMNodeNotPresent(\\DOMNode $node): bool\n{\n    // ...\n}\n\nif (!isDOMNodeNotPresent($node))\n{\n    // ...\n}\n```\n\n**Хорошо:**\n\n```php\nfunction isDOMNodePresent(\\DOMNode $node): bool\n{\n    // ...\n}\n\nif (isDOMNodePresent($node)) {\n    // ...\n}\n```\n\n\u003cbr\u003e\n\n## Избегайте условных выражений\n\n\u003c!-- TODO Перенести в паттерны --\u003e\n\u003c!-- TODO Переписать --\u003e\n\nНаверно, это кажется невозможным. Впервые это услышав, многие говорят: \"Как я смогу что-либо сделать без выражения `if`?\" Второй распространённый вопрос: \"Ну, это прекрасно, но зачем мне это?\" Ответ заключается в рассмотренном выше правиле чистого кода: функция должна делать что-то одно. Если у вас есть классы и функции, содержащие выражение `if`, то тем самым вы говорите своим пользователям, что функция делает больше одной вещи. Не забывайте — нужно оставить что-то одно.\n\n**Плохо:**\n\n```php\nclass Airplane\n{\n    // ...\n\n    public function getCruisingAltitude(): int\n    {\n        switch ($this-\u003etype) {\n            case '777':\n            return $this-\u003egetMaxAltitude() - $this-\u003egetPassengerCount();\n            case 'Air Force One':\n            return $this-\u003egetMaxAltitude();\n            case 'Cessna':\n            return $this-\u003egetMaxAltitude() - $this-\u003egetFuelExpenditure();\n        }\n    }\n}\n```\n\n**Хорошо:**\n\n```php\ninterface Airplane\n{\n    // ...\n\n    public function getCruisingAltitude(): int;\n}\n\nclass Boeing777 implements Airplane\n{\n    // ...\n\n    public function getCruisingAltitude(): int\n    {\n        return $this-\u003egetMaxAltitude() - $this-\u003egetPassengerCount();\n    }\n}\n\nclass AirForceOne implements Airplane\n{\n    // ...\n\n    public function getCruisingAltitude(): int\n    {\n        return $this-\u003egetMaxAltitude();\n    }\n}\n\nclass Cessna implements Airplane\n{\n    // ...\n\n    public function getCruisingAltitude(): int\n    {\n        return $this-\u003egetMaxAltitude() - $this-\u003egetFuelExpenditure();\n    }\n}\n```\n\n\u003cbr\u003e\n\n## Объекты и структуры данных\n\n\u003c!-- TODO --\u003e\n\n\u003cbr\u003e\n\n## Используйте инкапсуляцию объектов\n\n\u003c!-- TODO Переписать --\u003e\n\nВ PHP можно задать для методов ключевые слова `public`, `protected` и `private`. С их помощью вы будете управлять изменением свойств объекта.\n\n* Если вам нужно не только получать свойство объекта, то необязательно находить и менять каждый метод чтения (accessor) в кодовой базе.\n* Благодаря `set` проще добавить валидацию.\n* Можно инкапсулировать внутреннее представление.\n* С помощью геттеров и сеттеров легко добавлять журналирование и обработку ошибок.\n* При наследовании такого класса вы можете переопределить функциональность по умолчанию.\n* Вы можете лениво загружать свойства объекта, например получая их с сервера.\n\nТакже это часть принципа [Открытости/Закрытости](#Принцип-открытостизакрытости-openclosed-principle-ocp), входящего в набор объектно ориентированных принципов проектирования.\n\n**Плохо:**\n\n```php\nclass BankAccount\n{\n    public $balance = 1000;\n}\n\n$bankAccount = new BankAccount();\n\n// Buy shoes...\n$bankAccount-\u003ebalance -= 100;\n```\n\n**Хорошо:**\n\n```php\nclass BankAccount\n{\n    private $balance;\n\n    public function __construct(int $balance = 1000)\n    {\n        $this-\u003ebalance = $balance;\n    }\n\n    public function withdraw(int $amount): void\n    {\n        if ($amount \u003e $this-\u003ebalance) {\n            throw new \\Exception('Amount greater than available balance.');\n        }\n\n        $this-\u003ebalance -= $amount;\n    }\n\n    public function deposit(int $amount): void\n    {\n        $this-\u003ebalance += $amount;\n    }\n\n    public function getBalance(): int\n    {\n        return $this-\u003ebalance;\n    }\n}\n\n$bankAccount = new BankAccount();\n\n// Buy shoes...\n$bankAccount-\u003ewithdraw($shoesPrice);\n\n// Get balance\n$balance = $bankAccount-\u003egetBalance();\n```\n\n\u003cbr\u003e\n\n## У объектов должны быть private/protected компоненты\n\n\u003c!-- TODO Переписать --\u003e\n\n* `public`  методы и свойства наиболее опасны для изменений, поскольку внешний код может легко опираться на них, и вы не можете контролировать, какой код опирается на них. **Изменения в классе опасны для всех пользователей класса.**\n* `protected` модификатор являются столь же опасными, как и `public`, поскольку они доступны в рамках любого дочернего класса. Это фактически означает, что разница между `public` и `protected` является только механизмом доступа, но гарантия инкапсуляции остается неизменной. **Модификации в классе опасны для всех классов потомков.**\n* `private` модификатор гарантирует, что код **опасен для изменения только в границах одного класса** (вы защищены от модификаций, и у вас не будет [Jenga эффекта](http://www.urbandictionary.com/define.php?term=Jengaphobia\u0026defid=2494196)).\n\nПоэтому используйте `private` по умолчанию и `public/protected`, когда вам нужно предоставить доступ для внешних классов.\n\nДля получения дополнительной информации вы можете прочитать [сообщение в блоге](http://fabien.potencier.org/pragmatism-over-theory-protected-vs-private.html), написанное [Fabien Potencier](https://github.com/fabpot).\n\n**Плохо:**\n\n```php\nclass Employee\n{\n    public $name;\n\n    public function __construct(string $name)\n    {\n        $this-\u003ename = $name;\n    }\n}\n\n$employee = new Employee('John Doe');\necho 'Employee name: '.$employee-\u003ename; // Employee name: John Doe\n```\n\n**Хорошо:**\n\n```php\nclass Employee\n{\n    private $name;\n\n    public function __construct(string $name)\n    {\n        $this-\u003ename = $name;\n    }\n\n    public function getName(): string\n    {\n        return $this-\u003ename;\n    }\n}\n\n$employee = new Employee('John Doe');\necho 'Employee name: '.$employee-\u003egetName(); // Employee name: John Doe\n```\n\n\u003cbr\u003e\n\n## Классы\n\n\u003cbr\u003e\n\n## Композиция лучше наследования\n\n\u003c!-- TODO Переписать --\u003e\n\nКак говорится в известной книге \"[*Шаблоны проектирования*](https://en.wikipedia.org/wiki/Design_Patterns)\" Банды четырёх, по мере возможности нужно выбирать композицию, а не наследование. Есть много хороших причин использовать как наследование, так и композицию. Главная цель этой максимы заключается в том, если вы инстинктивно склоняетесь к наследованию, то постарайтесь представить, может ли композиция лучше решить вашу задачу. В каких-то случаях это действительно более подходящий вариант.\n\nВы спросите: \"А когда лучше выбирать наследование?\" Всё зависит от конкретной задачи, но можно ориентироваться на этот список ситуаций, когда наследование предпочтительнее композиции:\n\n1. Ваше наследование — это взаимосвязь is-a, а не has-a. Пример: Человек → Животное vs. Пользователь → Детали пользователя (UserDetails).\n2. Вы можете повторно использовать код из базовых классов. (Люди могут двигаться, как животные.)\n3. Вы хотите внести глобальные изменения в производные классы, изменив базовый класс. (Изменение расхода калорий у животных во время движения.)\n\n**Плохо:**\n\n```php\nclass Employee\n{\n    private $name;\n    private $email;\n\n    public function __construct(string $name, string $email)\n    {\n        $this-\u003ename = $name;\n        $this-\u003eemail = $email;\n    }\n\n    // ...\n}\n\n// Bad because Employees \"have\" tax data.\n// EmployeeTaxData is not a type of Employee\n\nclass EmployeeTaxData extends Employee\n{\n    private $ssn;\n    private $salary;\n\n    public function __construct(string $name, string $email, string $ssn, string $salary)\n    {\n        parent::__construct($name, $email);\n\n        $this-\u003essn = $ssn;\n        $this-\u003esalary = $salary;\n    }\n\n    // ...\n}\n```\n\n**Хорошо:**\n\n```php\nclass EmployeeTaxData\n{\n    private $ssn;\n    private $salary;\n\n    public function __construct(string $ssn, string $salary)\n    {\n        $this-\u003essn = $ssn;\n        $this-\u003esalary = $salary;\n    }\n\n    // ...\n}\n\nclass Employee\n{\n    private $name;\n    private $email;\n    private $taxData;\n\n    public function __construct(string $name, string $email)\n    {\n        $this-\u003ename = $name;\n        $this-\u003eemail = $email;\n    }\n\n    public function setTaxData(EmployeeTaxData $taxData)\n    {\n        $this-\u003etaxData = $taxData;\n    }\n\n    // ...\n}\n```\n\n\u003cbr\u003e\n\n## Избегать Текучий интерфейс (Fluent interface)\n\n\u003c!-- TODO Спорный пункт --\u003e\n\n[Текучий интерфейс (Fluent interface)](https://ru.wikipedia.org/wiki/Fluent_interface) - это объектно-ориентированный API, целью которого является улучшение читабельности исходного кода с помощью [Цепочки методов (Method chaining)](https://en.wikipedia.org/wiki/Method_chaining).\n\nХотя могут быть некоторые случаи в которых этот шаблон уменьшает многословность кода (например, [PHPUnit Mock Builder](https://phpunit.de/manual/current/en/test-doubles.html) или [Doctrine Query Builder](http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/query-builder.html)), но чаще всего это происходит с некоторыми издержками:\n\n1. Нарушение [Инкапсуляции](https://ru.wikipedia.org/wiki/Инкапсуляция_(программирование)).\n2. Нарушение [Декораторов](https://ru.wikipedia.org/wiki/Декоратор_(шаблон_проектирования)).\n3. Затрудняет [мокинг (mock)](https://ru.wikipedia.org/wiki/Mock-объект) в тестах.\n4. Осложняет чтение diff коммитов.\n\nДля получения дополнительной информации вы можете прочитать [сообщение в блоге](https://ocramius.github.io/blog/fluent-interfaces-are-evil/), написанное [Marco Pivetta](https://github.com/Ocramius).\n\n**Плохо:**\n\n```php\nclass Car\n{\n    private $make = 'Honda';\n    private $model = 'Accord';\n    private $color = 'white';\n\n    public function setMake(string $make): self\n    {\n        $this-\u003emake = $make;\n\n        // NOTE: Returning this for chaining\n        return $this;\n    }\n\n    public function setModel(string $model): self\n    {\n        $this-\u003emodel = $model;\n\n        // NOTE: Returning this for chaining\n        return $this;\n    }\n\n    public function setColor(string $color): self\n    {\n        $this-\u003ecolor = $color;\n\n        // NOTE: Returning this for chaining\n        return $this;\n    }\n\n    public function dump(): void\n    {\n        var_dump($this-\u003emake, $this-\u003emodel, $this-\u003ecolor);\n    }\n}\n\n$car = (new Car())\n-\u003esetColor('pink')\n-\u003esetMake('Ford')\n-\u003esetModel('F-150')\n-\u003edump();\n```\n\n**Хорошо:**\n\n```php\nclass Car\n{\n    private $make = 'Honda';\n    private $model = 'Accord';\n    private $color = 'white';\n\n    public function setMake(string $make): void\n    {\n        $this-\u003emake = $make;\n    }\n\n    public function setModel(string $model): void\n    {\n        $this-\u003emodel = $model;\n    }\n\n    public function setColor(string $color): void\n    {\n        $this-\u003ecolor = $color;\n    }\n\n    public function dump(): void\n    {\n        var_dump($this-\u003emake, $this-\u003emodel, $this-\u003ecolor);\n    }\n}\n\n$car = new Car();\n$car-\u003esetColor('pink');\n$car-\u003esetMake('Ford');\n$car-\u003esetModel('F-150');\n$car-\u003edump();\n```\n\n\n\n\n\n\u003cbr\u003e\n\n# Архитектурные концепции\n\n\u003cbr\u003e\n\n## SOLID\n\n\u003c!-- TODO Переписать --\u003e\n\n**SOLID** - это мнемонический акроним, введенный Michael Feathers для первых пяти принципов объектно-ориентированного программирования и дизайна, описанных Robert Martin.\n\n* [S: Принцип единственной ответственности](#Принцип-единственной-ответственности)\n* [O: Принцип открытости/закрытости](#Принцип-открытостизакрытости)\n* [L: Принцип подстановки Барбары Лисков](#Принцип-подстановки-Барбары-Лисков)\n* [I: Принцип разделения интерфейса](#Принцип-разделения-интерфейса)\n* [D: Принцип инверсии зависимостей](#Принцип-инверсии-зависимостей)\n\n\u003cbr\u003e\n\n## Принцип единственной ответственности\n\n\u003c!-- (Single Responsibility Principle, SRP) --\u003e\n\u003c!-- TODO Поясни, что также относится к методам --\u003e\n\nКак говорится в книге Clean Code: \"Для изменения класса никогда не должно быть более одной причины\". Порой заманчиво набить класс всевозможной функциональностью, как мы это делаем с сумками и рюкзаками, которые разрешается взять в качестве ручной клади в самолёт. Проблема в том, что ваш класс не будет концептуально связанным (conceptually cohesive), и поэтому возникнет много причин изменить его. Важно минимизировать количество случаев, когда вам нужно изменять класс. А важно потому, что когда в классе избыток функциональности и вам нужно поменять её часть, то может быть трудно понять, как это отразится на зависимых модулях в кодовой базе.\n\n**Плохо:**\n\n```php\nclass UserSettings\n{\n    private $user;\n\n    public function __construct(User $user)\n    {\n        $this-\u003euser = $user;\n    }\n\n    public function changeSettings(array $settings): void\n    {\n        if ($this-\u003everifyCredentials()) {\n            // ...\n        }\n    }\n\n    private function verifyCredentials(): bool\n    {\n        // ...\n    }\n}\n```\n\n**Хорошо:**\n\n```php\nclass UserAuth\n{\n    private $user;\n\n    public function __construct(User $user)\n    {\n        $this-\u003euser = $user;\n    }\n\n    public function verifyCredentials(): bool\n    {\n        // ...\n    }\n}\n\nclass UserSettings\n{\n    private $user;\n    private $auth;\n\n    public function __construct(User $user)\n    {\n        $this-\u003euser = $user;\n        $this-\u003eauth = new UserAuth($user);\n    }\n\n    public function changeSettings(array $settings): void\n    {\n        if ($this-\u003eauth-\u003everifyCredentials()) {\n            // ...\n        }\n    }\n}\n```\n\n\u003cbr\u003e\n\n## Принцип открытости/закрытости\n\n\u003c!-- (Open/Closed Principle, OCP) --\u003e\n\u003c!-- TODO Переписать --\u003e\n\nКак сказал Bertrand Meyer: \"Программные сущности (классы, модули, функции и т. д.) должны быть открыты для расширения, но закрыты для модифицирования\". Что это означает? Позвольте пользователям добавлять новую функциональность без изменения кода.\n\n**Плохо:**\n\n```php\nabstract class Adapter\n{\n    protected $name;\n\n    public function getName(): string\n    {\n        return $this-\u003ename;\n    }\n}\n\nclass AjaxAdapter extends Adapter\n{\n    public function __construct()\n    {\n        parent::__construct();\n\n        $this-\u003ename = 'ajaxAdapter';\n    }\n}\n\nclass NodeAdapter extends Adapter\n{\n    public function __construct()\n    {\n        parent::__construct();\n\n        $this-\u003ename = 'nodeAdapter';\n    }\n}\n\nclass HttpRequester\n{\n    private $adapter;\n\n    public function __construct(Adapter $adapter)\n    {\n        $this-\u003eadapter = $adapter;\n    }\n\n    public function fetch(string $url): Promise\n    {\n        $adapterName = $this-\u003eadapter-\u003egetName();\n\n        if ($adapterName === 'ajaxAdapter') {\n            return $this-\u003emakeAjaxCall($url);\n        } elseif ($adapterName === 'httpNodeAdapter') {\n            return $this-\u003emakeHttpCall($url);\n        }\n    }\n\n    private function makeAjaxCall(string $url): Promise\n    {\n        // request and return promise\n    }\n\n    private function makeHttpCall(string $url): Promise\n    {\n        // request and return promise\n    }\n}\n```\n\n**Хорошо:**\n\n```php\ninterface Adapter\n{\n    public function request(string $url): Promise;\n}\n\nclass AjaxAdapter implements Adapter\n{\n    public function request(string $url): Promise\n    {\n        // request and return promise\n    }\n}\n\nclass NodeAdapter implements Adapter\n{\n    public function request(string $url): Promise\n    {\n        // request and return promise\n    }\n}\n\nclass HttpRequester\n{\n    private $adapter;\n\n    public function __construct(Adapter $adapter)\n    {\n        $this-\u003eadapter = $adapter;\n    }\n\n    public function fetch(string $url): Promise\n    {\n        return $this-\u003eadapter-\u003erequest($url);\n    }\n}\n```\n\n\u003cbr\u003e\n\n### Принцип подстановки Барбары Лисков\n\n\u003c!-- (Liskov Substitution Principle, LSP) --\u003e\n\u003c!-- TODO Переписать (пример с кругами и квадратами) --\u003e\n\nЗа этим пугающим термином скрывается очень простая идея. Формальное определение: \"Если S — это подтип Т, то объекты типа Т могут быть заменены объектами типа S (например, вместо объектов типа Т можно подставить объекты типа S) без изменения каких-либо свойств программы (корректность, задачи и т. д.)\". Ещё более пугающее определение.\n\nМожно объяснить проще: если у вас есть родительский и дочерний классы, тогда они могут быть взаимозаменяемы без получения некорректных результатов. Рассмотрим классический пример с квадратом и прямоугольником. С точки зрения математики квадрат — это прямоугольник, но если смоделировать эту взаимосвязь is-a посредством наследования, то у вас будут проблемы.\n\n**Плохо:**\n\n```php\nclass Rectangle\n{\n    protected $width = 0;\n    protected $height = 0;\n\n    public function setWidth(int $width): void\n    {\n        $this-\u003ewidth = $width;\n    }\n\n    public function setHeight(int $height): void\n    {\n        $this-\u003eheight = $height;\n    }\n\n    public function getArea(): int\n    {\n        return $this-\u003ewidth * $this-\u003eheight;\n    }\n}\n\nclass Square extends Rectangle\n{\n    public function setWidth(int $width): void\n    {\n        $this-\u003ewidth = $this-\u003eheight = $width;\n    }\n\n    public function setHeight(int $height): void\n    {\n        $this-\u003ewidth = $this-\u003eheight = $height;\n    }\n}\n\nfunction printArea(Rectangle $rectangle): void\n{\n    $rectangle-\u003esetWidth(4);\n    $rectangle-\u003esetHeight(5);\n\n    // BAD: Will return 25 for Square. Should be 20.\n    echo sprintf('%s has area %d.', get_class($rectangle), $rectangle-\u003egetArea()).PHP_EOL;\n}\n\n$rectangles = [new Rectangle(), new Square()];\n\nforeach ($rectangles as $rectangle) {\n    printArea($rectangle);\n}\n```\n\n**Хорошо:**\n\nЛучший способ - разделить четырехугольники и выделить более общий подтип для обеих фигур.\n\nНесмотря на кажущееся сходство квадрата и прямоугольника, они разные.\nКвадрат имеет много общего с ромбом, а прямоугольник с параллелограммом, но они не являются подтипом.\nКвадрат, прямоугольник, ромб и параллелограмм - это отдельные фигуры со своими собственными свойствами, хотя и схожими.\n\n```php\ninterface Shape\n{\n    public function getArea(): int;\n}\n\nclass Rectangle implements Shape\n{\n    private $width = 0;\n    private $height = 0;\n\n    public function __construct(int $width, int $height)\n    {\n        $this-\u003ewidth = $width;\n        $this-\u003eheight = $height;\n    }\n\n    public function getArea(): int\n    {\n        return $this-\u003ewidth * $this-\u003eheight;\n    }\n}\n\nclass Square implements Shape\n{\n    private $length = 0;\n\n    public function __construct(int $length)\n    {\n        $this-\u003elength = $length;\n    }\n\n    public function getArea(): int\n    {\n        return $this-\u003elength ** 2;\n    }\n}\n\nfunction printArea(Shape $shape): void\n{\n    echo sprintf('%s has area %d.', get_class($shape), $shape-\u003egetArea()).PHP_EOL;\n}\n\n$shapes = [new Rectangle(4, 5), new Square(5)];\n\nforeach ($shapes as $shape) {\n    printArea($shape);\n}\n```\n\n\u003cbr\u003e\n\n## Принцип разделения интерфейса\n\n\u003c!-- (Interface Segregation Principle, ISP) --\u003e\n\u003c!-- TODO Переписать --\u003e\n\nСогласно ISP, \"Клиенты не должны зависеть от интерфейсов, которые не используют\".\n\nХороший пример демонстрации принципа: классы, для которых требуются большие объекты настроек (settings objects). Рекомендуется не требовать от клиентов настраивать много параметров, потому что по большей части они им не нужны. Если сделать их опциональными, то это поможет избежать раздутости интерфейса.\n\n**Плохо:**\n\n```php\ninterface Employee\n{\n    public function work(): void;\n\n    public function eat(): void;\n}\n\nclass HumanEmployee implements Employee\n{\n    public function work(): void\n    {\n        // ....working\n    }\n\n    public function eat(): void\n    {\n        // ...... eating in lunch break\n    }\n}\n\nclass RobotEmployee implements Employee\n{\n    public function work(): void\n    {\n        //.... working much more\n    }\n\n    public function eat(): void\n    {\n        //.... robot can't eat, but it must implement this method\n    }\n}\n```\n\n**Хорошо:**\n\nНе каждый работник является сотрудником, но каждый сотрудник является работником.\n\n```php\ninterface Workable\n{\n    public function work(): void;\n}\n\ninterface Feedable\n{\n    public function eat(): void;\n}\n\ninterface Employee extends Feedable, Workable\n{\n}\n\nclass HumanEmployee implements Employee\n{\n    public function work(): void\n    {\n        // ....working\n    }\n\n    public function eat(): void\n    {\n        //.... eating in lunch break\n    }\n}\n\n// robot can only work\nclass RobotEmployee implements Workable\n{\n    public function work(): void\n    {\n        // ....working\n    }\n}\n```\n\n\u003cbr\u003e\n\n## Принцип инверсии зависимостей\n\n\u003c!-- (Dependency Inversion Principle, DIP) --\u003e\n\u003c!-- TODO Переписать --\u003e\n\nЭтот принцип гласит:\n\n1. Высокоуровневые модули не должны зависеть от низкоуровневых. Оба вида должны зависеть от абстракций.\n2. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.\n\nСначала это может быть трудным для понимания, но если вы работали с PHP-фреймворками (вроде Symfony), то уже встречались с реализацией этого принципа в виде инъекции зависимости (Dependency Injection, DI). Однако эти принципы не идентичны, DI ограждает высокоуровневые модули от деталей своих низкоуровневых модулей и их настройки. Это может быть сделано посредством DI. Огромное преимущество в том, что снижается сцепление (coupling) между модулями. Сцепление — очень плохой шаблон разработки, затрудняющий рефакторинг кода.\n\n**Плохо:**\n\n```php\nclass Employee\n{\n    public function work(): void\n    {\n        // ....working\n    }\n}\n\nclass Robot extends Employee\n{\n    public function work(): void\n    {\n        //.... working much more\n    }\n}\n\nclass Manager\n{\n    private $employee;\n\n    public function __construct(Employee $employee)\n    {\n        $this-\u003eemployee = $employee;\n    }\n\n    public function manage(): void\n    {\n        $this-\u003eemployee-\u003ework();\n    }\n}\n```\n\n**Хорошо:**\n\n```php\ninterface Employee\n{\n    public function work(): void;\n}\n\nclass Human implements Employee\n{\n    public function work(): void\n    {\n        // ....working\n    }\n}\n\nclass Robot implements Employee\n{\n    public function work(): void\n    {\n        //.... working much more\n    }\n}\n\nclass Manager\n{\n    private $employee;\n\n    public function __construct(Employee $employee)\n    {\n        $this-\u003eemployee = $employee;\n    }\n\n    public function manage(): void\n    {\n        $this-\u003eemployee-\u003ework();\n    }\n}\n```\n\n\u003cbr\u003e\n\n## Не повторяйся!\n\n\u003c!-- (Don’t repeat yourself, DRY) --\u003e\n\u003c!-- TODO Переписать --\u003e\n\u003c!-- TODO Не применим к тестам --\u003e\n\nПостарайтесь соблюдать принцип [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).\n\nСтарайтесь полностью избавиться от дублирующего кода. Он плох тем, что если вам нужно менять логику, то это придётся делать в нескольких местах.\n\nПредставьте, что вы владеете ресторанчиком и отслеживаете, есть ли продукты: помидоры, лук, чеснок, специи и т. д. Если у вас несколько списков с содержимым холодильников, то вам придётся обновлять их все, когда вы готовите какое-то блюдо. А если список один, то и вносить изменения придётся только в него.\n\nЗачастую дублирующий код возникает потому, что вы делаете две и более вещи, у которых много общего. Но небольшая разница между ними заставляет вас писать несколько функций, и те по большей части делают одно и то же. Удаление дублирующего кода означает, что вы создаёте абстракцию, которая может обрабатывать все различия с помощью единственной функции/модуля/класса.\n\nПравильный выбор абстракции критически важен, поэтому нужно следовать принципам SOLID, описанным в разделе \"[Классы](#Классы)\". Плохие абстракции могут оказаться хуже дублирующего кода, так что будьте осторожны! Но если можете написать хорошие, то делайте это! Не повторяйтесь, иначе окажется, что при каждом изменении вам нужно обновлять код в нескольких местах.\n\n**Плохо:**\n\n```php\nfunction showDeveloperList(array $developers): void\n{\n    foreach ($developers as $developer) {\n        $expectedSalary = $developer-\u003ecalculateExpectedSalary();\n        $experience = $developer-\u003egetExperience();\n        $githubLink = $developer-\u003egetGithubLink();\n        $data = [\n        $expectedSalary,\n        $experience,\n        $githubLink\n        ];\n\n        render($data);\n    }\n}\n\nfunction showManagerList(array $managers): void\n{\n    foreach ($managers as $manager) {\n        $expectedSalary = $manager-\u003ecalculateExpectedSalary();\n        $experience = $manager-\u003egetExperience();\n        $githubLink = $manager-\u003egetGithubLink();\n        $data = [\n        $expectedSalary,\n        $experience,\n        $githubLink\n        ];\n\n        render($data);\n    }\n}\n```\n\n**Хорошо:**\n\n```php\nfunction showList(array $employees): void\n{\n    foreach ($employees as $employee) {\n        $expectedSalary = $employee-\u003ecalculateExpectedSalary();\n        $experience = $employee-\u003egetExperience();\n        $githubLink = $employee-\u003egetGithubLink();\n        $data = [\n        $expectedSalary,\n        $experience,\n        $githubLink\n        ];\n\n        render($data);\n    }\n}\n```\n\n**Очень хорошо:**\n\nЛучше использовать компактную версию кода.\n\n```php\nfunction showList(array $employees): void\n{\n    foreach ($employees as $employee) {\n        render([\n        $employee-\u003ecalculateExpectedSalary(),\n        $employee-\u003egetExperience(),\n        $employee-\u003egetGithubLink()\n        ]);\n    }\n}\n```\n\n\u003cbr\u003e\n\n## Не используйте шаблон Одиночка (Singleton)\n\n\u003c!-- TODO Переписать --\u003e\n\nШаблон проектирования Одиночка (Singleton) является [антипаттерном](https://ru.wikipedia.org/wiki/Одиночка_(шаблон_проектирования)). Перефразируем Brian Button:\n\n1. Как правило, одиночки используются в качестве **глобального экземпляра**, почему это так плохо? Потому, что вы **скрываете зависимости вашего приложения** в своем коде, вместо того, чтобы сделать их явными через интерфейсы. Сделать что-то глобальным, чтобы избежать его распространения - это [чем-то попахивает](https://ru.wikipedia.org/wiki/Код_с_запашком).\n2. Одиночки нарушают принцип [единой ответственности](#Принцип-единственной-ответственности-single-responsibility-principle-srp): в силу того, что **они контролируют собственное создание и жизненный цикл**.\n3. Одиночки по своей сути приводят к тому, что код получается тесно [связанным](https://ru.wikipedia.org/wiki/Зацепление_(программирование)). Это во многих случаях **затрудняет его тестирование**.\n4. Одиночки несут состояние на протяжении всей жизни приложения. Еще один удар по тестированию, **так как вы можете столкнуться с ситуацией, когда необходимо закончить тесты**, что является большим нет для модульных тестов. Зачем? Потому что каждый модульный тест должен быть независимым от другого.\n\n[Misko Hevery](http://misko.hevery.com/about/) также очень хорошо разбирается в [корне проблемы](http://misko.hevery.com/2008/08/25/root-cause-of-singletons/).\n\n**Плохо:**\n\n```php\nclass DBConnection\n{\n    private static $instance;\n\n    private function __construct(string $dsn)\n    {\n        // ...\n    }\n\n    public static function getInstance(): DBConnection\n    {\n        if (self::$instance === null) {\n            self::$instance = new self();\n        }\n\n        return self::$instance;\n    }\n\n    // ...\n}\n\n$singleton = DBConnection::getInstance();\n```\n\n**Хорошо:**\n\n```php\nclass DBConnection\n{\n    public function __construct(string $dsn)\n    {\n        // ...\n    }\n\n    // ...\n}\n```\n\nСоздайте экземпляр класса `DBConnection` и настройте его с помощью [DSN](https://www.php.net/manual/ru/pdo.construct.php#refsect1-pdo.construct-parameters).\n\n```php\n$connection = new DBConnection($dsn);\n```\n\nИ теперь вы должны использовать экземпляр класса `DBConnection` в своем приложении.\n\n\n\u003cbr\u003e\n\n# Laravel\n\n\u003c!-- TODO --\u003e\n\u003eТекст раздела ещё не написан\n\n\u003cbr\u003e\n\n# GIT\n\n\u003c!-- TODO --\u003e\n\u003eТекст раздела ещё не написан\n\n\u003cbr\u003e\n\n# Тестирование\n\n\u003c!-- TODO --\u003e\n\u003eТекст раздела ещё не написан\n\n\u003cbr\u003e\n\n# Рекомендую почитать\n\n- Clean Code concepts adapted for PHP [jupeter/clean-code-php](https://github.com/jupeter/clean-code-php)\n- Концепции Чистого Кода, адаптированные для PHP [peter-gribanov/clean-code-php](https://github.com/peter-gribanov/clean-code-php)\n- Книга [*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgomzyakov%2Flarabook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgomzyakov%2Flarabook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgomzyakov%2Flarabook/lists"}