Задать вопрос
Ответы пользователя по тегу PHP
  • Можно ли настроить запросы к php через вебсокет и http одновременно?

    3. Вебсокет и unix сокет - это совершенно разные вещи. Websockets - это протокол обмена информацией по TCP/IP.
    Как работают websockets:
    - Клиент начинает с простого HTTP запроса с просьбой апгрейднуться до websockets.
    - Сервер соглашается, и тогда между Клиентом и Сервером устанавливается постоянное TCP/IP соединение, по которому данные могут ходить туда-обратно по протоколу websockets уже без кучи церемониальных ритуалов, как принято в обычном HTTP.

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

    2. Что можно использовать:
    - Ratchet
    - Swoole / OpenSwoole
    - Workerman

    У каждой из этих асинхронных библиотек/фреймворков есть свои особенности и нюансы. Но это всё очень хорошо описано в их документации, так что просто следуйте тому, что там написано, и не волнуйтесь. Советовать что-то одно не буду, потому что на вкус и цвет все фломастеры разные, и выберете то, что будет отвечать вашим конкретным задачам.
    Ответ написан
  • Symfony 6.4 php 8.4 много сыплется deprecated, есть ли пути решения?

    Если сыплются deprecated, и вы не хотите их совсем отключать, можете для канала deprecation создать отдельный хэндлер в монологе. Если пишете в файлы, то настроить ротацию. И тогда и сами deprecated сохранятся, и основные логи не будут замусорены.
    Ответ написан
    Комментировать
  • Как называется такая вложенность в php?

    Это называется PHP. Потому как PHP изначально создавался как сам себе шаблонизатор HTML. Именно поэтому в коде присутствуют открывающие и закрывающие конструкции "<?php" и "?>"

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

    <?php if ($isSent): ?>
    
        <p>Email sent successfully!</p>
    
    <?php else: ?>
    
    <form method="post" action="<?php echo $_SERVER["PHP_SELF"]; ?>">
        <label for="subject">Тема письма : </label> <br>
        <input type="text" name="subject" size="30"> <br>
    
        <label for="elvismail">Содержание письма : </label> <br>
        <textarea name="elvismail" id="" cols="30" rows="10"></textarea> <br>
    
        <input type="submit" name="submit">
    </form>
    
    <?php endif; ?>


    Погуглите "php endif endfor"
    https://www.php.net/manual/en/control-structures.a...
    Ответ написан
    4 комментария
  • Почему PHP считает int числа после математических операций как float?

    Все коллеги, ответившие выше, правы. float заразен. Всё, чего он касается в математических выражениях, превращается во float.

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

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

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

    Попробуем использовать хотя бы то, что нам дали. Для этого будем придерживаться простого правила:

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

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

    <?php
    
    $sum = 400;
    
    // Было
    $all = $sum * (55 / 100);
    var_dump($all); // На выходе float(220.00000000000003)
    
    // Стало
    $all2 = calculateAll($sum);
    var_dump($all2); // На выходе int(220)
    
    function calculateAll(int $sum): int
    {
        return $sum * (55 / 100);
    }


    Вывод
    float(220.00000000000003)
    PHP Deprecated:  Implicit conversion from float 220.00000000000003 to int loses precision in /Users/vitiok78/Downloads/float.php on line 12
    
    Deprecated: Implicit conversion from float 220.00000000000003 to int loses precision in /Users/vitiok78/Downloads/float.php on line 12
    int(220)
     
    Ответ написан
  • Как из строки с путём получить элемент массива?

    <?php
    function getArrayValueByPath($array, $path, $separator = "/")
    {
        // Разбиваем путь на ключи
        $keys = explode($separator, $path);
        // Текущий уровень массива - начинаем с корня
        $current = $array;
    
        foreach ($keys as $key) {
            // Если ключа нет в массиве, то выбрасываем исключение
            if (!isset($current[$key])) {
                throw new \Exception(
                    "Ключ $key не найден в массиве по адресу '$path'"
                );
            }
            // Переходим к следующему уровню вложенности массива
            $current = $current[$key];
        }
    
        // Возвращаем значение по заданному пути
        return $current;
    }
    
    // Использование функции:
    
    $arr = [
        "k1" => [
            "k2" => [
                "k3" => [
                    "k4" => "v",
                ],
            ],
        ],
    ];
    
    $result = getArrayValueByPath($arr, "k1/k2/k3");
    
    var_dump($result);
    
    /** Результат выполнения:
    
    array(1) {
      ["k4"]=>
      string(1) "v"
    }
    
    */
    Ответ написан
    Комментировать
  • Как избежать race condition при вставки новой записи в бд SQL, PHP?

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

    Пусть хук записывает всю необходимую информацию и, главное, время в специальную таблицу в базе данных. Это всё, что будет делать хук. Таким образом он будет работать всегда быстро и всегда за одно и то же очень малое время.

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

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

    Поэтому, умные люди придумали профайлинг. Мы собираем статистику и пытаемся её отобразить в более-менее наглядном виде.

    Например, есть вот такой инструмент
    https://github.com/arnaud-lb/php-memory-profiler
    Readme очень подробный, и можно, в принципе, им и обойтись.

    Есть ещё вот такие видео, в которых всё более комплексно и подробно

    https://www.youtube.com/watch?v=NNMp-97rk9c&t=219s

    https://www.youtube.com/watch?v=56I5C0NYjv8&t=293s

    Удачи в поисках!
    Ответ написан
    1 комментарий
  • В чем может быть причина сильного увеличения длительности ответа запросов при установке Load Balancer на 2 сервера со стаком MySQL, PHP, Nginx?

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

    Рекомендую вам найти слабое звено, начиная с самого конца:

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

    2. Когда тестовое окружение будет так же тормозить, начните искать слабое звено. Рекомендую начать с конца.

    3. Замените весь ваш прекрасный PHP код на один единственный файл index.php с Hello World. Если проблема уйдёт, значит виновата ваша программа. Чтобы найти место уже в программе, рекомендую запустить Prometheus + Grafana, установить библиотеку в ваш PHP, и писать в мониторинг длительность каждой сложной операции (длительность всего запроса, длительность запросов к базе, длительность запросов во внешние API, длительность каких-то подозрительных циклов, алгоритмов и т.д.) Так вы сможете найти точное место в программе, где происходит затуп. Это надо будет сделать и на проде после решения проблемы, чтобы вы всегда знали, что происходит с вашим проектом.

    4. Если проблема не в программе, проверьте конфиг PHP. Может быть, у вас там тупо мало воркеров включено, может быть не включён OPcache и т.д. Почитайте, как правильно настраивать PHP на проде.

    5. Если проблема не в этом, то... https://youtu.be/bq3HksAwb2Q?si=NsBZeYeHsq69JW6o
    Ответ написан
    Комментировать
  • Как должен выглядеть идеальный контроллер?

    Если хотите идеал, то он должен соответствовать следующим пунктам:

    1. Сериализация/десериализация - это дорогостоящее мероприятие, поэтому оно должно делаться только в двух местах: прямо на входе и прямо на выходе. Вход - это ваш контроллер, Выход - это другой сервис, куда вы передаёте данные, или база данных (тут тоже происходит сериализация, либо явно, либо в ORM). Во всех остальных слоях инфообмен должен совершаться уже при помощи объектов PHP либо нативных типов. Это экономит ресурсы. При передаче между слоями приложения объектов вместо значений либо ассоциативных массивов вы сразу будете видеть очепятки, IDE вам прекрасно поможет при помощи автодополнения, объекты могут иметь какие-то полезные методы.

    2. Очень желательно в каждом из слоёв иметь собственный класс, отвечающий за данные. Например, нам в слой API приходит JSON-чик с новым пользователем.
    - Сериализуем JSON в DTO UserInAPI, сразу валидируем всё то, что мы можем валидировать без слоя бизнес-логики, и либо отдаём клиенту ошибку, либо передаём сам объект UserInAPI в следующий слой: слой бизнес-логики
    - В слое бизнес логики, получаем DTO UserInAPI на входе, преобразуем его в свой бизнес-объект UserInBusiness, валидируем его уже с точки зрения бизнеса, и либо возвращаем ошибку в слой API, либо совершаем над ним действия, и передаём объект класса UserInBusiness в слой работы с базой
    - В слое работы с базой данных получаем на входе объект UserInBusiness, преобразуем его уже в сущность базы данных UserInDB, валидируем всё на предмет корректности данных для базы, и либо возвращаем ошибку в бизнес, либо сохраняем сущность класса UserInDB в базу.

    Зачем такие сложности, вы спросите? А просто обратите внимание на то, что скорость изменения кода в разных слоях разная.
    - API вообще должен меняться раз в сто лет, чтобы не злить клиентов. Поэтому DTO класс UserInAPI будет стабильным и редко будет меняться.
    - Бизнес-логика меняется очень часто. У класса UserInBusiness постоянно будут добавляться поля и методы, тут жизнь будет кипеть.
    - Слой базы данных будет меняться реже, чем слой бизнеса, но чаще, чем слой API, потому что нам нужны будут новые поля в базе, новые таблицы и связанные таблицы.
    - И если мы один тип сущности протащим во все слои, то эта сущность обрастёт таким количеством различной хрени, что нам плохо станет, когда будем на неё смотреть. Либо она обрастёт кучей декораторов в каждом из слоёв. Поэтому лучше всё разделить.

    3. Теперь внимание, казалось бы, что мы слишком сильно связываем наши слои, и нижестоящие слои знают что-то о вышестоящих, а это неправильно. Ведь мы передаём объект UserInAPI в слой бизнеса, т.е. слой бизнеса должен уметь работать с этим объектом. И так же слой базы должен уметь работать с объектом бизнеса UserInBusiness. Как же быть? А очень просто. На входе слоёв использовать интерфейсы. Т.е. в слое бизнеса мы будем принимать не сам класс UserInAPI, а объект, имплементирующий интерфейс UserIncoming, который объявим в бизнес слое, и заставим слой API сделать так, чтобы его класс UserInAPI имплементировал этот интерфейс. Таким образом слой бизнеса ничего не будет знать о слое API, а будет ждать на входе данные по контракту, описанному в интерфейсе. Бизнесу плевать на конкретную реализацию, ему нужны только методы getUsername, getEmail из интерфейса. А какой класс ему их предоставит - пофигу. Таким образом мы практически полностью разделяем слои и в два счёта сможем поменять слой API, где у нас HTTP контроллеры, на слой RabbitMQ, SOAP, Grpc и т.д.
    Ответ написан
    6 комментариев
  • Что пытается сделать злоумышленник на моем сайте?

    На первом скрине, похоже, атака через "Stacked Queries". Хацкер надеется, что вы не отфильтровали входящие данные.

    Допустим у вас из бройзера приходит ID товара. И в программе запрос:

    SELECT * FROM products WHERE productid={нефильтрованный ID};


    Хакер отправляет вместо ID товара следующее:

    1; SELECT pg_sleep(25)


    В результате получится запрос:

    SELECT * FROM products WHERE productid=1; SELECT pg_sleep(25);


    И ваш сервер при каждом таком запросе будет спать 25 секунд. И порядочное количество таких запросов в случае удачи атаки смогут повесить вам там всё.

    То, что он продолжает долбиться, ничего не значит. Он, наверняка, использует какого-то бота, где накиданы в кучу различные векторы атаки, и они все применяются, пока ваш сервер не упадёт в результате срабатывания какой-то из них.
    Ответ написан
    Комментировать
  • Шифрование уникальной ссылки для пользователя. Как реализовать?

    Можно вообще отказаться от базы, и использовать JWT токен. В нём можно указать идентификатор пользователя в зашифрованном виде, время жизни ссылки и любую другую необходимую информацию. В базе данных ничего хранить не надо. Однако учтите, информация в JWT токене не зашифрована по умолчанию, любой может её из него вытащить. Прелесть JWT токена в том, что его нельзя изменить, потому что он подписан ключом шифрования. Это на случай, если база данных - драгоценный ресурс, который вы не хотите использовать.
    Ответ написан
    Комментировать
  • Что делать, если unexpected end, но код раньше работал?

    У вас открывающих тегов "<?php" 28 штук, а закрывающих "?>" - 29 штук. Один из ваших открывающих тегов укороченный "<?" (строка 155).  Я не знаю, какая у вас сейчас версия PHP и какая настройка по поводу коротких тегов, но рекомендую короткие открывающие теги не использовать.

    Плюс рекомендую исправить ошибку, два раза закрываете один и тот же тег
    668a9eff0f49b708574679.png

    А еще всегда попробуйте везде добавить пробел перед вопросом в закрывающей конструкции "?>".
    Очень часто именно это является проблемой
    Ответ написан
    6 комментариев
  • Переход с MD5 на SHA256 что нужно сделать чтобы работало)?

    Согласен с обоими ответившими. Дополню, как конкретно я бы сделал.

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

    Можно, например, для нового хеша завести новое поле в базе с дефолтным null.

    Когда юзер пытается залогиниться своим паролем, смотрим, если в новом поле null, то проверяем аутентификацию по старой схеме, но генерируем новый хэш по новой схеме (password_hash), записываем его в новое поле.
    А если в новом поле уже не null, то, значит, пользователь уже на новой схеме, и продолжаем аутентификацию уже с новым хэшем (password_verify).

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

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

    Да, и не удаляя сразу данные из старого поля, мы всегда сохраняем возможность вернуться на старую схему, если вдруг обнаружили какую-то дыру в безопасности нашего нового метода.
    Ответ написан
  • Как добавить php5.6 в ubuntu 16.04.07?

    Зачем все эти мучения в 2024 году, когда можно один раз разобраться в том, что такое Docker, и навсегда забыть о подобной головной боли...

    https://hub.docker.com/layers/library/php/5.6-apac...
    Ответ написан
    2 комментария
  • Запросы soap в инфоклинику на php?

    Когда я вижу слово SOAP в задаче для PHP, меня инстинктивно передёргивает. Ненавижу и саму идею SOAP, и, особенно, её идиотскую реализацию в PHP.

    Лично для себя нашёл решение: если мне нужно только несколько конкретных запросов от какого-то SOAP сервиса, я просто беру и делаю запросы при помощи curl и чистого XML. И создание XML запроса, и потом парсинг XML ответа, для меня проще, быстрее, приятнее и надёжнее, чем работа с дурацким SoapClient

    Вот вам пример работы с SOAP через curl
    https://stackoverflow.com/questions/7120586/soap-r...

    Т.е. берём программу SoapUI, подключаемся к их апихе, копируем оттуда примеры XML запроса и ответа для наших нужд, и пишем curl запрос по этим шаблонам.

    Кстати, есть инструменты, которые позволяют сгенерировать целый пакет для работы с определенным SOAP API по его wsdl адресу. Там и классы запросов и классы ответов будут сгенерированы, и всё, чтобы делать запросы.
    Можете попробовать, но я недолюбливаю такие инструменты.
    https://github.com/WsdlToPhp/PackageGenerator
    Ответ написан
  • Как сделать авторизацию по двум таблицам в Symfony?

    Разные категории пользователей в Symfony лучше всего организовать при помощи ролей.

    Основная таблица users будет содержать всех ваших пользователей, это проще всего. Там же и будет поле с ролями.

    Для дополнительных полей другой категории пользователей лучше всего создать отдельную таблицу, которая будет связана с основной через связь one-to-one либо one-to-many по id пользователя (посмотрите уроки по Doctrine).

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

    Очень советую досмотреть полностью уроки с symfonycasts.com. Они великолепны, и там точно всё необходимое должно быть.
    Ответ написан
    2 комментария
  • Переписать грамотнее и с помощью Laravel реально ли и каким лучше образом?

    Шаблонизатор - прекрасная и очень нужная штука для PHP.

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

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

    Могу дать вам ещё один вариант вместо Laravel. Это Symfony. Да, освоение этого фреймворка будет немного сложнее, чем Laravel, но преимуществом Symfony является то, что он заставляет писать код чище, чем обычно пишут на PHP. Он более строгий, а строгость в программировании всегда к лучшему. Symfony состоит из большого числа компонентов, и собирать проект можно как конструктор. Минимальная его сборка весьма легковесна. Кстати, огромное количество компонентов Symfony используется и в других фреймворках, многие из них можно использовать и без фреймворка.

    Как раз таким компонентом, который можно использовать без фреймворка, т.е. так, как вы хотите, является шаблонизатор Twig. Это превосходный шаблонизатор, его легко внедрять.
    https://twig.symfony.com/

    Но всё же рассмотрите возможность перейти на фреймворк, потому что сейчас в мире PHP почти везде используют именно фреймворки.
    Ответ написан
    9 комментариев
  • Какой аналог itemOperations в api-platform 3 версии?

    Сам ещё не ставил третью версию, но одним глазом посмотрел документацию, и вроде как они тупо по названию классов в аргументе options ориентируются. В принципе, это логично, если подумать. Но не очевидно.
    #[ApiResource(
        operations: [
            new Get(), // item
            new Put(), // item
            new Patch(), // item
            new Delete(), // item
            new GetCollection(), // collection
            new Post() // collection
        ],
    )]
    Ответ написан
  • Как можно решить проблему с доступам к методам PHP классов унаследованным от одного класса?

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

    <?php
    
    class Node
    {
        protected function func1() {
            print "NODE PARENT; ";
        }
    }
    class NodeA extends Node
    {
        public function FUNC2() {
            print "NODE A; ";
        }
    
        // Используем метод родителя внутри этого класса
        public function func1Overrided() {
            print "From parent: " . parent::func1();
        }
    
        // Переопределяем метод так, чтобы его нельзя было использовать
        protected function func1() {
            throw new \Exception("Нельзя вызывать этот метод из NodeB");
        }
    }
    
    class NodeB extends Node
    {
        // Функция инициализации
        public function onInit(NodeA $a): void
        {
            // Сделал так, чтобы не мокать api )
            (function (?NodeA $a) {
                $a->FUNC2(); // Метод успешно вызывется так как он public
                $a->func1();  // Метод теперь кидает исключение, использовать не получится
            })($a);
        }
    }
    
    $nodeA = new NodeA;
    $nodeA->func1Overrided(); // Работает вызов метода funс1 из родителя
    
    $nodeB = new NodeB;
    $nodeB->onInit($nodeA); // Выдаёт ошибку, нельзя использовать метод func1 из класса NodeB


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

    Вот минимально-инвазивное решение, которое позволит и наследование сохранить (если оно прям ну вот сильно надо), и решить проблему при помощи композиции, основанной на трейте.

    <?php
    // Делаем общий трейт для всех классов
    trait Func1 {
        private function func1() {
            print "FUNC1; ";
        }
    }
    
    class Node
    {
        // Включаем трейт
        use Func1;
    }
    
    class NodeA extends Node
    {
        // Включаем трейт
        use Func1;
    
        public function FUNC2() {
            print "FUNC 2 NODEA; ";
        }
    }
    
    class NodeB extends Node
    {
        // Включаем трейт
        use Func1;
    
        // Функция инициализации
        public function onInit(NodeA $a): void
        {
            // Добавить зависимость
            (function (?NodeA $a) {
                $a->FUNC2(); // Метод успешно вызывется так как он public
                $a->func1();  // Метод использовать не получится, т.к. он private
            })($a);
        }
    }
    
    $nodeA = new NodeA;
    
    $nodeB = new NodeB;
    $nodeB->onInit($nodeA); // Выдаёт ошибку, нельзя использовать метод func1 из класса NodeB
    Ответ написан