@Allexio
Программист-путешественник.

Попинайте 2. Удалось ли исправить устаревший код, который забраковал работодатель?

Всем привет!

Здесь я спрашивал про то, что не так с моим кодом Попинайте. Работодатель сказал, что у меня код PHP устаревший. В чем именно проблемы?

Спасибо всем, кто ответил конструктивно )) Да, действительно код из 90-х годов был..

Поизучал немного "правильный PHP подход" (php-the-right-way ), вот что получилось: https://github.com/alex-verem/example-code/

Что изменил:

- Использование стабильной версии PHP 7.2 и выше
- Соблюдение cтандартов написания кода (PSR-1, PSR-2, PSR-4)
- ООП
- Использование пространства имен
- Менеджер зависимостей (composer)
- Абстрактная библиотека PDO для подключения к БД
- Централизованная обработка исключений, система логирования и режим рабочего сервера без вывода ошибок пользователю

Также в проекте используется шаблонизатор Twig и комментарии в формате phpDocumentor.

Пример включает следующие файлы:

1. config.php - Конфигурация приложения (настройки БД и прочая конфигурация)
2. index.php - Пример страницы (вывод городов и стран по поисковой фразе)
3. bootstrap.php - Подгрузка необходимых файлов на каждую страницу
3. autoload.php - Автозагрузчик классов
4. error-handler.php - Централизованная обработка исключений и ошибок
5. composer.json - Директивы для менеджера зависимостей Composer
6. /templates/ - Шаблоны для шаблонизатора Twig
6. /Turcalendar/ - Папка с классами приложения
-- Place/Place.php (представляет место, а именно город или страну)
-- Place/PlaceFactory.php (класс для добавления/удаления/поиска города/страны, представленные классом Turcalendar\Place\Place)
-- Place/PlaceFilter.php (фильтр, используемый в классе Turcalendar\Place\PlaceFactory для поиска объектов класса Turcalendar\Place\Place)

Буду благодарен за любую конструктивную критику ))
  • Вопрос задан
  • 516 просмотров
Решения вопроса 1
@Flying
Согласен с FanatPHP по поводу того что изменения, по сравнению с предыдущим вариантом, просто колоссальные, поздравляю!

Однако сразу видны те направления движения которые стоит рассматривать в первую очередь:

  1. Вам стоит лучше изучить возможности языка. PHP развивается очень динамично и в 7-й версии было добавлено огромное количество вкусных возможностей, про них стоит знать и ими стоит пользоваться
  2. Необходимо лучше изучить имеющуюся на данный момент экосистему готовых пакетов, это позволит вам использовать готовые, проверенные решения вместо изобретения своих велосипедов. Подключение Twig - первый шаг, но далеко не последний
  3. Вам обязательно стоит посвятить время изучению кода популярных проектов. Это многое может рассказать вам о подходах к решениям, паттернах и т.п. Конечно, параллельно нужно будет выяснять значения массы используемых терминов чтобы лучше понимать происходящее, это тоже пойдёт вам на пользу.
  4. Смотрите как развиваются другие технологии, которые вы используете, таблицами сейчас не верстают :)
  5. Рекомендую рассмотреть вариант использования для написания кода нормальной IDE, к примеру PHPStorm, это существенно улучшит вашу производительность и убережёт от массы ошибок


Теперь более предметно:

Оформление кода:

Вы утверждаете что код соответствует PSR-2 (кстати, актуальный - PSR-12), однако есть мелкие огрехи (раздел 2.2).

Оформление composer.json:

Лучше указывать требования к платформе, в вашем случае - как минимум версию PHP т.к. platform requirements проверяются Composer'ом при установке.

Поскольку ваш проект - не библиотека, то composer.lock тоже должен быть частью репозитория.

Про настройки autoload'а вам уже сказали, обращу только внимание на то что сейчас структура репозитория отражает PSR-0, а не актуальный PSR-4, при использовании PSR-4 ваш код проекта лежал бы в src.

Организация репозитория

Код проекта должен лежать за пределами web root, поэтому вам явно не хватает папки public с единственным index.php

Конфигурация

Конфигурирование через константы - весьма устаревший подход. К примеру представьте что вы разворачиваете этот код на production сервере и вам нужно сменить данные для подключения к базе данных не создавая локальных изменений в рабочей копии. Сейчас у вас это не получится. Выход: читаем про
12 factor app и, в частности, раздел config, а затем подключаем в проект, к примеру, vlucas/phpdotenv или symfony/dotenv

