PHPMailer上成功/失败的不同行为

时间:2017-12-08 16:34:35

标签: javascript php jquery phpmailer

我有一个PHPMailer表单,我正在使用.ajax使用这个SO答案jquery-ajax-post-example-with-php

它成功发送或不发送,所以我知道Mailer和验证码正在工作。现在,如果我可以获得成功的不同行为并且失败,那将是很好的,但我不会以某种方式得到这个权利。

首选行为

成功 - >重新加载屏幕并显示bootstrap模态并成功msg

失败 - >重新加载屏幕并显示带有失败消息的引导模式

我有很多代码,所以,尽可能将模态视为vanilla,而PHPMailer实际上也是发送的。我遇到的问题应该在下面的代码中,但是如果你还有别的东西,那就问问吧。尽量将问题尽可能地整理

  <script type="text/javascript">
        $(document).ready(function() {
            var request;

            $("#contactForm").submit(function(event){
                event.preventDefault();

                // Abort any pending request
                if (request) {
                    request.abort();
                }

                // setup some local variables
                var $form = $(this);

                // Let's select and cache all the fields
                var $inputs = $form.find("input, select, button, textarea");

                // Serialize the data in the form
                var serializedData = $form.serialize();

                // Let's disable the inputs for the duration of the Ajax request.
                // Note: we disable elements AFTER the form data has been serialized.
                // Disabled form elements will not be serialized.
                $inputs.prop("disabled", true);

                request = $.ajax({
                    url: "processLogin.php",
                    type: "post",
                    data: serializedData
                });

                // Callback handler that will be called on success
                request.done(function (response, textStatus, jqXHR){
                    if (response == true ) {
                        top.location.reload();
                        // top.location.href="/";
                        // $('#successEmail').modal('show');
                    } else {
                        // top.location.reload();
                        $('#failEmail').modal('show');
                    }
                });

                // Callback handler that will be called on failure
                request.fail(function (jqXHR, textStatus, errorThrown){
                    // Log the error to the console
                    // console.error(
                    //     "The following error occurred: "+
                    //     textStatus, errorThrown
                    // );
                    // top.location.reload();
                });

                // Callback handler that will be called regardless
                // if the request failed or succeeded
                request.always(function () {
                    // Reenable the inputs
                    $inputs.prop("disabled", false);
                });

            });
        });
    </script>

PHPMailer表单

<?php
    session_start();
?>
<?php
    /**
    * This example shows how to handle a simple contact form.
    */
    $msg = '';


    use PHPMailer\PHPMailer\PHPMailer;

    require './mail/PHPMailer.php';
    require './mail/Exception.php';
    require './mail/SMTP.php';
    require './mail/PHPMailerAutoload.php';

    include_once $_SERVER['DOCUMENT_ROOT'] . '/securimage/securimage.php';

    $securimage = new Securimage();

    //Don't run this unless we're handling a form submission
    if (array_key_exists('email', $_POST)) {
        date_default_timezone_set('Etc/UTC');

        //Create a new PHPMailer instance
        $mail = new PHPMailer;

        $mail->SMTPDebug = 0;                                 
        $mail->isSMTP();    
        $mail->Host = 'smtp.live.com';                 
        $mail->SMTPAuth = true;     
        $mail->Username = 'email@outlook.com';       
        $mail->Password = 'password';                    
        $mail->SMTPSecure = 'tls';                   
        $mail->Port = 587;        

        $mail->setFrom('email@outlook.com', 'Mailer');
        $mail->addAddress('email@outlook.com', 'First Last');


        $email = isset($_POST['email']) ? $_POST['email'] : null;
        $name = isset($_POST['name']) ? $_POST['name'] : null;
        $phone = isset($_POST['phone']) ? $_POST['phone'] : null;
        $message = isset($_POST['message']) ? $_POST['message'] : null;


        if ($mail->addReplyTo($_POST['email'], $_POST['name'])) {
            $mail->Subject = 'Contact request';
            $mail->isHTML(true);
            $mail->Body = <<<EOT
    <div style="width:100%">
    <div><label style="color: #044F69; font-weight:bold">Email:</label> <span>{$_POST['email']}</span></div>
    <div><label style="color: #044F69; font-weight:bold">Name:</label> <span>{$_POST['name']}</span></div>
    <div><label style="color: #044F69; font-weight:bold">Phone:</label> <span>{$_POST['phone']}</span></div>
    <div><label style="color: #044F69; font-weight:bold">Message:</label> <span>{$_POST['message']}</span></div>
    </div>
    EOT;

            if ($securimage->check($_POST['captcha_code']) == false) {
                // echo "<meta http-equiv='refresh' content='0'>";
                exit;
            }

            //Send the message, check for errors
            if (!$mail->send()) {
                $msg = 'Sorry, something went wrong. Please try again later.';
            } else {
                header("Location: /");
                exit;
            }
        } else {
            $msg = 'Invalid email address, message ignored.';
        }
    }
    ?>

P.S。我拥有的代码(见上文)显示了失败模式,无论是否通过都没有重新加载。

提前致谢!

3 个答案:

答案 0 :(得分:2)

你选择了一个相对困难的例子来学习。成功/失败后重新加载意味着您​​需要引入会话或cookie或URL参数来跟踪请求之间的状态。您需要有几个移动部件才能正确运行。你必须有理由这样做,但如果你能在没有重新加载的情况下逃脱,它可以简化事情并且可能会提供更好的用户体验。我在下面列出了两个选项的信息。

首先,PHP。您有几种可能的退出状态,具体取决于电子邮件是否已成功发送等,但处理方式不一致。在一种情况下,它会进行重定向,在某些情况下会以静默方式退出。所有案件都应保持一致;对PHP页面的请求应该生成一个响应,无论发生什么,并且理想地将该响应传递回您的调用Javascript,以便它可以对发生的任何事情采取行动。

看起来你已经开始设置了它,但没有完成它 - 你准备了一个$msg,其中包含有关发生的事情的信息,但是从未显示过,$msg完全从几个退出案例(特别是成功案例和看起来像CAPTCHA失败的事件)。

除了$msg之外,我还会添加一个status标记,以便JS可以轻松判断请求是否成功;既然你在JS中读到了这个响应,我会把它变成JSON。

请注意,由于您希望在成功/失败两种情况下重新加载页面,严格来说JSON响应并不是必需的,但我认为这是一种很好的做法,有助于在开发过程中进行调试。

为确保每个退出条件生成标准响应,我必须将实际邮件发送代码嵌套在CAPTCHA测试中。这个IMO有点混乱,我个人会重构一下returning early,以便更简单地测试各种条件,因此最终不会有3-4个嵌套测试。例如,不是将所有内容嵌套在array_key_exists('email', $_POST)测试中,而是测试反向(如果没有电子邮件值),如果失败则立即退出。

// Don't run this unless we're handling a form submission
if (!array_key_exists('email', $_POST)) {
    // Generate response and bail out!
    echo $some_response_or_whatever;
    die();
}

我还建议重新考虑一下流程,例如,如果CAPTCHA失败,你真的想重新加载只是为了表明错误吗?你正在破坏使用AJAX的一个细节 - 无需重新加载即可获得响应。也许如果某些内容失败,您只需在用户点击提交的表单上显示错误消息?如果成功,你打开你的模态,如果真的有必要重新加载。

我在下面所做的更改摘要:

  1. $mail->send()嵌套在CAPTCHA条件中;

  2. 为CAPTCHA失败创建一个包含相应消息的$response;

  3. 为成功案例创建$response;

  4. 为所有回复添加status标记;

  5. $response添加到会话中,以便在页面重新加载后可用;

  6. 最后,将响应作为JSON回应,以便调用AJAX可以判断发生了什么。

  7. 这是更新的,缩写的(我删除了大块未更改的代码)代码:

    <?php
    session_start();
    
    // ... your code
    
    //Don't run this unless we're handling a form submission
    if (array_key_exists('email', $_POST)) {
    
        // ... your code
    
        if ($mail->addReplyTo($_POST['email'], $_POST['name'])) {
    
            // ... your code
    
            if ($securimage->check($_POST['captcha_code']) == false) {
                // Generate a response in this failure case, including a message and a status flag
                $response = [
                    'status'=> 1,
                    'msg'   => 'CAPTCHA test failed!'
                ];
    
            } else {
                //Send the message, check for errors
                if (!$mail->send()) {
                    // Generate a response in this failure case, including a message and a status flag
                    $response = [
                        'status'=> 1,
                        'msg'   => 'Sorry, something went wrong. Please try again later.'
                    ];
                } else {
                    // Generate a response in the success case, including a message and a status flag
                    $response = [
                        'status'=> 0,
                        'msg'   => 'Success!'
                    ];
                }
            }
        } else {
            // Generate a response in this failure case, including a message and a status flag
            $response = [
                'status'=> 1,
                'msg'   => 'Invalid email address, message ignored.'
            ];
        }
    }
    
    // Add the response to the session, so that it will be available after reload
    $_SESSION['response'] = $response;
    
    // Finally display the response as JSON so calling JS can see what happened
    header('Content-Type: application/json');
    echo json_encode($response);
    ?>
    

    接下来,你的Javascript。由于您希望在成功和失败的情况下重新加载页面,因此您不需要测试响应。

    request.done(function (response, textStatus, jqXHR){
        // You could test the response here, but since both success and failure 
        // should reload, there is no point.
        window.location.reload();
    
        // If you did want to test response and act on it, here's how to do that:
        /*
        if (response.status == 0) {
            // Success! Do something successful, like show success modal
            $('#successEmail').modal('show');
        } else {
            // Oh no, failure - notify the user
            $('.message').html(response.msg);
        }
        */
    });
    

    您在说明中没有提及fail案例中会发生什么。请注意,当请求失败时会触发fail回调,例如404或超时或类似情况,而不会触发您的某个故障情况(如无效电子邮件)。在这种情况下,我只是在前端生成一个msg,而不重新加载:

    request.fail(function (jqXHR, textStatus, errorThrown){
        // Say you have a <div class="message"></div> somewhere, maybe under your form
        $('.message').html('Error: ' + textStatus + ', ' + errorThrown)
    });
    

    所以,现在你的PHP已经触发,设置一个会话变量并返回,你的JS重新加载了页面。您需要测试会话变量的内容,并相应地触发一些JS。在您的前端页面上,在现有$(document).ready( ...内:

    <script>
    $(document).ready(function() {
        var request;
    
        // ... your code 
    
        <?php 
        // New PHP/JS to handle what happens when the page reloads
        if (isset($_SESSION['response'])) { 
            if ($_SESSION['response']['status'] == 0) { ?>
                // Success!
                $('#successEmail').modal('show');
            <?php } else { ?>
                $('#failEmail').modal('show');
            <?php }
    
            // In both cases we need to clear the response so next reload does not
            // fire a modal again
            unset($_SESSION['response']);
        } ?>
    });
    </script>
    

    您可以使用$_SESSION['response']['msg']访问模式中的成功/失败消息的内容。

    另一个建议 - 如果您的成功和失败模式类似,您只能使用一个,并使用Javascript更新其内容。

    <script>
    <?php if (isset($_SESSION['response'])) { ?>
        // First add the message to the modal, assuming you have a 
        // <div class="message"></div> in your modal
        $('#modal .message').html('<?php echo $_SESSION['response']['msg']; ?>');
    
        // Now open the one modal to rule them all
        $('#modal').modal('show');
    
        <?php
        unset($_SESSION['response']);
    } ?>
    </script>
    

    旁注 - 我不确定你的Abort any pending request是否真的按照自己的意愿行事。 request在文档就绪时声明,因此它总是存在,AFAICT?

答案 1 :(得分:0)

您完成的回调函数应仅反映成功条件,而失败应反映替代方案。

// Callback handler that will be called on success
request.done(function(response, textStatus, jqXHR) {
  $('#successEmail').modal('show');
});

// Callback handler that will be called on failure
request.fail(function(jqXHR, textStatus, errorThrown) {
  // Log the error to the console
  // console.error(
  //     "The following error occurred: "+
  //     textStatus, errorThrown
  // );
  $("#failEmail").modal("show");
});

正如你的问题所提供/描述的那样,重新加载页面会阻止脚本显示模态 - 如果你需要重新加载,你应该存储一个localStorage变量或cookie并在页面加载时测试它的值以显示模态为必要的。

答案 2 :(得分:0)

我在相当长一段时间内没有完成PHP,但我相信下面的代码可以解决您的问题。以下是我将您的PHP代码更改为:

<?php 
   $messageSent = true;
    try {
        $mail -> send();
    }
    catch (Exception $e) {
        $messageSent = false
    }
    $response = array('success' => $messageSent);
    echo json_encode($response)

现在在你的jQuery代码中,使用$ .always()回调而不是成功:

    request.always(function (data) {
        if (data.success) {
          // success handler    
        } else {
           // error handler
        } 
    });

在您的代码中,您有以下PHP:

if (!$mail->send()) {}

我不认为该行会执行。您假设$ mail-&gt; send()如果消息未发送则返回false。我不认为是这种情况。如果您查看PHP邮件程序主页上使用的示例:https://github.com/PHPMailer/PHPMailer作者使用try / catch。这告诉我,如果消息无法发送,PHPMailer会抛出异常,如果未捕获将传播500错误。我相信你的PHPMailer设置没有抛出任何异常(还),这就是为什么总是调用你的Ajax成功处理程序。但是,在javascript的以下行中:if (response == true ) {您错误地认为发送到成功处理程序的第一个参数是boolean(true或false)。它不是(尽管我们可以这样做)。因此,(response == true)永远不会评估为真。

我在代码中所做的是使用像PHPMailer示例那样的try / catch。这样,如果PHPMailer失败,你的后端不会抛出500错误。但是,如果执行catch子句,我将布尔标志设置为false。这使我们能够知道在没有实际发生后端崩溃的情况下发生了故障。现在我们只需使用名为success的键创建一个JSON对象。如果消息已成功发送,则为true;如果不成功,则为false。现在我们可以将它返回到前端。

由于我们在后端捕获任何可能的异常,因此前端将始终收到HTTP 200状态代码(好的)。因此,使用.success().error()没有任何意义,因为jQuery不会基于HTTP代码知道我们是成功还是失败。相反,我们使用.always()回调并检查返回的JSON对象的值以确定成功或错误。现在我们可以简单地使用.always()处理程序中的if语句来执行成功代码(&#34; if&#34;)或错误代码(&#34; else&#34;)。您希望在显示或隐藏模态等方面做的任何事情都可以在前端的JavaScript中完成,这是代码所属的位置。

我希望这会有所帮助。