Отправить сообщение через Ajax для запуска PHP-сценария в фоновом режиме - PullRequest
0 голосов
/ 15 января 2019

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

Я пытался использовать ignore_user_abort(true), header("Connection: close\r\n") и другую обработку соединений в PHP, но безуспешно. Это моя последняя попытка достичь желаемой функциональности.

Отправка скрипта

<script>
    function sendingRequest()
    {
        var form = document.getElementById("submit_change");
        var var1 = document.getElementById('var1').value;
        var var2 = document.getElementById('var2').value;
        var var3 = document.getElementById('var3').value;
        var dataString = 'var1='+ var1 + '&var2=' + var2 + '&var3=' + var3;
        $.ajax({
            type:"post",
            url:"recievingScript.php",
            data:dataString,
            cache:false
        });
        form.submit();
        return false;
    }
</script>

PHP скрипт

<?php
ignore_user_abort(true);
//includes, uses, requires, establish db connection
$var1 = $_POST['var1'];
$var2 = $_POST['var2'];
$var3 = $_POST['var3'];

header("Access-Control-Allow-Origin: *");
header("Connection: close");

//Code to be run

//end script
?>

Успешно: в сообщении Ajax скрипт PHP успешно запускается и отправляет сообщение об успешном завершении сценарию, который называется сценарием PHP. Но запуск сценария занимает около 10 секунд, поэтому не стоит заставлять пользователя ждать завершения сценария. Любой совет приветствуется, спасибо!

Ответы [ 2 ]

0 голосов
/ 15 января 2019

Мне было любопытно, и я решил попробовать предложение xhr.abort() из моего комментария.

Ниже приведен лишь базовый пример выполнения долго выполняющегося запроса AJAX PHP, при котором клиент не ожидает ответа от сервера или, скорее, досрочно прекращает выполнение сценария XHR и PHP.

Этот подход можно адаптировать для запуска локального сценария, который также отправляет запрос cURL или SOAP внешнему хосту.

https://first.domain.com/send.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Send It</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<form method="post" action="https://second.domain.com/recievingScript.php">
    <p><input type="text" name="name"/></p>
    <p>
        <button type="submit">Send</button>
    </p>
</form>
<script type="text/javascript">
    const writeLog = function(msg) {
        let date = new Date();
        window.console.log(date.toISOString() + ' ' + msg);
    };
    jQuery(function($) {
        let f = $('form');
        let xhrOptions = {
            url: f.attr('action'),
            type: f.attr('method'),
            data: {},
            cache: false,
            xhr: function() {
                let xhr = $.ajaxSettings.xhr();
                if ('undefined' === typeof xhr.upload.onload || null === xhr.upload.onload) {
                    //override the upload.onload event, only if it has not already been
                    xhr.upload.onload = function() {
                        //onload is triggered immediately after the POST headers are sent to the recipient
                        writeLog('Upload Completed - Ending Request');
                        xhr.abort();
                    };
                }
                return xhr;
            },
        };
        f.on('submit', function(e) {
            e.preventDefault();
            let formData = f.serialize();
            writeLog(formData);
            $.ajax($.extend(true, xhrOptions, {
                data: formData
            })).done(function(responseText) {
                //this is never triggered since the request is aborted
                writeLog('Success');
                writeLog(responseText);
            }).fail(function(jqxhr) {
                writeLog('Request ' + (jqxhr.readyState !== 4 ? 'Aborted' : 'Failed'));
            });
            return false;
        });
    });
</script>
</body>
</html>

Ответ:

send.html:18 2019-01-15T21:19:11.445Z name=Hello%20Dagroa
send.html:18 2019-01-15T21:19:11.558Z Upload Completed - Ending Request
send.html:18 2019-01-15T21:19:11.558Z Request Aborted

https://second.domain.com/recievingScript.php

\date_default_timezone_set('UTC');
\ignore_user_abort(true);
\header('Access-Control-Allow-Origin: https://first.domain.com');
if (!empty($_POST)) {
    $dateStart = new \DateTimeImmutable();
    //create a placeholder file
    if ($file = \tempnam(__DIR__, 'tmp_')) {
        $i = 1;
        //PHP >= 7 required for random_int - PHP < 7 use mt_rand() instead
        $rand = \random_int(5, 30);
        //add the number of seconds to create a stop date
        $dateStop = $dateStart->add(new \DateInterval(\sprintf('PT' . $rand . 'S')));
        while (new \DateTime() < $dateStop) {
            //loop until the stop date is reached
            $i++;
        }
        $dateEnd = new \DateTime();
        $dateDiff = $dateEnd->diff($dateStart);
        //write result to the temporary file
        \file_put_contents($file, \json_encode([
            'file' => \basename($file),
            'scriptFile' => \basename($_SERVER['SCRIPT_FILENAME']),
            'iterations' => $i,
            'start' => $dateStart->format(\DATE_RFC3339_EXTENDED),
            'end' => $dateEnd->format(\DATE_RFC3339_EXTENDED),
            'stop' => $dateStop->format(\DATE_RFC3339_EXTENDED),
            'elapsed' => $dateDiff->format('%i minutes, %s seconds, %f microseconds'),
            'slept' => $rand,
            'post' => $_POST,
        ], \JSON_PRETTY_PRINT));
    }
}

Ответ:

{
    "file": "tmp_hjLk4y",
    "scriptFile": "recievingScript.php",
    "iterations": 9653192,
    "start": "2019-01-15T21:19:12.171+00:00",
    "end": "2019-01-15T21:19:36.171+00:00",
    "stop": "2019-01-15T21:19:36.171+00:00",
    "elapsed": "0 minutes, 24 seconds, 3 microseconds",
    "slept": 24,
    "post": {
        "name": "Hello Dagroa"
    }
}

Примечания: Я использую php-fpm 7.2 в качестве прокси-сервера fcgi, используя Apache 2.4, который не должно иметь значения, кроме как при вызове функции random_int и DATE_RFC3339_EXTENDED.

Дополнительно const и let в JavaScript используется спецификация ECMAScript 6, которая поддерживается всеми текущими основными версиями браузера.

Буферизацию вывода PHP следует отключить, в противном случае может потребоваться принудительная очистка, используя flush() и / или ob_end_flush(). Некоторые другие условия конфигурации вашего веб-сервера также могут влиять на буферизацию вывода, например, кодирование gzip.

jQuery выдаст метод запроса OPTIONS непосредственно перед методом запроса POST. Убедитесь, что $_POST не пусто, или проверьте $_SERVER['REQUEST_METHOD'] для нужного типа, чтобы предотвратить двойное выполнение.

0 голосов
/ 15 января 2019

Вы можете настроить то, что вы называете фоновым рабочим. Выполненная функция будет выполнена в фоновом режиме, то есть пользователям не нужно ждать, пока функция полностью завершит работу. Затем вы можете выставить маршрут / Api для вызова этой функции. В Laravel они называют это Джобс, который можно поставить в очередь.

Лично у меня нет опыта настройки фонового работника, кроме Laravel, который поставляется из коробки. Но вы можете проверить эти ссылки!

Классный рабочий PHP

doBackground PHP

Laravel Queues

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...