Сегодняшнее занятие из курса “Otus PHP Professional“ будет посвящено практикам написания читабельного кода. Рассмотрим принципы KISS, DRY, YAGNI.

О занятии

Цели занятия

  • после занятия вы сможете:

  • проанализировать понятие «хорошего кода».

Краткое содержание

  • написание хорошего кода;

  • принципы Fluent interface;

  • coding styles и необходимость документирования кода;

  • PHP the Right way и стандарты из PHP-FIG.

Результаты

  • писать код, за который «не стыдно».

Преподаватель

  • Дмитрий Кириллов, техн. директор “1С-Старт“

  • Дата лекции: 14.04.2022

/**/


Тезисы из лекции

План вебинара

Ожидаемые результаты

Стиль программирования

Советы по стилю

Пример хорошей организации кода:

  • все функции задокументированы

  • конструкторы вверху, публичные методы в центре, приватные методы внизу

Советы по форматированию стро

Пример рефакторинга:

поправили:

То же самое вертикально:

Пример:

(звезда) в PHPStorm можно быстро генерировать сеттеры из соотв. меню:

(звезда) Здесь опция “Fluent setters” будет возвращать нам инициализируемый объект (что и нужно для Конструктора)

Про пустые строки


Стандарты PSR

(warning) См. стандарты PSR тут: http://php-fig.org/psr

Пример оптимизации кода (исп. Collection / Коллекции)

Пример заблуждения

  • нужно уметь убеждать писать в стиле команды

  • нужно уметь переучивать “упертых“, чтобы работали в одно стиле с ост. командой

  • (звезда) см. курс “Тимлид от Отуса“: https://fas.st/hKsvp


Смысл кода: полезные советы

Имена переменных и констант

Пример:

Перечисления / enum

  • хранят список всех допустимых значений

Поправим код выше:

Нежелательность отрицаний

Пример:

Читабельность названий функций и параметров

Пример именований

Про английский

Код должен быть как хороший UI….

Принципы разработки ПО

~ Dont Repeat Yourself

Ко, которые не дублируется/не повторяется.

Пример: иметь в компании свой репозиторий библиотек как наработок

Принцип KISS

~ Keep It Simple Stupid

Код ккоторый не переусложнен.

Принцип YAGNI

~ You Are Gonna Need It ~ Тебе это не потребуется

Код, который не нужен/не используется. И не написан )

“Хороший разработчик — ленивый разработчик“

Условные выражения

Составные выражения лучше комбинировать. Пример:

Советы по избавлению от ветвлений

Пример:

(звезда) здесь исп. readonly — можно инициализировать 1 раз (в отл. от констант, где инициализация происходит сразу)

Рефакторим: избавляемся от match, используем паттерн Стратегия

(звезда) Далее в конфиге/маппере и т.п. задаем нужные действия, вместо match/switch

(звезда) в существующее запущенное приложение можно добавить новый код/класс, через eval

Советы по функциям

Пример: рефакторинг

“Тяжелый код“: все вперемешку:

конвертация, фильтрация, расчеты …

Флаги функций

Пример:

Здесь лучше вынести отд логику для админов и не-админов:

  • getUserDocuments

  • getAdminDocuments

Переписали, используя общий метод

(звезда) лучше не смотреть на стандартные php функции как на эквивалент кода (они далеки от идеала, например, array_search)

Пример рефакторинга “грязного кода”

Написание исключений

(warning) Т.е. важна типизация исключений, например к коду выше:

Exception генерируем, когда код дальше работать не может (когда нет того, что должно было быть обязательно), return false/null иначе


Пример на д/з: code-review

Изучите код и найдите недочеты (сам код рабочий): https://gist.github.com/DmitryKirillov/1aa9764ca9a8399f25b5fa04fc546d3a

<?php

declare(strict_types=1);

require_once '../vendor/autoload.php';

use GuzzleHttpClient;

