Ответы пользователя по тегу PHP
  • Зачем (в наше время) везде писать phpdoc комменты?

    @EvgeniiR
    https://github.com/EvgeniiR
    Посторонним В.,
    Мой вопрос не про комментарии в клиентском коде, а именно про пхп-док блоки для классов и их членов, т.к. они почему-то часто считаются стандартом де-факто.

    Дублирование типов в php-доках не нужно и никаким стандартом не является(если только внутренним-корпоративным).

    P.S. И да, если бы программисты хоть иногда, перед тем как что-то заюзать задавали себе вопрос, зачем они это делают, и нет ли способов делать дела лучше, индустрия разработки была-бы совсем другой.
    Ответ написан
    1 комментарий
  • Как подкорректировать Router?

    @EvgeniiR
    https://github.com/EvgeniiR
    private $classConfig;

    Использовать типизованные параметры

    $this->classConfig = new Config;

    Использовать DI и интерфейсы, ниже подробнее.

    $arr = $this->classConfig->getConfigs('routes'); // Вывод роутеров (Ниже пример)
        if ($arr) {
          foreach ($arr as $key => $value) {
                	$route = '#^' . $key . '$#';
                	$this->routes[$route] = $value;
            	}
        }

    Плохо. Почитайте про GRASP паттерны, особенно Information expert и, на будущее - Coupling/Cohesion(совсем-совсем на будущее, хотя можете глянуть докладик - тык, думаю сойдёт).
    Без регулярных выражений можно обойтись.
    Информация должна быть там где данные, не нужно доставать данные из какого-то объекта чтобы принять решение на основе их - попросите объект с данными принять решение.

    Как вариант - вместо этого должен инжектиться (по интерфейсу) какой-нибудь ControllerInfoProvider, или RoutesProvider, с методом вроде getControllerInfo($requestData);, который вернёт экземпляр DTO вроде
    /**
     * @psalm-immutable
     */
    class ControllerInfo {
        public string $controllerClass;
    
        public string $controllerMethod;
    
        /**
         * @var string[]
         * @psalm-var class-string[]
         */
        public array $beforeControllerMiddlewares;
    
        /**
         * @param string[] $beforeControllerMiddlewares
         * @psalm-param class-string[] $beforeControllerMiddlewares
         */
        public function __construct(string $controllerClass, string $controllerMethod, array $beforeControllerMiddlewares = [])
        {
            $this->controllerClass = $controllerClass;
            $this->controllerMethod = $controllerMethod;
            $this->beforeControllerMiddlewares = $beforeControllerMiddlewares;
        }
    }

    Ну и ещё можно postControllerMiddlewares, или, с более понятным названием вроде ResposeAwareMiddlewares, потому что миддлвари это хорошо.
    Ещё там аннотации для статического анализатора psalm, потому что ловить ошибки на этапе разработки запустив ./vendor/bin/psalm лучше чем ловить их после запуска кода, особенно в продакшене.

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

    $url = trim($_SERVER['REQUEST_URI'], '/');
    foreach ($this->routes as $route => $params) {
    if (preg_match($route, $url, $matches)) {
    $this->params = $params;
    if (isset($this->params['folder'])) {
    $this->params['folder'] = '\\' . ucfirst($this->params['folder']);
    }
    return true;
    }
    }
    return false;

    Не нужно в Роутере глобальные переменные парсить. Пусть до роутера это дело обработает какой-нибудь RequestParser, и создаст DTO-объект запроса, можно по psr-7, можно без него, чтобы проще было. Это не критично.

    По поводу параметров в контроллере - стоит обратить внимание на argument resolvers, и не заниматься этим в роутере. Вещь отличная и удобная, свои аргумент резолверы тоже пишутся просто.

    echo "<b>" . ucfirst($this->params['controller']) . "Controller</b> or <b>" . $action . "</b> not found.";
            return false;

    Не роутера это дело, в stdout писать. Пусть кинет исключение, а ответ пользователю сформируется уровнем выше.

    $path = 'app\controllers' . $this->params['folder']

    Роутер не должен определять структуру директорий. Используйте полное имя в конфиге роутов, т.е. SomeController::class.
    Сваливать все контроллеры в одну папочку занятие сомнительное.

    Почему (зачем) предпочитаю хранить роутеры в файлах (в базе данных), если можно автоматизировать

    Как удобнее так и храните. Конфиг, база, аннотации, php массив - без разницы

    Нужно ли корректировать код, указанный выше или и так сойдет?

    Чем больше сделаете из описанного выше - тем лучше.

    Что лучше: загружать контроллер из роутера или загружать класс контроллер (проверки...), который будет загружать необходимый контроллер?

    Получили ControllerInfo, обратились в ControllersRegistry->getControllerClass(), получили инстанс контроллера.
    Далее либо вызвали нужный метод, либо, если всё же сделали - попросили аргумент резолвер распарсить и инициализировать аргументы:
    $args = $this->argumentsResolver->resolve($controller, $method, $request);

    Вызвали миддлвари, что-то вроде этого: https://github.com/thephpleague/tactician/blob/mas... , вызвали контроллер:
    $response = $controller->{$action}(... $args)
    Ответ написан
    Комментировать
  • Считается ли это плохим кодом?

    @EvgeniiR
    https://github.com/EvgeniiR
    get()

    Чего он делает? И где типы и имена параметров, где возвращаемые типы?

    preparePath() (он просто указывает путь, где именно брать данные (это не бд)),

    Ничего не понятно даже с комментарием.

    validate()

    Кого validate?

    paginate()

    И чего оно делает?

    Правильно ли я понимаю, что это грубое нарушение SRP?
    ...
    Ну а в самом изначальном классе оставить moveToStorage + get методы.

    Оставьте гейтвей к базе который умеет из неё данные доставать и в нужном формате возвращать, остальное в отдельные классы.
    Ответ написан
    Комментировать
  • Есть ли в PHP общий класс от которого наследуются все классы?

    @EvgeniiR
    https://github.com/EvgeniiR
    Хочу здесь принимать объекты конкретного типа, а в интерфейсе нельзя указать общий тип для всех классов

    Вы нарушаете Liscow Substitution Principle когда в наследниках требуете более конкретный тип чем в родителе, не нужно так делать.

    Для экземпляров любых классов есть тайп хинт object, но он вам не поможет по описанной выше причине.

    Смысла в общем интерфейсе для всех репозиториев нет.
    Ответ написан
    5 комментариев
  • Как вытащить jsonschema со встроенными ссылками?

    @EvgeniiR
    https://github.com/EvgeniiR
    Json Shema - валидный JSON, поэтому:
    - Декодировать json(схему)
    - Пробежаться по полученному вложенному массиву, и каждый раз когда в ключе попадается "$ref", заменять это поле целиком на объект, путь которого в значении(ссылке).

    Придётся лишь чуть-чуть повозиться с тем чтобы корректно преобразовать адресс ссылки в абсолютный путь в массиве.

    Upd: Но возможно это не самая лучшая затея, т.к. $ref это не просто сокращение синтаксиса, но ещё и возможность рекурсии (дока), которую вы не сможете развернуть без ссылок.
    Ответ написан
    5 комментариев
  • Можно ли в репозиторий использовать для сохранения обьектов?

    @EvgeniiR
    https://github.com/EvgeniiR
    Save your repository from save

    Можно ли понимать репозиторий как это по сути промежуточное звено между приложением и внешним хранилищем

    Это будет уже не репозиторий а гейтвей к хранилищу.
    Репозиторий - коллекция доменных объектов.

    Denis ,
    можно ли заменить CRUD репозиторием? Нет

    CRUD можно заменить готовыми решениями(postgrest etc.).
    Ответ написан
    2 комментария
  • Почему советуют не выбирать yii2 для разработки?

    @EvgeniiR
    https://github.com/EvgeniiR
    1. Yii мёртв. Устарел лет на 10 по подходам и кодовой базе, и не развивается.
    2. Плохой дизайн. Глобальное состояние для всего, наследование от базового класса модели, валидация через массивы там же, наследование для расширения всего и вся и прочая чушь. Отсутствие многих удобных фич типа нормального DI/аргумент резолверов, чего только стоит гибкость конфигурации сервисов в Симфе.
    3. Свои велосипеды вместо чего-нибудь готового
    4. Все компоненты прибиты гвоздями и не заменяются своими. Это делает код на нём нерасширяемым и нетестируемым(Ну то есть в теории переписав пол фреймворка и 100500 своих адаптеров можно писать нормально, но те кто хочет писать нормально просто уходят с Yii).
    5. Слабое комьюнити которое сидит на нём потому что не осилило ничего другого / генерирует CRUD`ы через Gii(Заменить бы их уже не postgrest и прочие обёртки над базой) / инертные кодеры которым без разницы чего делать лишь бы на хлеб хватало.
    6. Все фреймворки далеки(очень) от идеала, но Yii сильно отстаёт от прочих.
    Ответ написан
    Комментировать
  • Как программировать PHP/MYSQL чтобы потом можно было синхронизировать БД?

    @EvgeniiR
    https://github.com/EvgeniiR
    Что и с чем синхронизировать?
    Чтобы id не пересекались на нескольких нодах можно использовать UUID.
    Можно ещё всякое типа microtime+идентификатор ноды, но вряд ли нужно без каких-то серьёзных требований по производительности
    Ответ написан
    6 комментариев
  • В каких случаях при разработке сайта на PHP лучше использовать не MVC (и его подобия), а какой-нибудь другой подход?

    @EvgeniiR
    https://github.com/EvgeniiR
    В каких случаях при разработке сайта на PHP лучше использовать не MVC (и его подобия), а какой-нибудь другой подход?

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

    Большинство даже в MVC никогда не пыталось разобраться, и думает что это когда в проекте 3 папочки есть(model/ view/ controller/).
    Или что MVC было придумано для веб приложений (а это не так :) ).
    Ответ написан
    8 комментариев
  • Можете ли оценить код, и сказать как сделать его лучше?

    @EvgeniiR
    https://github.com/EvgeniiR
    1. Type hints для свойств через phpdoc использовать. Или обновиться до 7.4 и использовать type-hints средствами языка.
    2. Type hints для аргументов методов и возвращаемых значений использовать.
    3. Написать класс Logger и использовать как зависимость. Желательно чтобы он был адаптером для LoggerInterface(psr3)
    4. Методы и переменные нормально называть, половину пхп-доков повыпиливать.
    5. Логику(ветвления) либо выносить в зависимости, либо писать юнит-тесты на неё. Учитывая что этот класс ещё и в базу лазит через AR, логики(ветвлений) тут лучше не делать.
    6. $value передавать не в конструктор а в конкретные методы где оно нужно, чтобы не пересоздавать экземпляр класса для каждого списания.
    7.
    private function initiator($system = true){
        if ($system){
            return User::find()->where(['login'=>'System'])->one();
        }
        return Yii::$app->user->identity;
    }


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

    5. это вы имеете ввиду что в методах new используется?

    Да, внедряйте зависимости через Dependency Injection. То есть не создавайте new CashLog а требуйте экземпляр логгера с нужными вам методами в конструктор. Если логгеру нужен юзер а у вас в классе только его идентификатор - желательно бы передавать в логгер идентификатор, а дальше пусть его забота будет, чтобы не нагружать этой логикой ваш класс.
    В принципе о связности/тестируемости вам рановато пока думать, в будущем стоит присмотреться к принципам Coupling/Cohesion.
    Ответ написан
    Комментировать
  • Как правильно задавать свойства классам php?

    @EvgeniiR
    https://github.com/EvgeniiR
    Правильно - задавать свойства в конструкторе класса. А публичные поля или методы get/set это нарушение инкапсуляции.
    Экземпляр класса должен полноценно работать и иметь в себе все необходимые для работы данные сразу после его создания.

    Есть исключения когда мы имитирируем структуры данных через классы-DTO(В PHP нету встроенных типов структур/data-классов), но заполнение и там должно быть через конструктор.

    Странно, здесь же на тостере Когда использовать static метода? например, пишут что правильная работа с ооп, это как раз задавать параметры через методы, а не через конструктор.

    На Тостере много чего пишут, не стоит всему верить, точнее стоит ничему не верить, а анализировать :)
    В плане каких-то конкретных терминов часто можно узнать очень много интересных вещей загуглив первоисточник термина/историю появления(в т.ч. причины).

    Постарайтесь определить цели и понять как эти варианты помогают вам эти цели достичь. А "правильных" вариантов без заданной цели не может быть.
    В будущем в этом плане стоит смотреть на концепты Coupling/Cohesion, и почему они важны(книжки Clean Architecture, Clean Code и т.п., так же принцип Low Coupling + High Cohesion входит в паттерны GRASP).

    По теме - задавая параметры через set() методы мы:
    - Подразумеваем что все пользователи нашего класса знают какие у него параметры, и более того - что они означают. Это создаёт большую нагрузку на пользователей класса, усложняет клиентский код.
    - Позволяем в любом месте где используется класс поменять что-то внутри, и неожиданно обнаружить ошибки в других местах системы. А ещё настройки могут конфликтовать друг с другом, и одни свойства зависеть от других. Установка одного поля может требовать установки другово, и такие вещи стоит указывать на уровне интерфейса, то есть сделать вместо условных setStartDate() и setEndDate() метод setDatePeriod(DatePeriod period), даже если внутри класса это два поля.

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

    И ещё - есть разница между методами которые меняют состояние класса, и конкретно "сеттерами". Семантика важна. То что внутри класса что-то меняется нас не интересует. Нам важно лишь чтобы выполнилось то, что мы ожидаем от класса. Если мы хотим обновить статью, мы вызовем метод updateArticle(), или какой-нибудь changeTitle(), например. Если мы хотим кофе мы скажем машинке makeEspresso(), а не будем указывать количество зёрен, упростив таким образом клиентский код.
    Ответ написан
    1 комментарий
  • Как правильно получить составные данные из базы и сложить их в одну сущность?

    @EvgeniiR
    https://github.com/EvgeniiR
    Немножко не ответ
    Для класса PostsModel:
    Автор Поста является потомком Поста.
    Пост для автора - Родитель.
    Пост для даты поста - Родитель.

    *Тут была реплика про уровень комьюнити тостера, и даже "Кураторов Тега", которые позволяют себе нести вышепроцитированную чушь, но я решил её опустить, т.к. разумный человек и так поймёт со временем*

    Совет - развивайте критическое мышление и фильтруйте информацию, не верьте всему в интернете, даже если у этого 100500 плюсов на каком-нибудь ресурсе типа Хабра/SO/Medium/Тостер и т.п.


    Алексей Коновалов,
    нам нужна одна точка получения данных.

    Нам не нужна "единая точка получения данных". У вас есть ваши модели(сущности, доменные модели) - это компонент вашей системы отвечающий за часть бизнес-логики.
    Так же у вас есть представления данных. Представлений данных может быть множество. Не нужно реиспользовать одну и ту же модельку для логики/записи и для чтения(представления, UI).
    Выбрали данные из БД(raw SQL/Eloquent/DQL смапленный на DTO, что угодно)
    ->заполнили ими структуру которая сформирована исходя из нужд клиентского модуля(Frontend`а вашего),
    -> привели к нужному формату(json etc.) и отдали на фронтенд

    Он работает с базой данных и делает все, что связано с постами. И тут как раз проблема.

    Очень хорошо что вы подметили что это проблема, серьёзно. Именно по этому "Active Record" - антипаттерн.
    Некоторые, к большому сожалению, годами к этому не могут придти, того больше - отстаивают каждое архитектурное решение какого-нибудь Laravel не с позиции инженера, а с позиции религиозного фанатика.

    Модели для записи ничего про HTTP, Request и представления знать не должны. Модели на чтение по сути просто структуры данных.
    Ответ написан
    Комментировать
  • Как провалидировать jsonShema?

    @EvgeniiR
    https://github.com/EvgeniiR
    Чтобы запретить присылать лишние поля, вам нужно описывать объекты в схеме, объявлять их свойства(properties) и указывать в схеме параметр additionalProperties => false

    Пример из документации

    {
      "type": "object",
      "properties": {
        "number":      { "type": "number" },
        "street_name": { "type": "string" }
      },
      "additionalProperties": false
    }


    https://json-schema.org/understanding-json-schema/...
    Ответ написан
  • Можно ли сократить код?

    @EvgeniiR
    https://github.com/EvgeniiR
    /**
     * @param Text|TextRun $arg
     */
    function printText($arg): void {
      if($arg instanceof TextRun) {
        foreach($arg as $el) { printText($el); }
      } elseif ($arg instanceof Text) {
        echo $arg->getText();
      } else { 
        throw new \InvalidArgumentException('...');
      }
    }
    Ответ написан
    Комментировать
  • Где найти ресурс с подборкой "правильных" решений на PHP?

    @EvgeniiR
    https://github.com/EvgeniiR
    как правильно реализовать авторизацию с токенами, сделать корзину в онлайн магазине и.т.д,

    Есть бесконечное число вариантов сделать корзину в онлайн магазине, и бесконечное число переменных влияющих на принятое решение.
    Вы знаете что такое комбинаторный взрыв? Это именно то что произойдёт если вы будете пытаться запомнить все реализации любых задач, не говоря уже о том что любая новая задача остаётся по прежнему уникальной(в какой-либо степени), и просто копирование решения из похожей задачей почти никогда проблемы не решит, а в связи с упомянутой выше проблемой в виде множества обстоятельств влияющих на набор выбранных решений, слепое их копирование скорее сильно навредит развитию проекта, чем поможет в сколь-либо обозримом будущем.

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

    P.s. кстати, последнее что я смотрел про проектированию корзины - вот. Полезный ролик чтобы взглянуть на вещи по новому, но не для того чтобы копировать решение. естественно.
    Ответ написан
    4 комментария
  • Правильный ли пример реализации middleware?

    @EvgeniiR
    https://github.com/EvgeniiR
    Мало что можно сказать по паре десятков строчек кода, даже без гитхаба с проектом. Пара примечаний:

    1. Очень смущает парметр $args в методе execute(). Массив, типы и элементы внутри которого не определены, потенциальный источник многих проблем.
    2. Зачем прокидывать DI в контроллер?

    И совет - попробуйте psalm. Ставится и запускается на дефолтных настройках в два клика, позволит избегать огромного количества проблем с типами, и понимать где проблемы, никакие обращения к необъявленным явно элементам в массиве там уже не прокатят
    Ответ написан
  • Как использовать функцию в роли класса (объекта)?

    @EvgeniiR
    https://github.com/EvgeniiR
    class App {
        public function getLocale();
    }
    
    function app {
        return new App(); // в вашем примере тут синглтон
    }


    Воспользуйтесь переходом к определению в вашей IDE, или поставьте PhpStorm если в вашей среде разработки нет такой возможности, чтобы отследить где объявляется эта функция

    p.s. но вообще, очень так себе подход, тут и статика, и глобальное состояние. Глобальный App в php только в Yii фреймворке я видел, да и те одумались и вроде выпилят его в 3 версии(если она будет, конечно)
    Ответ написан
    2 комментария
  • Что выбрать - пост запрос из консоли или команду на выполнение скрипта?

    @EvgeniiR
    https://github.com/EvgeniiR
    Не нужны там запросы. Все популярные фреймворки позволяют без проблем добавлять консольные команды, и иметь отдельные контроллеры под это дело
    Ответ написан
    Комментировать
  • Как написать простого торгового робота?

    @EvgeniiR
    https://github.com/EvgeniiR
    Демоны.
    while(true) {
     //do things...
    sleep(1);
    }


    Для надёжности стоит повесить супервизор сверху, хотя надо отдать должное, чтобы в php7 текла память программисту нужно действительно постараться.
    Ответ написан