Ответы пользователя по тегу PHP
  • В чем преимущество "фабричного метода" перед простым созданием объектов?

    @xfg
    Проблема, которую решает фабричный метод - это возможность отложить создание объекта на попозже. Скажем, у вас имеется некий алгоритм, в котором на одном из шагов требуется создание объекта. Вы знаете, что у вас может быть несколько различных реализаций такого объекта. Тогда вы можете создать фабричный метод и использовать его в своем алгоритме для создания объекта. Это позволяет вам переложить ответственность по созданию объекта на подклассы и таким образом написать общий алгоритм не привязываясь к конкретной реализации объекта.

    Советую для понимания посмотреть пример в англоязычной википедии и там же реализацию на Java. Суть в том, что у вас есть игра лабиринт. И есть два режима игры, обычный - когда игрок может перемещаться из комнаты только в смежную комнату и магический режим - когда игрок может перемещаться в любую комнату. Алгоритм самой игры для обоих режимов один и тот же, а вот бизнес-правила перемещения игрока по комнатам - разные. Вы реализуете эти бизнес-правила в OrdinaryRoom и MagicRoom, а в самом алгоритме игры вместо того, чтобы создавать какой-то из этих объектов - вызываете просто абстрактный метод makeRoom(). А затем создаете два подкласса, которые будут реализовывать метод makeRoom и возвращать нужный тип комнаты. Таким образом вы получаете два различных режима игры написав один алгоритм.

    Вообще смысл данного шаблона довольно сложно понять. В интернете его почти всегда объясняют неверно. Прямо как пример на C#. Это совсем не то, о чем писали в книге банды четырех. Пример соответствующий тому о чем рассказывается в книге реализован в примере на Java. Нужно приложить усилия и постараться вникнуть в идею. Можно перед этим изучить Template method, его проще понять, там похожая идея, только там вы перекладываете часть поведения на подклассы, а здесь перекладываете создание объекта на подклассы. Factory method практически всегда используется в связке с Template method если мы говорим о классической реализации шаблона из книги банды четырех.

    Вообще шаблон не настолько распространенный насколько может показаться. Мне с 2002 года в моей практике не пригодился ни разу.
    Ответ написан
    2 комментария
  • Как лучше обращаться с параметрами метода?

    @xfg
    Аргументировать довольно просто и даже сослаться на конкретные абзацы из книги Implementing domain-driven design, а именно, то что автор пишет про сервисы. Автор книги довольно четко рассказывает почему сервисы не должны обладать состоянием.

    У вас классический сервис и у вас проблема, только лишь потому, что вы впринципе неправильно подошли к проектированию вашего сервиса.

    В первую очередь вам нужно спроектировать интерфейс CalculatorInterface

    interface CalculatorInterface {
      public function calculate($products, $city);
    }


    Далее вам нужно создать две реализации этого интерфейса CdekCalculator и EmsCalculator

    class CdekCalculator implements CalculatorInterface {
      public function calculate($products, $city) {
         // реализация расчета через cdek
      }
    }
    
    class EmsCalculator implements CalculatorInterface {
      public function calculate($products, $city) {
        // реализация расчета через ems
      }
    }


    И теперь ваш класс доставки будет выглядеть таким образом

    class Delivery {
      private $calculator;
    
      public function __construct(CalculatorInterface $calculator) {
         $this->calculator = $calculator;
      }
      public function calculate($products, $city) {
        //тут ещё какая-то логика, которая определяет условия доставки, если она вообще возможна
        return $this->calculator->calculate($products, $city);
      }
    }


    Предмета спора больше нет. Вы просто передаете в класс Delivery нужную реализацию калькулятора. Код выше это псевдоязык, не php и вообще только базовая идея того, как это должно быть. У вас наверняка тут же возникнет вопрос, как вам тогда выводить все расчеты доставки в ваш UI. Но извините не могу написать за вас всё приложение.

    Это базовые вещи в объектно-ориентированном программировании. Называется полиморфизм подтипов. Без этих знаний невозможно писать хороший объектно-ориентированный код. Лучше оба придите к выводу, что вам срочно нужно почитать какие-нибудь книги об объектно-ориентированном программировании и начать с самых основ, что такое полиморфизм и в частности полиморфизм подтипов.

    Вообще protected/private методы и ветвления из условных конструкций - это практически всегда показатель спагетти-кода и первый звоночек, что вы пишете процедурный код используя ООП синтаксис. Вы придете к этому выводу если будете разрабатывать через TDD, а затем сможете нагуглить ваши предположения у других программистов.
    Ответ написан
    3 комментария
  • Правильно ли использовать Web Socket PHP для онлайн игры на unity?

    @xfg
    Да, экшены так и делают. Сервер может отправлять около 20 снепшотов игрового состояния на клиент, то есть приблизительно каждые 50 миллисекунд. Чтобы изображение не дергалось отображение игрового состояния смешают назад в прошлое скажем на 50 миллисекунд, а клиент таким образом имеет возможность интерполировать перемещение игроков и анимации между текущим и предыдущим состоянием. Соответственно и сервер должен рассчитывать ввод пользователя в прошлом учитывая это смещение на 50 миллисекунд, учитывая время на путешествие пакета от клиента до сервера и возможно что-то еще, чтобы сделать стрельбу или маханием мечем максимально точным.

    Для пошаговой игры это избыточно. Там достаточно просто отправлять новое состояние с сервера только в ответ на ввод пользователя.

    Другой вопрос стоит ли для этого использовать websocket php, когда насколько мне известно unity итак предоставляет возможности для организации мультиплеера.
    Ответ написан
    Комментировать
  • Бросать исключение или возвращать коды ошибок/успеха? Является ли исключением то, что метод не может выполнить свою задачу?

    @xfg
    Исключения и есть современный способ реакции программы на ошибки, которые приводят к бессмысленности дальнейшего её выполнения. Соответственно, если у пользователя недостаточно средств для перевода, дальнейшее выполнение программы не имеет смысла, необходимо прервать её выполнение выбросив исключение. Когда этого механизма не существовало использовали коды ошибок.

    Вы также всегда имеете возможность посмотреть популярные open-source решения и убедиться какой точки зрения придерживается сообщество.
    Ответ написан
    1 комментарий
  • "Удаление" агрегата в DDD?

    @xfg
    Идея у вас верная, только вместо защищенного onDelete, делают обычный публичный метод remove, который ничего не делает, а просто выбрасывает доменное событие. Этот метод как и все остальные просто вызывается внутри application сервиса, а затем удаляется из репозитория.


    $catalog->remove();
    $repository->remove($catalog);


    Чтобы была атомарность, нужно события выбрасывать после того, как изменения были сделаны в базе данных. Само событие нужно также в базу сохранять, в одной транзакции с созданием/обновлением/удалением данных об агрегате. Какая-то штука затем должна удалять обработанные события. Какая-то штука должна проталкивать события из базы, которые почему-то там уж слишком долго живут - подписчикам. Обычно происходит при сбое, когда в базу событие записалось, а подписчикам не отправилось.

    Какая-то сложная замороченная хрень с консистентностью по событию получается. Еще бывает, что тот кто принял событие не может его обработать, так как нарушается какой-нибудь инвариант в его доменной модели и шлет событие о неудаче, на которую должен отреагировать тот от кого пришло событие этому. Больше трех шагов и совершенно непонятно, какие события куда летают, кто на них реагирует и что вообще происходит в такой системе.

    Поэтому я бы попробовал Saga Orchestration, это такая штука, в которой пошагово описывается какие сервисы нужно вызвать, чтобы успешно завершить бизнес-операцию, а также какие действия нужно выполнить на каждом шаге в случае сбоя одного из сервисов участвующих в бизнес-операции для того, чтобы вернуть всю систему в консистентное состояние.
    Ответ написан
    2 комментария
  • В чем разница между 'фабричным методом' и 'простой фабрикой'?

    @xfg
    Фабричный метод

    Фабричный метод позволяет избавить клиента от необходимости знать о процессе создания конкретных объектов. Клиент зависит только от интерфейса фабричного метода. Клиент используя фабричный метод может создавать и работать с любым объектом реализующим интерфейс IMobile.

    Простая фабрика

    Строго говоря в GoF нет такого паттерна как "простая фабрика". Но тем не менее это откуда-то пошло и подобный кусок кода стали называть "простой фабрикой", об этом есть упоминание в википедии и что в это можно инкапсулировать сложный процесс создания объекта, например когда он зависит от настроек в конфигурационном файле или от пользовательского ввода.

    Я не вижу паттерн в простой фабрике. Я вижу лапшу из операторов ветвления. Вместо этого пользовательский ввод или настройки конфига можно напрямую без свалки из операторов ветвления сконвертировать в имя требуемого класса фабричного метода и уже его объект передавать клиенту таким образом избавившись от спагетти-кода в виде "простой фабрики".

    Невозможно сказать кто придумал "простую фабрику" и зачем. То, что в ней написано, это спагетти-код. То, что в ней написано, можно решить более элегантным способом без свалки из операторов ветвления.
    Ответ написан
    5 комментариев
  • Какой PHP-микро-фреймворк взять для простенького REST API с авторизацией, и чтобы не из "большой тройки"?

    @xfg
    Сделал бы на Express.js, но там тяжелые запросы к БД ожидаются, а в этих случаях ноду вроде использовать не рекомендуют.

    Делайте. Вы неверно понимаете как работает node.js. Наоборот, если у вас тяжелые запросы, то node.js это то, что нужно. Ввод/вывод в node.js неблокирующий, это значит, что вместо ожидания ответа от сети или файловой системы, вы сможете обслуживать других клиентов.

    Не рекомендуют использовать node.js для сложных математических/физических и тому подобных расчетов требовательных к процессорному времени, что соответственно будет блокировать процесс пока процессор занят обсчитыванием этой задачи. Что вообще ни разу не про веб. И даже в таком случае, можно разделить такую задачу и выполнить её за несколько тиков или даже в дочернем процессе.
    Ответ написан
    1 комментарий
  • Как лучше организовать рабочее окружение для веб разработчика?

    @xfg
    linux + docker + git вот и всё окружение.
    Ответ написан
    Комментировать
  • Как работать с websocket в php без библиотек?

    @xfg
    Прочитать соответствующий RFC https://tools.ietf.org/html/rfc6455 чтобы понять, как происходит рукопожатие и какие байты в переданном сообщении за что отвечают. После этого будет понятно как написать реализацию. Я досконально уже не помню, но фактически от клиента приходит обычный http запрос с определенными заголовками, сервер разбирает этот запрос и если всё ок, то сохраняет открытое соединение в массив, если нет, то отправляет соответствующий ответ и закрывает соединение. Дальше по открытому соединению начинает сыпаться поток байтов от клиента их нужно разбирать, чтобы понять длину сообщения, сами данные переданные в фрейме, закончился фрейм или еще нет и тому подобное. Обратно также кодировать данные в поток байтов и отправлять по открытому соединению. Каждый байт в переданном фрейме несет определенный смысл. Обо всем этом подробно написано в RFC, но на английском. Вообще это хорошо примерно понимать как работает, но глупо писать такую низкоуровневую реализацию, когда есть готовые. Такие вещи развивают и поддерживают годами. Вы же не пишите HTTP серверы, а берете готовые вроде nginx и тому подобное.

    В каком месте можно полученные данные подготовить к записи в бд.

    Как сделать, что бы на стороне клиента, один websocket отвечал за сообщения, другой за статьи. (Или за эти два действия отвечает один websocket, тогда как мне на сервере это различать).

    Вебсокет это низкоуровневая штука, для передачи потока байтов от клиента на сервер, в отличии например от HTTP, где есть заголовки и тело сообщения. Поверх вебсокета нужно делать еще один протокол или самописный или выбрать один из готовых. Это проще говоря, то как выглядят ваши фреймы (сообщения), которые вы отправляете с клиента на сервер и назад. Например клиент может отправлять такой фрейм:
    ["id", "controller/action", {param1: value1, param2: value2}]

    в ответ получать
    ["id", "OK"]
    если запрос был обработан успешно или
    ["id", "ERR", {error: "action not found"}]
    если произошла ошибка. По переданному id в массиве, можно понимать, к какому запросу относится ответ.
    Для уведомлений (событий) сервер может отправлять клиентам что-то такое
    ["user_added", {user: {...}}]
    и т.д. Этот протокол необходимо придумать самому или выбрать из готовых (популярных пока нет) и написать его реализацию (клиентскую и серверную часть) или опять же взять уже готовую.

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

    Но это уже всё должно быть, просто возьми real-time фреймворк. Там за тебя написали и websocket сервер и протокол поверх него и экшены уже есть. Всё низкоуровневое уже готово. Бери и пиши приложение. В nodejs самый популярный это например https://github.com/socketio/socket.io, а в php я не знаю, но уверен, что тоже есть что-то популярное.

    Своё написать не получится, без опыта и без попыток сделать приложение на чем-то готовом. Нужно как минимум прочитать RFC и посмотреть реализации других разработчиков. Для этого нужно быть кем-то больше, чем "программистом сайтов".
    Ответ написан
    1 комментарий
  • Фреймворк, макро-Фреймворк для разработки портала?

    @xfg
    Да по хорошему нужно делать на микрофреймворке. Самый популярный из них slim. Весь остальной необходимый функционал собирают из библиотек. Но желательно иметь представление о многоуровневой архитектуре иначе будет спагетти-код. Без опыта да, лучше выбрать yii или laravel. Поскольку документация ответит на все ваши вопросы от старта и до релиза сайта. Но поскольку документация описывает RAD (rapid application development) будьте готовы, что на выходе получится спагетти-код. Но на микрофреймворке без опыта будет еще хуже.

    Выбирайте любой. Разницы особой нет. У yii сильное русскоязычное сообщество. Разработчики и сообщество иногда делятся информацией о том как строить многоуровневую архитектуру. Получите опыт и придет осознанное понимание что достаточно микрофреймворка.

    Обычный путь разработчика: plain php -> mvc framework -> microframework -> plain php
    Ответ написан
    1 комментарий
  • Структурирование исключений. Что вы указываете в качестве exeption code?

    @xfg
    Понял, вас интересует для чего нужно свойство code в исключениях. К сожалению, я так и не смог нагуглить точного описания для чего в php это свойство добавили к исключению и для каких целей php предлагает его использовать сегодня.

    Но я думаю, что так сложилось исторически. В PHP 3 появились самые первые средства для работы с ООП. Не такие мощные и функциональные, какими вы их видите сегодня в PHP 5/7. Тогда механизм исключений в php не позволял добавлять множественные catch блоки. Это не имело смысла, так как в php не существовало возможности типизировать аргументы функций. Соответственно единственная возможность отличить одно исключение от другого внутри блока catch это использовать свойство code. Время прошло, добавили типизацию, множественные catch блоки, а свойство code осталось, как и многое другое в php. Использовать свойство code в настоящее время вряд ли имеет какой-либо смысл.
    Ответ написан
    Комментировать
  • Как Вы задаете правила для пользователей?

    @xfg
    Раньше RBAC был. Модуль искал в файлах с контроллерами все экшены и отображал в виде списка. Чекбоксами отмечали для каких экшенов сгенерировать права. Дальше права назначались на роль. Роли на пользователей. В коде прежде чем запустить любой экшен был хук, который проверял есть ли у роли право с названием "модуль:контроллер:экшен". Есть - выполняем. Нет - 403 Forbidden.

    Была куча проблем. И вообще позже заметили, что назначаем права один раз и больше никто ничего не меняет. Потому что все эти права это бизнес-логика нашего приложения. Мы же не cms делаем и не нужно пытаться выносить эти правила из кода в веб-интерфейс. Меняются бизнес-правила - меняется код. Теперь всё проще. Мы пишем мидлвары например аутентифицирован/заблокирован и т.д. Мидлвары просто вешаются на нужный экшен и передают управление друг другу по цепочке до самого экшена и каждый из мидлваров может зарубить запрос. Это оказалось гибче, проще и удобнее, чем тыкать сотни чекбоксов в веб интерфейсе ведь в мидлваре можно написать абсолютно любой код. Потому что не бывает так, что вам надо сегодня заблокированным запретить комментировать фото, завтра разрешить, а послезавтра снова запретить. Даже если надо, то мы напишем соответствующий мидлвар, повесим на экшен и выкатим в продакшен, а не будем тыкать каждый день чекбоксы в малопонятном веб-интерфейсе прав доступа. Тем более заказчик всё равно не понимал, что это за чушь NewsModule:NewsController:addAction сколько бы ему не объясняли, что это название разрешения на добавление новостей, а нотация указывает для какого модуля, контроллера и экшена, а названия разрешений такие убогие, потому что генерируются автоматически из исходного кода. У него взрывалась голова и он говорил, ладно ребята, надо будет поменять, я вам наберу :)
    Ответ написан
    Комментировать
  • Как правильно настраивать дев-окружение для веб-разработки?

    @xfg
    Не думайте о доменах. Вы смешали администрирование и программирование. Не нужно никакого dev сервера. Делайте работу на локальной dev машине, отправляйте изменения в удаленный репозиторий и всё. Можете вообще не устанавливать nginx/apache и т.д. на локальную dev машину, чтобы не забивать голову всякими доменами, а проект запускать под встроенным PHP сервером например из корня проекта и тогда будете обращаться к вашим сервисам по адресу localhost:port/service1/index.php, localhost:port/service2/index.php и т.д.

    Домены будете создавать уже на продакшене. В простейшем случае склонируете на продакшн машину удаленный репозиторий проекта и в конфигах nginx нужно будет написать что-то типа такого

    server {
      server_name company.com;
      root /home/www/company/frontend;
     ...
    }
    server {
      server_name admin.company.com;
      root /home/www/company/backend;
     ...
    }
    server {
      server_name service1.company.com;
      root /home/www/company/service1;
     ...
    }
    server {
      server_name service2.company.com;
      root /home/www/company/service2;
     ...
    }


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

    Так и делают. Разработчикам не нужен никакой dev сервер. Они клонируют репозиторий, делают что-то локально у себя и отправляют изменения в удаленный репозиторий. Для тестеров и всяких менеджеров просто поднимают так называемый stage-сервер где они и тестируют приложение, но это тоже самое что и продакшн сервер, просто доступ к нему только внутри компании. Можно настроить continuous integration чтобы все изменения из репозитория в master ветке автоматически бы приводили к деплою приложения на stage и продакшн сервера. Примерно так в общих словах устроена веб разработка.
    Ответ написан
    22 комментария
  • Как разграничивать права доступа в API?

    @xfg
    Сделать бекенд и фронтенд контроллеры. Через бекенд контроллер можно менять все поля, через фронтенд контроллер не все. Пользователей и Администраторов сделать используя RBAC. Пользователи могут вызывать фронтенд контроллеры и не могут бекенд контроллеры.
    Ответ написан
    6 комментариев
  • С чего начать рефакторинг?

    @xfg
    Без знаний архитектурных принципов делать рефакторинг бессмысленно. Это будет переливание из пустого в порожнее. Уже упомянули прочитать и добавить MVC если его нет. Для CRUD-приложения будет достаточно. Потом можно почитать про GRASP и SOLID. Потом про DDD, которое дает целостное представление об архитектуре. Всё в контексте объектно-ориентированной парадигмы. Для других парадигм ничего посоветовать не могу, не достаточно опыта.
    Ответ написан
    Комментировать
  • Как писать тесты?

    @xfg
    Существуют принципы проектирования позволяющие отделять бизнес-логику от хранения. Тесты будут лаконичные и наименее ломкие. Когда невозможно, мокают методы для работы с бд.

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

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

    Почему? Потому что ни какие тесты вам не дадут 100% гарантии, а вот устроить себе вечный ад из поддержки кучи хрупких тестов, которые десятками валятся от малейшего изменения - запросто. Выбираем золотую середину. Вариант выше, дает достаточную уверенность, что скорее всего всё работает как ожидается. Важный функционал тоже работает как ожидается.

    Если вы будете покрывать тестами всё, что только можно, я вам гарантирую, вы через месяц начнете выкатываться в продакшн с красными тестами.
    Ответ написан
    2 комментария
  • В чем косяк (полиморфизм)?

    @xfg
    В программировании несколько различных видов полиморфизма. Вам следовало уточнить, о каком из них идет речь. Собеседующий с вами конечно же не совсем корректен, так как простейшая форма полиморфизма в вашем примере все же присутствует. Другое дело, что в php под полиморфизмом обычно понимают полиморфизм подтипов. Выглядит так
    interface UnitInterface {
      public function setHp();
    }
    class Warrior implements UnitInterface {
      public function setHp() {...}
    }
    class Medic implements UnitInterface {
      public function setHp() {...}
    }
    
    class MainProgram {
      private $unit;
    
      public function __construct(UnitInterface $unit) {
        $this->unit = $unit;
      }
      public function run() {
        return $this->unit->setHp();
      }
    }
    
    echo (new MainProgram(new Warrior())->run();

    Идея в том, что конструктор класса MainProgram ничего не знает о конкретных реализациях ваших юнитов. Он знает только о том, что они должны удовлетворять интерфейсу UnitInterface. В будущем если у вас хорошо спроектирован интерфейс, то вы сможете заменить одну реализацию юнита на другую, не изменяя код внутри MainProgram. Таким образом, вы соблюдаете принцип открытости/закрытости из SOLID, который говорит, что классы должны быть открыты для расширения, но закрыты для изменений.
    Ответ написан
    1 комментарий
  • Можно ли сделать псевдо-"движок" сайта на include?

    @xfg
    Ваше решение уже совсем из прошлого века. Возьмите https://www.slimframework.com/ и тут посмотрите как сделать layout stackoverflow.com/a/37163209/2868530

    Будет похоже на что-то современное.
    Ответ написан
    Комментировать
  • Как правильно протестировать такой класс?

    @xfg
    Тестировать нужно только публичные методы класса. Вообще лучше внедрить внешние сервисы которые отвечают за сохранение и выгрузку в конструктор этого класса, замокать работу с базой/выгрузку и написать чистый юнит-тест. Такие тесты будут быстрыми, короткими и легко поддерживаемыми. На самый важный функционал сайта потом можно будет написать несколько медленных функциональных тестов. И всё, этого вполне достаточно. Сейчас же вы хотите интеграционный тест написать. Уродливый, медленный, с кучей лишнего бойлерплейта. В этом очень мало смысла. Нужно писать юнит-тесты на свой код и только для важных фич типа оплаты или регистрации писать функциональные тесты. Интеграционные тесты не писать вообще. Помните, каждая лишняя написанная вами строка кода, требует вашей дальнейшей поддержки в будущем. Пишем тесты, которые дают наибольший результат при минимуме кода.
    Ответ написан
    Комментировать