Сервисы

Использование bootstrap.php - хорошо, но для организации работы с сервисами сейчас как правило используются dependency injection контейнеры, даже PSR-11 для них есть. В текущем виде ваш подход слабо расширяем, а передача сервисов через глобальные переменные - так себе идея по многим причинам.

Понятно что вворачивать DI container для примера на три файлика - overkill, но с самим подходом вам следует ознакомиться. Из реализаций самая популярная сейчас - symfony/dependency-injection, но есть и альтернативы.

Автолоадер

Про autoload.php вам уже сказали, вместо него стоит корректно конфигурировать автозагрузку в composer.json и полагаться на то что Composer вам сгенерирует. Он ведь умеет всё это и оптимизировать при необходимости.

Внешние пакеты

Для новых проектов рекомендуется выбирать актуальные и поддерживаемые версии пакетов. Хотя Twig 1.x ещё поддерживается, тем не менее актуальная версия - 3.x.

Обработка ошибок

Попробовать сделать это самостоятельно, безусловно, полезно чтобы лучше разобраться как это работает, но для реальной работы лучше полагаться на проверенные решения, к примеру filp/whoops или symfony/error-handler

Типы данных

Важно помнить что PHP с версии 7.0 поддерживает указание скалярных типов для аргументов и
возвращаемых значений, с 7.1 - nullable типы и class constant visibility, а с 7.4 - типизированные свойства. Всё это гораздо надёжнее чем описание типов через аннотации и этим стоит пользоваться.

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

Data Objects

Сейчас Place и PlaceFilter по сути являются data объектами т.е. они не содержат самостоятельной логики, а просто переносят некие данные. При этом оба этих класса имеют пустые конструкторы (которые, кстати, лучше убирать), а загрузка данных организована через setter методы. Это позволяет изменить данные в них в произвольные моменты времени что вряд ли является желаемым поведением. Вместо этого подобные объекты лучше организовывать в виде immutable объектов (статья с примерами) чтобы не позволять ненужного нарушения целостности данных. Альтернативно, к примеру, для PlaceFilter может лучше подходить использование паттерна
Builder
, хотя в википедии не самый лучший пример.

Именование

Все мы, надеюсь, помним про две сложные проблемы, здесь как раз проявляется вторая из них. PlaceFactory ну никак не является реализацией одноимённого паттерна.

Оптимизации

Понятно что это мелочи, но явно видно что компиляцию запросов (1, 2) можно кэшировать вместо того чтобы перекомпилировать каждый раз.

References

PDOStatement::bindParam() принимает аргумент $variable как reference, а в коде в этот аргумент передаётся выражение что недопустимо.

Английский язык

Я специально почти везде по тексту давал ссылки на информацию на английском языке т.к. он - основной язык в нашей индустрии и его важно знать и использовать.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
FanatPHP
@FanatPHP
Чебуратор тега РНР
По сравнению с прошлым авриантом - небо и земля.
В целом этот код лучше, чем 99.9% того что пишут на тостере. No kidding.

Улучшить можно только по мелочи.
as Place в неймспесах лишнее. as нужно если ты другое имя даешь

Рекомендую перейти с bindParam на передачу массива в execute.
Так получится убрать дублирование кода в getPlaces(). Добавляя условие в запрос тут же добавляешь жлемент в массив. потом тупо скармливаешь этот массив execute(). Пример можно посмотреть здесь

если имена полей совпадают с имнами свойств класса, то вместо
while ($row = $stmt->fetch()) {
            $place = new Place();
            $place->setPlaceID($row["placeID"]);
            $place->setTypeID($row["typeID"]);
            $place->setName($row["name"]);
            $place->setTansliterated($row["tansliterated"]);
            $place->setCountryID($row["countryID"]);
            $place->setDescription($row["description"]);
            $places[] = $place;
}

можно написать
$places = $stmt->fetchAll(PDO::FETCH_CLASS, 'Place');

Но вообще это не очень гибко и лучше уже начинать мигрировать в сторону ORM

кстати, по ридми
в https://phptherightway.com/ написано Use the Current Stable Version (7.4)
PSR-4 это не стандарт написания кода

плюс обработку ошибок следует сделать конфигурируемой, в зависимости от окружения.
при разработке удобнее видеть ошибки на экране
плюс вообще конфиг надо сделать зависящий от окружения.
дома у тебя пароль от базы один, а на боевом сервере другой.
конфиг должен подкключаться в зависимости от окружения
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы