Ответы пользователя по тегу PHP
  • Как исправить проблему с mail php и заголовками для gmail?

    Vamp
    @Vamp
    Заголовки To и Subject подставляются автоматически функцией mail. Вам не нужно добавлять их самостоятельно. Просто удалите строки:

    $headers .= "To: $to\r\n";
    $headers .= "Subject: $subject\r\n";
    Ответ написан
    1 комментарий
  • Как деплоить php приложение вместе с docker?

    Vamp
    @Vamp
    Это типичный вариант развёртывания приложения в докере. Единственный его минус - некоторое время даунтайма между моментом когда опустился старый контейнер и поднялся новый. Для решения этой проблемы есть разные техники. Например, blue-green deployment. Это когда есть два контейнера, условно называемые green и blue. И трафик льётся только на один. Скажем, на green. В момент деплоя обновляется blue контейнер и после того как он полностью будет готов - переключаем трафик на blue. В следующий раз деплой начнётся с обновления green.

    Переключение трафика делается разными способами. Самый безопасный - переписать конфиг nginx и релоаднуть его. Или менее безопасный, зато без необходимости править конфиг, - добавить оба конейнера в одну upstream группу и опустить green контейнер после обновления blue.

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

    Другой вариант развёртывания - контейнер с пхп поднят постоянно, но код проекта монтируется в контейнер через volume. Далее ci джоба по sftp заливает новый код в соседнюю папку и переключает симлинк. Симлинк переключается атомарно и контейнер сразу начинает работать с новым кодом. Ничего больше делать не надо. Я рекомендую такой подход когда на проекте менее 50 серверов.
    Ответ написан
    4 комментария
  • Как работать с DI-контейнером?

    Vamp
    @Vamp
    В самом контейнере задаются только примитивные типы данных?

    В DI контейнере обычно регистрируют объекты, общие для всего приложения, а не для конкретного запроса. Например, коннект к базе, роутер, кеш. Автовайринг примитивных типов в DI - прямая дорога к путанице и сложным для обнаружения ошибкам.

    А все объектные зависимости решаются с помощью автовайринга через Reflection API?

    Можно и так. А можно руками связи выставить. Или аннотациями. Разные способы бывают, смотря какие поддерживаются выбранной вами реализацией DI.

    К примеру, у меня есть контроллер.

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

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

    Если строки с названием шаблонов фиксированы, хранятся в конфиге/базе и одинаковы для всех страниц и всех пользователей, то можно шаблон и в DI зарегистрировать. В противном случае лучше создавать его во фронт контроллере. Либо зарегистрировать в DI фабрику шаблонов, которая будет создавать объекты вью в зависимости от переданных параметров. Аналогично и с моделью.

    Модель принимает класс соединения с БД, а БД принимает массив с настройками подключения.

    В контейнере можно зарегистрировать объект Settings, в котором хранятся данные для подключения к базе и прочие глобальные настройки, а затем инжектить его в класс для работы с базой. Ещё контейнер может сам выступать хранилищем настроек и инжектить их (в том числе примитивные). Так сделано в DI компоненте Symfony.

    Есть еще класс пагинации, он тоже должен быть в контроллере и его конструктор принимает три параметра типа int.

    Опять же, если эти три параметра не зависят от запроса, то можно зарегистрировать пагинатор в DI. Если зависят, то можно из конструктора перенести эти параметры в метод и вызывать его в контроллере. Например: $page = $this->paginator->calulatePage($one, $two, $three)
    Ответ написан
    2 комментария
  • Как запустить парсер NBBC под PHP8?

    Vamp
    @Vamp
    Нужно ещё конструкторы в классах переименовать в __construct() и тогда останется только два теста, которые заваливаются из-за того, что htmlspecialchars по умолчанию применяет ENT_QUOTES.
    Ответ написан
    Комментировать
  • Как работать с сырыми результатами SELECT MySQL CLI в PHP CLI?

    Vamp
    @Vamp
    Вам придется самостоятельно парсить $res в более удобный массив или объект. Можно слегка упростить вывод mysql, добавив аргумент --batch.
    Ответ написан
    1 комментарий
  • Как авторизовавшись на одном сайте в "сети сайтов", быть автоматически авторизованным и на других?

    Vamp
    @Vamp
    Видел однажды такую реализацию.

    При успешном прохождении авторизации кидает на страницу, где через тег img подключается "картинка" с каждого домена, участвующего в sso тусовке.

    <h2>Вы успешно вошли</h2>
    <img src="https://example.com/auth.php?key=1hB7fa014bbXCDg920">
    <img src="https://example.net/auth.php?key=hVVFD41looas8730MM">
    <img src="https://example.org/auth.php?key=QnaVBhj7mqwaBK9xq6">

    При авторизации создаётся и записывается в базу пачка одноразовых временных токенов, привязанных к только что авторизованному пользователю. При загрузке auth.php проверяется этот токен (передается через key в примере выше) и если токен ещё не просрочился, то выдаётся однопиксельный прозрачный gif с авторизационным кукисом. Этот кукис привязывается к домену, с которого загружается картинка.
    Ответ написан
    1 комментарий
  • Что означает синтаксис dockerfile (php-fpm)?

    Vamp
    @Vamp
    что такое dev суфикс (freetype и freetype-dev) и как понять какую версию ставить (я ставил методом тыка, когда получал ошибки и гуглил)

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

    почему, например, для mycrypt мне сперва нужно загрузить именно libmcrypt-dev, потом через pecl скачать mcrypt, а потом через алиас docker-php-ext-enable включить (экспериментировал и не работает с другими сценариями), а если смотреть на opcache, тот тут сразу docker-php-ext-install и все, а xdebug еще один сценарий: pecl -> docker-php-ext-enable - почему все по разному, как это понять?

    Для разных модулей требуются разные сторонние библиотеки. Для gd это freetype (для работы со шрифтами), libpng (для поддержки формата png), libjpeg-turbo (для поддержки jpeg). У opcache таких зависимостей нет, поэтому ничего дополнительно устанавливать не требуется.

    в чем отличие этих сценариев установки расширений (что делает каждая команда, простыми словами)?

    У php есть модули, которые поставляются с самим php. Например: zip, opcache, pdo_mysql, intl. Такие модули устанавливаются специально предусмотренным для этого скриптом docker-php-ext-install. Этот скрипт распаковывает исходники php, собирает запрошенные вами модули, копирует их в /usr/local/lib/php/extensions/no-debug-non-zts-20190902 (в других образах php путь может отличаться), прописывает загрузку модуля в ini и удаляет распакованные ранее исходники. То есть под капотом скрипт docker-php-ext-install вызывает скрипты docker-php-source, docker-php-ext-configure, docker-php-ext-enable.

    А есть модули, которые хостятся отдельно на сайте pecl.php.net. Это официальный репозиторий, где хостятся всякие разные сторонние модули типа mcrypt, imagick, xdebug. Для их установки нужно использовать утилиту pecl. Так как pecl автоматически не включает модули в ini, из-за этого необходимо самому включать их при помощи скрипта docker-php-ext-enable.

    нагуглил что
    docker-php-source - создает /usr/src/php

    Ещё распаковывает туда исходники самого php и поставляемых с ним модулей.

    docker-php-ext-install - скачивает (ОТКУДА?)

    Из архива /usr/src/php.tar.xz

    docker-php-ext-configure- конфигурирует перед установкой (что это значит?)

    Этот скрипт настраивает настройки, с которыми будет скомпилирован модуль. Например, тот же gd можно собрать без поддержки jpeg если сконфигурировать без опции --with-jpeg. Нужно ли запускать docker-php-ext-configure так же зависит от того, что написано в документации. Очень мало модулей требует предварительной конфигурации перед установкой. Мне на ум приходит только gd.

    В целом, официальная документация php вам поможет понять каким способом нужно устанавливать модули. Смотрим раздел requirements. Если написано "This extension is bundled with PHP", значит нужно устанавливать при помощи скрипта docker-php-ext-install. Если написано что-то вроде "This PECL extension is not bundled with PHP", значит нужно устанавливать через pecl.

    Независимо от способа установки, нужно всегда устанавливать библиотеки, от которых зависят конкретные нужные вам модули. Например, в том же imagick написано "ImageMagick >= 6.2.4 is required". Это значит, что нужно установить библиотеку imagemagick. В вашем базовом образе на основе alpine 3.16 в репозиториях ОС присутствует версия 7.1.0, которая подходит под условие >= 6.2.4:

    /var/www/html # apk info imagemagick
    imagemagick-7.1.0.35-r0 description:
    Collection of tools and libraries for many image formats
    
    imagemagick-7.1.0.35-r0 webpage:
    https://imagemagick.org/
    
    imagemagick-7.1.0.35-r0 installed size:
    4664 KiB


    Для intl нужна "ICU library, version 4.0.0 or newer". Такая библиотека тоже присутствует в репозиториях альпины:

    /var/www/html # apk info icu
    icu-71.1-r2 description:
    International Components for Unicode library
    
    icu-71.1-r2 webpage:
    https://icu.unicode.org/
    
    icu-71.1-r2 installed size:
    796 KiB


    Dev версии библиотек нужны только в момент установки модулей. Дальше их желательно удалить, чтобы уменьшить размер вашего образа.
    Ответ написан
    6 комментариев
  • Установка php в Docker?

    Vamp
    @Vamp
    RUN DEBIAN_FRONTEND=noninteractive apt-get install -y php php-pgsql php-mbstring php-xml php-apcu composer php-mongodb php-curl php-memcached
    Ответ написан
    Комментировать
  • Как на PHP при OAuth-авторизации в Яндекс.Почте работать по протоколам IMAP и SMTP, используя не пароль, а OAuth-токен?

    Vamp
    @Vamp
    Стандартный модуль php_imap не поддерживает XOAUTH2. И, вероятно, никогда не будет поддерживать. Существует план поддержки XOAUTH2, но работа по нему не ведётся.

    Вам следует воспользоваться альтернативными реализациями IMAP протокола, поддерживающие XOAUTH2. Например, webklex/php-imap.

    С SMTP так же - берите любую библиотеку с поддержкой XOAUTH2.
    Ответ написан
    Комментировать
  • Как правильно написать свой обработчик ошибок php?

    Vamp
    @Vamp
    Как правило, обычные ошибки не обрабатывают в таком обработчике, а конвертируют в ErrorException.

    function error_handler(
        $error_code,
        $error_message,
        $error_file_name,
        $error_line
        // $error_context - не объявляйте этот аргумент. Он не существует.
      ){
    
        throw new ErrorException($error_message, 0, $error_code, $error_file_name, $error_line);
    
      }
    
      set_error_handler('error_handler', -1);

    Ну а дальше ловите исключения как обычно.

    // some code...
    
    $host_db = '127.0.0.1';
    $login_db = 'root';
    $password_db = '';
    $database_db = 'some_db';
    
    $DB = new PDO('mysql:host=' . $host_db . ';dbname=' . $database_db, $login_db, $password_db);
    $DB -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    try {
        $DB -> beginTransaction();
    
        // some code...
    
        $DB -> commit();
    } catch (\Throwable $e) {
        $DB->rollback();
    }
    // some code...
    Ответ написан
    6 комментариев
  • ООП: Правильно ли архитектурно так делать?

    Vamp
    @Vamp
    Возможно, это нормально, так и должно быть ? или как архитектурно правильно решать подобное?

    Это нормально. Безликие массивы становятся осмысленными сущностями. Такой код становится проще понимать и поддерживать.

    Используемый вами подход называется data transfer object (DTO). Широко распространенная практика. DTO отлично сочетается с иммутабельностью, которая присутствует в ваших классах.

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

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

    Называется ORM. Находится в ответственности ORM слоя/фреймворка.
    Ответ написан
    4 комментария
  • Как настроить xdebug для докера на vps?

    Vamp
    @Vamp
    У вас сложности с тем, что xdebug является инициатором коннекта на ваш домашний ip. И если домашний провайдер выдаёт вам не белый IP, то напрямую такой коннект сделать не удастся.

    Выходом может быть проброс порта через ssh.

    ssh -R 9003:172.17.0.2:9003 user@vpsaddr
    Данная команда установит ssh соединение с логином user на ваш vps по адресу vpsaddr и откроет на этом сервере порт 9003, который сквозь ssh соединение будет прозрачно проксироваться на 9003 порт уже на вашем локальном компьютере.

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

    Узнать его можно командой
    docker inspect ваш-контейнер | grep IPAddress
    Этот ip адрес нужно прописать в xdebug.client_host и в опцию -R вместо 172.17.0.2.
    Ответ написан
    8 комментариев
  • Как мониторить работу скрипта PHP?

    Vamp
    @Vamp
    Начните с настройки директив slowlog в конфиге fpm.

    request_slowlog_timeout mixed
    The timeout for serving a single request after which a PHP backtrace will be dumped to the 'slowlog' file. A value of '0' means 'Off'. Available units: s(econds)(default), m(inutes), h(ours), or d(ays). Default value: 0.

    slowlog string
    The log file for slow requests. Default value: #INSTALL_PREFIX#/log/php-fpm.log.slow.

    Как только какой-либо скрипт работает дольше request_slowlog_timeout, снимается стек трейс данного скрипта и записывается в текстовый файл по пути, указанному в slowlog. Если тормозит какое-то конкретное место, то в стек трейсе будет видно какое. Например, если тормозит база, то в slow логе в топе стек трейса будет, например, вызов PDOStatement::execute.
    Ответ написан
    Комментировать
  • Получения энтропии для генератора случайных чисел в PHP - как можно сделать случайность по настоящему случайной?

    Vamp
    @Vamp
    Если вам не нужна криптографическая стойкость, то заморочки с генератором истинно случайных чисел абсолютно неоправданы. ГСЧ на вихре мерсенна отлично подходит для любых задач, не связанных с криптографией.
    Ответ написан
    Комментировать
  • Как запустить 5000 потоков параллельно с GET запросами?

    Vamp
    @Vamp
    Распараллелить выполнение в самом воркере с помощью ReactPHP или лучше GuzzleAsync. В таком случае не придется держать 5000 воркеров именно

    Вариант с GuzzleAsync - самый лучший. Под капотом он использует возможности curl_multi_exec, которые позволяют асинхронно отправлять несколько запросов, не плодя при этом лишние процессы. Не уверен конечно, что осилит 5000 параллельных запросов, но даже если и не сможет, то можно разделить 5000 между несколькими воркерами.

    2. "Правильно ли" это вообще делать с помощью PHP или это все таки задача уже других языков которые умеют в параллельное выполнение, корутины? Go, NodeJs?

    У вас нагрузка в основном IO bound, так что не имет значения какой язык выбрать. Главное чтобы он поддерживал IO multiplexing (который поддерживается в PHP через вышеупомянутый curl_multi_exec).

    3. Может уже есть готовые решения в виде библиотек на PHP? Искал, но не нашел

    Guzzle
    Ответ написан
    3 комментария
  • Как проверить хэш PHP функции crypt на Python?

    Vamp
    @Vamp
    Есть встроенная библиотека crypt

    from crypt import crypt
    
    print(crypt("hello world", "ab"))
    Ответ написан
    Комментировать
  • Как получить последний использованный прокси в Guzzle?

    Vamp
    @Vamp
    В guzzle есть другой способ передачи заданий - через promise.
    $requests = function () use ($data, $client) {
        foreach ($data as $item) {
            yield function () use ($client, $item) {
                return $client->sendAsync($item['request'])
                    ->then(function (Response $response) use ($item) {
                        echo $item['proxy'] . ' -> ' . $response->getStatusCode() . PHP_EOL;
                    });
            };
        }
    };
    Ответ написан
    1 комментарий
  • В чем практический смысл импорта функций из стандартной библиотеки?

    Vamp
    @Vamp
    Практический смысл видится мне только один - избежание проблем в случае появления в будущем в неймспейсе Foo\Bar функции, совпадающей по имени со стандартной.

    Но вероятнее всего это просто принятый в проекте стиль написания кода.
    Ответ написан
  • Что нужно вернуть при вызове сервиса методом OPTIONS?

    Vamp
    @Vamp
    Список поддерживаемых методов обычно передается через http заголовок Allow. Этот заголовок допускается возвращать в ответ на метод OPTIONS.
    HTTP/1.1 200 OK
    Allow: OPTIONS, GET, HEAD, POST
    Cache-Control: max-age=604800
    Date: Thu, 13 Oct 2016 11:45:00 GMT
    Expires: Thu, 20 Oct 2016 11:45:00 GMT
    Server: nginx
    Content-Length: 0


    По default планирую возвращать, что метод не поддерживается.

    Если вы планируете возвращать эту ошибку используя http код 405, то наличие заголовка Allow со списком поддерживаемых методов обязательно. Это требование RFC7231.
    Ответ написан
    Комментировать