Ответы пользователя по тегу PHP
  • Почему PHP требует Apache или Nginx чтобы работать в docker контейнере?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    какой смысл использовать Apachе или Nginx содержащий образ? Разве нельзя работать с контейнером содержащим только php?

    Разве нельзя работать с контейнером содержащим только php?

    Можете. Вы можете поставить PHP-CLI контейнер и работать с ним без веб-сервера конечно же
    FROM php:7.1-cli
    Что позволит вам запускать консольные команды (композер туда же)

    Если ставите PHP-FPM контейнер, то понятное дело вам нужен веб-сервер (гуглите принцип работы с Fast CGI).
    Нужен, если вы хотите общаться с приложением через HTTP
    FROM php:7.1-fpm
    Ответ написан
    1 комментарий
  • Как правильно писать глубокие интеграционные тесты?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Эти тесты функциональные строят обычно на фикстурах:
    - загружается состояние системы (например БД) до необходимого, например загружается категория КА и товар ТА
    - поднимается приложение или достаточное число сервисов для теста
    - проводится тест (например добавляется товар дублер товар ТА и система должна ругнуться или удаляется товар, которого нет, например товар ТБ)

    И так для каждого кейса — для него подгоняется состояние. Необходимо, чтобы тест все же должен быть относительно изолированным. Для некоторых тестов состояние одно и инструкции его поднятия будут одинаковы. Кроме того это быстро и проще.
    Для скорости тестов обычно берут in memory БД — это еще одна важная причина использовать сложные хорошие ORM, которые могут работать и с примитивными базами в памяти и с полноценными, отделяя код и работу с данными. Разница во времени по сравнению с реальным БД сильно выше.

    Вообще эти тесты сложные и потому их число как правило сильно меньше простых юнитов... Кроме того иногда уже имеет смысл делать это через приемочные (браузерные) тесты
    Ответ написан
  • Зачем в catch делать throw?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    try {
        $this->initDBConnect();
    } catch (\Exception $e) {
        // Тут залогировали
       $this->logger->error('Некоторое сообщение', [некоторые данные]);
    
        // тут выкинули дальше
        // или throw new MyAnyException, 
        // а не просто дальше выкидывать тоже самое
        throw $e; 
     }


    Для чего бросать дальше
    Например идет процесс оплаты товара, в клиенте выкинулось исключение... мы в слое работы с АПИ платежки поймали исключение и залогировали, но выкинули дальше, чтобы наша система выше поймала, откатила транзакцию и плюнула ошибкой уже своей для слоя выше
    Ответ написан
    3 комментария
  • В каких случаях при разработке сайта на PHP лучше использовать не MVC (и его подобия), а какой-нибудь другой подход?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Об архитектуре нужно обязательно почитать Робкрта Мартина: Чистая архитектура
    Ответ написан
    Комментировать
  • Как в связке nginx+php71-fpm+yii настроить очередь запросов?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    и отдает он очередью именно этому клиенту, если параллельно буду заходить на сайт с другого окна из режима инкогнито у него своя очередь и страницы будут открываться

    Похоже на то, что к файлу сессии параллельно не может доступ получить другой процесс. Обычно для решения похожих проблем сессии выносят в тот же Redis.
    Но и тут могут быть проблемы: https://habr.com/ru/post/318836/
    Ответ написан
    1 комментарий
  • Php микрофреймворк - это rad фреймворк или enterprise?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Микройфреймворки для микро-сервисов, для API: для небольших сервисов

    Это могут быть и интерпрайз и rad, вообще вы можете придумать свою классификацию :)
    Классификация такая штука — для нее нужен признак, по которой классифицируют. Микро — с ограниченным функционалом, тк остальное выбирается под нужные задачи...

    нам же ничего не мешает подключить доктрину и прочие компоненты и перейти к enterprise

    Интерпрайз не зависит от наличия Доктрины. Интерпрайз — это про долго, давно, сложно и дорого.
    Ответ написан
    4 комментария
  • Что делать, если нужно получить часть данных сущности?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Для отображения используйте DTO и доставайте их из репозитория. Для логики отображения негоже сущности из бизнес-слоя вытягивать.

    1 способ — заюзать Доктрину
    сразу не увидел, что вы без нее работаете

    Доктрина умеет создавать дтошки из коробки
    class PostDTO
    <?php
    
    namespace App\DTO;
    
    class PostDTO
    {
        /** @var string */
        private $name;
        
        /** @var string */
        private $description;
        
        /** @var string */
        private $text;
        
        public function __construct(string $name, string $description, string $text)
        {
            $this->name = $name;
            $this->description = $description;
            $this->text = $text;
        }
    
        public function getName()
        {
            return $this->name;
        }
    
        public function getDescription()
        {
            return $this->description;
        }
    
        public function getText()
        {
            return $this->text;
        }
    }

    И используя синтаксис NEW DQL создаем наши DTO :
    $query = $em->createQuery('SELECT NEW App\DTO\PostDTO(p.name, p.description, p.text) FROM App\Entity\Post p');
    $users = $query->getResult(); // array of PostDTO

    Источник: https://www.doctrine-project.org/projects/doctrine...

    2 способ — заюзать DBAL/PDO
    Сделать запросы через более низкую прослойку без ORM (например через Doctrine DBAL или PDO) и результат фетчить в те же самые PostDTO в репе
    Ответ написан
    2 комментария
  • Преобразование путей при работе с архивами, какие есть варианты?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Используйте 7z, он конвертирует обратный слеш в нормальный
    https://help.ubuntu.ru/wiki/7zip
    Ответ написан
  • PHPUnit: можно ли одновременно замокапить и класс, и интерфейс?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Не надо плодить классы. Воспользуйтесь тогда анонимным классом (для таких задач их и придумали) прямо в коде:
    new class implement YourInterface {
        // тут методы, которые интерфейс содержит
        // тут методы, которые вы будете вызывать
    }
    Ответ написан
    8 комментариев
  • Зачем использовать callback функции?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Крайне хорошее описание по таким вещам есть в Википедии. А теперь к примерам:

    1. Передача параметром
    Такие функции удобно передавать параметром/возвращать из других функций. Тут лучше углубиться в тему: Функция высшего порядка

    Например для удобной передачи асбтрактной функциональности, которая будет применяться например к каждому члену итератора/массива. Это используется например в таких стандартных функциях array_{map/filter/reduce}. В коллбеке то, что будет применяться к каждому члену массива.

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

    2. Отложенный/ленивый вызов
    Например в PHP приложениях используется в роутерах/контейнере/логере, да много где. Суть в том, что в соллбеке спрятана функциональность, иногда очень большая. Элементов и этих фукцниональности тоже может быть много, а нужно все зарегистрировать. Чтобы не плодить тысячи объектов при регистрации — каждый кусочек функциональности заворачивают в коллбек, и только при вызове (например роута), вызывается коллбек и соответственно все, что в нем начинает работать.

    class Container
    {
         private $callableServices;
    
         public function add(string $serviceName, callable $service)
         {
              $this->callableServices[$serviceName] = $service;
         }
    
         public function get(string $serviceName)
         {
              $service = $this->callableServices[$serviceName];
    
              return $service();
         }
    }
    
    $container = new Container();
    // объект тут не создается, тк он внутри функции, которая не вызывается
    $container->add('a', function() {
         return new FirstBigService();
    });
    // объект тут не создается, тк он внутри функции, которая не вызывается
    $container->add('b', function() {
         return new SecondBigService();
    });
    
    // Внутри метода get() идет не просто доставание нужного элемента, 
    // а еще и его вызов (тк лежат функции), то есть именно сейчас 
    // произойдет new FirstBigService()
    $service = $container->get('a');


    3. Изоляция контекста
    // тут левый код
    ...
    
    // Код ниже работает изолировано и на него ничего не может подействоват,
    // т.к. внутри свой контекст
    (function() {
        $app = new Application();
        $app->run();
    });
    Ответ написан
    Комментировать
  • Dependency Injection Container, попробовал на практике - не понял смысла?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Контейнер
    Главная задача контейнера — переиспользовать сервисы более одного раза, например некий провайдер более чем в 1 месте или некий Sender в 20 местах по всему приложению. И упростить к ним доступ.

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

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

    Современные контейнеры предоставляют на самом деле еще ооочень много фишек: автовайринг, когда вам и конфиг писать не нужно, просто в конструкторе написали класс, а контейнер если нашел такой класс, то сам и сконфигурирует (и также учтет его параметры конструктора), то есть разработчику остается работа только с уникальными и не стандартными ситуациями.
    Или дает упрощенные фабрики/билдеры/декораторы.

    Dependency Injection
    И в будущем удобно будет тестировать ?

    С контейнером или нет, с DI тестирование становится возможным по определению и будет относительно простым в любом случае.

    Главное не объединяйте эти два понятия. Сам паттерн — просто частный случай инверсии зависимости, класс получил зависимость через конструктор/сеттер, и все, он не знает деталей, потому и тестить проще — просто подменил на пустышку.
    А контейнер — средство для работы со сложным приложением, которое внедряет как раз таки, то есть выполняет работу внедрения. Центральное в нем — слово контейнер.

    Несколько точек входа
    По поводу ваших точек входа: несколько точек входи = несколько иницализаций.
    Как-бы они разные по своей сути приложения уже по определению получаются.
    Но вы можете упростить, и вынести инициализацию контейнера в абстракцию — в некий класс App/Kernel и там это делать, а в точке входа инициализировать не контейнер раз за разом, а именно конкретное приложение.
    Ответ написан
    7 комментариев
  • Как сделать unit test метода в котором нужен заполненный массив?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    По-хорошему $this->solutions должно устанавливаться через конструктор, потому вам не составит труда заинжектить это состояние в тестируемый объект.

    Если у вас "пришлось", то можете заюзать некий хак, например через рефлексию/closure::bind установить состояние в тестируемый объект.

    Вот на скорую руку накидал (Разместите его в своем TestCase или прямо в тесте)
    protected function setPrivateProperty(object $object, string $propertyName, $value): void
    {
        $reflectionClass = new \ReflectionClass(get_class($object));
        $property = $reflectionClass->getProperty($propertyName);
        
        $property->setAccessible(true);
        $property->setValue($object, $value);
        $property->setAccessible(false);
    }


    ПРИМЕР
    Класс и метод, который нужно протестить и приватная переменная которого заполняется не через конструктор
    class System 
    {
        // Не важно, как оно будет заполнено, важно, 
        // что в методе мы проверяем некоторые значения  
        // и мы создадим ниже в тестах эти ситуации разными тест-кейсами
        private $solutions;
    
        public function checkRecords(): string
        {
            if (count($this->solutions) === 3) {
                return 'full';
            }
    
            if (count($this->solutions) === 6) {
                return 'overflow';
            }
    
            throw new IncorrectCountException('Incorrect solutions count.');
        }
    }


    Тесты
    public function testCheckRecordsFull(): void
        {
            // 3 элемента
            $data = [1, 2, 3];
            $system = new System();
            $this->setPrivateProperty($system, 'solutions',  $data);
    
            $status = $system->checkRecords();
    
            $this->assertEquals('full', $status);
        }
    
        public function testCheckRecordsOverFlow(): void
        {
            // 6 элементов
            $data = [1, 2, 3, 4, 5, 6];
            $system = new System();
            $this->setPrivateProperty($system, 'solutions',  $data);
    
            $status = $system->checkRecords();
    
            $this->assertEquals('overflow', $status);
        }
    
        public function testCheckRecordsIncorrectCOunt(): void
        {
            // сколько-то элементов, но не 3 и не 6
            $data = [1];
            $system = new System();
            $this->setPrivateProperty($system, 'solutions',  $data);
    
            $this->expectException(IncorrectCountException::class);
            $system->checkRecords();
        }

    Код тестовый, просто отобразить принцип, возможно у вас в классе зависимости -- нужно будет сделать моки и т.д. Тут некоторые повторения можно вынести в setUp().
    Также хочу обратить внимание, что тесты построены по принципу ААА (Arrange, Act, Assert).
    Как итог, 3 ситуации, все протестированы, методу не важно, как заполнлась приватная переменная, но вся его логика протестирована. Все легко и просто.
    Ответ написан
    7 комментариев
  • Какой PHP-микро-фреймворк взять для простенького REST API с авторизацией, и чтобы не из "большой тройки"?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Symfony 4 в стандартной поставке— это микрофреймворк, серьезно
    Ответ написан
    2 комментария
  • Как вывести все значения строки, если изначально не знаешь сколько их будет?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    $massive = "1H|\^&|||ttt|||||h|BATCH|P|1 P|1 O|1|5331|598^0012^1^^S1^SC|^^^133^1\^^^140^1|R||20190625132832||||N||||1|||||||20190625135509|||F R|1|^^^133/1/not|1.52|uIU/ml||N||F||admin|||E1 R|2|^^^140/1/not|4.99|pmol/l||N||F||admin|||E00 21 L|1|N 43";
    
    // Ищем все числа из трех цифр, перед которыми стоит ^^^ 
    // и результат записываем в matches
    preg_match_all('~\^{3}(?P<target>\d{3})~', $massive, $matches);
    
    // В массиве matches c индексом 'target' лежат наши значения
    //  [
    //     0 => "133"
    //     1 => "140"
    //     2 => "133"
    //     3 => "140"
    //  ]
    var_dump($matches['target']);

    https://ideone.com/e6U7yB

    Далее циклом пройдитесь по этим значениям, например отфильтруйте их, очистите от дублей, и сопоставьте со своим словарем и выводите в html уже.
    И все это динамически, то есть не завязано на порядок этих символов
    Ответ написан
    1 комментарий
  • Почему в PHP строки и массивы не являются объектами?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    От Никиты Попова (разраб из core team языка) есть библиотечка в виде php-extension:
    Extension
    Пример имплементации

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

    Обсуждение на Reddit: https://www.reddit.com/r/PHP/comments/2xmawu/are_s...

    UPD: В опросе того, что хотят увидеть в PHP 8 год назад, скалярные объекты заняли 4 место с неплохим результатом: https://blog.nikolaposa.in.rs/2018/09/23/community...
    may be...
    Ответ написан
    2 комментария
  • Где лучше качество кода?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Все плохо
    • Глобальные переменные — зло
    • На лицо некий код, который и с сессией работает и с БД — явное нарушение общепринятных принципов, например SRP
    • $dao как инициализирована? Явно не через конструктор
    • Использование цифры, вот сиди гадай по ней — это плохо, используйте именованную константу. Например 3 — это некий статус для desktop, вот и назовите ее STATUS_DESKTOP, хотя далее...
    • вы обращаетесь к одному методу, который шибко умный — работает с флагами, тем самым нарушая SRP и сам содержит if/else, сделайте просто 2 раздельных метода:
      getMobileOpinionList()
      getDefaultOpinionList()

    • Предикат лучше вынести в отдельный метод isMobileOnly(), тк условия могут поменяться в нем, не будете же везде в if менять по всему проекту
    Ответ написан
    33 комментария
  • Зачем нужен контейнер если php умирает?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Пишем код, кода становится много, его нужно обслуживать и бороться со сложностью, также нужно тщательно тестировать, тк деньги и надежность пользователей

    Чтобы тестировать методы класса и не зависеть от реализации -- соблюдается принцип инверсии зависимостей (и некоторые другие)

    Инверсия зависимостей -- нужно помнить и держать зависимости к нашему классу где-то и сам конкретный сервис наполнять нужно этими зависимостями, руками делать это
    накладно

    try {
        echo
            (new PurchaseOrder(
                new LocalOrderStorage(
                    new NullOrderStorage()
                ),
                new OrderId($inputParams['order_id'])
            ))
                ->newInvoice(
                    new InvoiceNumber(
                        new Vendor(
                            new LocalVendorStorage(),
                            new VendorId($inputParams['vendor_id'])
                        ),
                        new VendorInvoiceNumber($inputParams['vendor_invoice_number']),
                        new DateTime($inputParams['date_time'])
                    ),
                    new VendorInvoiceNumber($inputParams['vendor_invoice_number']),
                    new DateTime($inputParams['date_time']),
                    new InvoiceAmount(
                        new Amount($inputParams['amount']),
                        new Currency($inputParams['currency'])
                    )
                )
                    ->json()
        ;
    } catch (Exception $exception) {
        return
            (new ErrorResult())
                ->json($exception->getCode(), $exception->getMessage())
            ;
    }

    Кроме того появляется куча параметров в проекте.

    На помощь приходит паттерн Dependency Injection Container (Service Container), который за нас это делает и всасывает в себя эту заботу, а мы продолжаем писать код и делать это быстро, доставляя features for customers
    Ответ написан
    1 комментарий
  • Как добавить возможность живого редактирования сайта?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Если ваша загвоздка именно в том, чтобы у разных сущностей статьи было свое оформление в виде html (классы, структура вложенных тегов и прочее), то можно пойти на встречу тем людям, кто редактируем и совсем спрятать от них эти детали, причем убрать любую возможность сломать верстку

    В разных компаниях делают по-разном, где-то юзают маркдаун, где-то контент-билдер, который перетягиванием как конструктор собирает статью, но есть вот такой вариант:
    https://editorjs.io/

    Попробовать в-живую: https://vc.ru/writing

    Сделан одни издательством (vc.ru, TJournal) -- редактор просто позволяет накидывать разные сущности статьи, на выходе JSON, который вы можете трансформировать в разные блоки... В документации почитаете, как расширить словарь этих сущностей.

    У меня в блоге например некий подвариант маркдауна, вставляешь 2 картинки -- парсер видит соседство и генерирует код слайдера, если отдельно -- парсер генерирует 2 тега img со своими атрибутами. Блог -- обычная CMS, сделан Ильей Бирманом
    Ответ написан
    1 комментарий
  • Как найти значение в массиве и удалить?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Задача: нужно отфильтровать элемент массива по некому критерию (у вас это -- наличие урла в одном из его подэлементов)

    Решение: используем фильтр (функция array_filter сделано именно для этих операций), который проверит на нужное условие (наш критерий) и вернет false или true, тем самым скажет -- оставить весь элемент или удалить.

    $badUrl = "example.com";
    
    $resultArray = array_filter($startArray, function($arr) use ($badUrl) {
        return !(isset($arr['url']) && $arr['url'] === $excludeUrl);
    });


    В PHP 7.4 (когда будут доступны короткие лямбды):
    $badUrl = "example.com";
    $resultArray = array_filter(
        $startArray, 
        fn($arr)  => !(isset($arr['url']) && $arr['url'] === $excludeUrl)
    );


    Пример работы для вашего примера: https://ideone.com/eXWm3o
    Документация: https://www.php.net/manual/ru/function.array-filter.php
    Ответ написан
    1 комментарий