class MessageStatusDTO
{
    private $status;

    public function __construct(array $status)
    {
        $this->status = $status;
    }
}

interface iMessageSender
{
    public function sendMessage(string $phone, string $name, string $message): void;

    public function getStatus(): ?MessageStatusDTO;
}

abstract class MessageSender implements iMessageSender
{
    private $statusDTO;

    abstract public function sendMessage(string $phone, string $name, string $message): void;

    public function getStatus(): ?MessageStatusDTO
    {
        return $this->statusDTO;
    }
}

class MessageSenderSMS extends MessageSender implements iMessageSender
{
    private $client;
    private $statusDTO;

    public function __construct()
    {
        $this->client = new Client();
    }

    public function sendMessage(string $phone, string $userName, string $message): void
    {
        try {
            if (
                $this->validatePhone($phone) &&
                $this->validateMessage($message) &&
                $this->validateName($userName)
            ) {
                $url = 'https://smspilot.ru/api.php'
                    .'?send='.urlencode($message)
                    .'&to='.urlencode($phone)
                    .'&from=1C-Start'
                    .'&apikey=310742H2L1I10JQHZU6G6W4ZT8EYM7RQ78ZG094QB30274CG8GR052MHA00Y0M90'
                    .'&format=json';
                $response = (string)$this->client->send(new GuzzleHttpPsr7Request('GET', $url))->getBody();
                $this->statusDTO = new MessageStatusDTO([
                    'time' => time(),
                    'response' => $response,
                ]);
                file_put_contents('/var/log/app/app.log', "Sent message $message to phone $phone", FILE_APPEND);
            }
        } catch (GuzzleHttpExceptionGuzzleException $e) {
            file_put_contents('/var/log/app/error.log', $e->getMessage(), FILE_APPEND);
        }
    }

    public function validatePhone(string $phone): bool
    {
        return is_numeric($phone) && strlen($phone) == 10;
    }

    public function validateMessage(string $message): bool
    {
        return strlen($message) < 140;
    }

    public function validateName(string $userName): bool
    {
        // Дмитрий
        // Анна-Мария
        // Фальк Дирске
        return (bool)preg_match('/[А-Я][а-я]+([s-][А-Я][а-я]+)?/u', $userName);
    }

}

$MessageSenderSMS = new MessageSenderSMS();

// Тесты
$phone = '9051034567';
var_dump($MessageSenderSMS->validatePhone($phone));

$name = 'Фальк Дирске';
var_dump($MessageSenderSMS->validateName($name));

$message = 'Тест';
var_dump($MessageSenderSMS->validateMessage($message));

// Отправка сообщения
$MessageSenderSMS->sendMessage('9051112233', 'Дмитрий', 'Тест');

?>

Мои замечания:

( № строки / замечание )

3 настройки такие вынести в общий (вызывающий) файл

48 $username передает, проверяется но далее в коде не используется

60 токен в коде (вынести в перем окружения, конфиг и т.п.)

62 use XXX вместо полного

74 валидаторы сделать private

70 отд ф-я для обработки ошибко нужна (set_error_handler)

70 абс путь к логу: жесткая привязка к имени файла (файловой системе)

76, 81, 89 константу сделать именованной

81 надо исп. mb_strlen

86 вместо комментариев с примерами — д.б. тесты , которые нагляднее все покажут

89 в регулярках не хватает начала и конца строки: ^$

Общее:

— косяки с точки зрения арх. и Solid

— аннотация у функций нет (комментариев)

— нет namespaces

— много классов в одном файле

Хорошие практики для PHP

(звезда) доп совет: избегать подавление ошибок @<function>

Полезные инструменты для PHP (валидаторы)

(звезда) См. “Google copilot”: напишет программу за тебя (надо зацынить): https://copilot.github.com/

Итак, что такое хороший код?


Дополнительные материалы

Tags

Нет комментариев

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.