Ответы пользователя по тегу PHP
  • Docker / PHP - Как организовать воркеры?

    Vamp
    @Vamp
    "нет возможности из процесса, поднятого в докере запускать под-процессы"

    Это неправда. Те же самые nginx и php-fpm запускают мастер процесс и стартуют кучу своих воркеров как отдельные процессы.

    Простейший способ сделать то что вы хотите - запустить скрипт внутри контейнера

    docker run -d -v /home/project:/var/www php:8.1-cli php /var/www/worker.php

    В дальнейшем если воркер упадет, докер его перезапустит. И таких docker run команд выполняете столько, сколько хотите одновременно работающих воркеров.
    Ответ написан
    3 комментария
  • Как работать с сырыми результатами 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 с авторизационным кукисом. Этот кукис привязывается к домену, с которого загружается картинка.
    Ответ написан
    2 комментария
  • Что означает синтаксис 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 - не объявляйте error_context,
        // он объявлен deprecated в PHP 7.2, а в 8.0 уже удалён
      ){
    
        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.
    Ответ написан
    Комментировать
  • PHP-DI и контроллеры. Как избежать повтора кода?

    Vamp
    @Vamp
    Если аргументы конструктора у контроллеров помечены type hint'ами, то контроллеры необязательно регистрировать в контейнере. Можно создавать их напрямую по имени класса:

    $controller3 = $container->get('project\Controllers\Controller3');

    Здесь используется фича autowiring, которая по умолчанию включена. PHP-DI смотрит при помощи рефлексии какие типы требует конструктор контроллера и подставляет соответствующие сервисы, зарегистрированные в контейнере.
    Ответ написан
    1 комментарий
  • Как средствами PHP нарисовать дугу для SVG файла?

    Vamp
    @Vamp
    Проще всего нарисовать дугу через элемент path.

    $image = new SVG('100mm','100mm');
    $doc = $image->getDocument();
    $square = new SVGLine('0mm', '0mm', '55mm', '55mm'); // x0 y0 x1 y1
    $square->setStyle('stroke', '#FF0000'); //цвет
    $doc->addChild($square);
    
    $arc = new SVGPath('m 40,40 A 30,30,0,0,1,150,150');
    $arc->setStyle('stroke', '#FF0000');
    $doc->addChild($arc);
    
    header('Content-Type: image/svg+xml');
    echo $image;


    Нужно немного изучить формат path. Он не сложный.

    m 40,40 A 30,30,0,0,1,150,150

    m - это команда move. Устанавливает "кисть" в указанную позицию. Далее команда A - arc (дуга). Её формат несколько сложнее:

    30,30 - радиус дуги по осям x и y
    0 - угол поворота дуги вокруг оси x
    0 - должна ли дуга быть больше 180 градусов или нет
    1 - в какую сторону рисовать дугу - по часовой или против
    150,150 - координаты конца дуги
    Ответ написан
    2 комментария
  • Как проверить какая ошибка в $_FILES?

    Vamp
    @Vamp
    Согласно официальной документации, превышение post_max_size можно отследить только добавив get параметр к форме:
    <form action="edit.php?processed=1">

    if (!empty($_GET['processed']) && empty($_POST) && empty($_FILES)) {
        echo 'Ошибка! Вы загрузили слишком большой файл';
    }
    Ответ написан