通过引用传递参数时出现致命错误
Fatal error when passing parameter by reference
在我们的 PHP 应用程序中,有一个功能可以使用 PHPMailer
发送邮件,同时收集发送过程的日志:
function sendWithLog(PHPMailer $mail, &$debug_log, $log_level = PHPMailer_SMTP::DEBUG_CONNECTION) {
$debug_log = [];
$mail->SMTPDebug = $log_level ?: PHPMailer_SMTP::DEBUG_CONNECTION;
$mail->Debugoutput = function($message) use(&$debug_log) {
$debug_log[] = $message;
};
$sent = $mail->send();
$debug_log = !$sent
? $mail->ErrorInfo
. ($debug_log ? "\n" . implode("\n", $debug_log) : "")
: "";
return $sent;
}
所以,引用传递的变量$debug_log
应该先初始化为空数组,最后做成字符串
如果再次调用该函数,将上一次调用的 $debug_log
作为 $debug_log
传递,我会看到此错误:
PHP Fatal error: Uncaught Error: [] operator not supported for strings
但是,在用use()
语句绑定到闭包之前,变量仍然应该被初始化为空数组,所以我真的不明白这个错误是怎么发生的。
我尝试使用简化版本的代码重现该行为 here,但它似乎没有触发错误。
编辑:
我已经通过改变函数解决了这个问题:
function sendWithLog(PHPMailer $mail, &$debug_log, $log_level = PHPMailer_SMTP::DEBUG_CONNECTION) {
$messages = []; // <-- different variable for collecting the log messages!
$mail->SMTPDebug = $log_level ?: PHPMailer_SMTP::DEBUG_CONNECTION;
$mail->Debugoutput = function($message) use(&$messages) {
$messages[] = $message;
};
$sent = $mail->send();
$debug_log = !$sent
? $mail->ErrorInfo
. ($messages ? "\n" . implode("\n", $messages) : "")
: "";
return $sent;
}
但是,我仍然想知道它是如何首先触发错误的。
用法示例如下:
// $users -> query result from database
while($user = $users->fetch()) {
// build mail and then
$sent = sendWithLog($mail, $mail_log);
// here $mail_log should be initialized to NULL the first time by the PHP engine
// and in subsequent calls it is the log string of the previous call which should be
// reset to [] first thing inside the function call
if(!$sent) {
error_log("ERROR SENDING MAIL: $mail_log");
}
}
编辑 2
这是一个示例堆栈跟踪:
PHP Fatal error: Uncaught Error: [] operator not supported for strings in /opt/agews64/www/includes/class/AGEws/Mail.php:134
Stack trace:
0 /opt/agews64/www/includes/class/PHPMailer/SMTP.php(220): AGEws_Mail::{closure}('CLIENT -> SERVE...', 1)
1 /opt/agews64/www/includes/class/PHPMailer/SMTP.php(991): PHPMailer_SMTP->edebug('CLIENT -> SERVE...', 1)
2 /opt/agews64/www/includes/class/PHPMailer/SMTP.php(885): PHPMailer_SMTP->client_send('QUIT\r\n')
3 /opt/agews64/www/includes/class/PHPMailer/SMTP.php(827): PHPMailer_SMTP->sendCommand('QUIT', 'QUIT', 221)
4 /opt/agews64/www/includes/class/PHPMailer.php(1721): PHPMailer_SMTP->quit()
5 /opt/agews64/www/includes/class/PHPMailer.php(670): PHPMailer->smtpClose()
6 /opt/agews64/www/includes/class/AGEws/Mail.php(147): PHPMailer->__destruct()
7 /opt/agews64/www/includes/class/AGEws/Mail.php(118): AGEws_Mail::sendWithLog(NULL, 'SMTP Error: Dat...', 3)
8 /opt/agews64/www/includes/class/AGEws/Cron/Import.php(154): AGEws_Mail::prepareAndSendWithLog('SMTP Error: Dat...', Array, 'Risu in /opt/agews64/www/includes/class/AGEws/Mail.php on line 134
这 只是 太长了,无法发表评论,所以让我在这里输入:
我唯一能想到的考虑你的例子 - 我不确定这里工作的内部结构,但无论如何让我试着解释 - 是:
您将 $mail->Debugoutput
设置为可调用项,将项目附加到 $debug_log
。现在,当你在你的函数结束时将 $debug_log
设置为一个字符串($debug_log = !$sent
等), 在 字符串设置期间,它可能会发生在 implode()
期间,由于某种原因发生字符串连接时 $mail->Debugoutput
再次被调用(可能在 __toString()
期间?),它试图将数组项附加到 $debug_log
,这是不可能的,因为它被设置为一个字符串。因此错误。
在我们的 PHP 应用程序中,有一个功能可以使用 PHPMailer
发送邮件,同时收集发送过程的日志:
function sendWithLog(PHPMailer $mail, &$debug_log, $log_level = PHPMailer_SMTP::DEBUG_CONNECTION) {
$debug_log = [];
$mail->SMTPDebug = $log_level ?: PHPMailer_SMTP::DEBUG_CONNECTION;
$mail->Debugoutput = function($message) use(&$debug_log) {
$debug_log[] = $message;
};
$sent = $mail->send();
$debug_log = !$sent
? $mail->ErrorInfo
. ($debug_log ? "\n" . implode("\n", $debug_log) : "")
: "";
return $sent;
}
所以,引用传递的变量$debug_log
应该先初始化为空数组,最后做成字符串
如果再次调用该函数,将上一次调用的 $debug_log
作为 $debug_log
传递,我会看到此错误:
PHP Fatal error: Uncaught Error: [] operator not supported for strings
但是,在用use()
语句绑定到闭包之前,变量仍然应该被初始化为空数组,所以我真的不明白这个错误是怎么发生的。
我尝试使用简化版本的代码重现该行为 here,但它似乎没有触发错误。
编辑:
我已经通过改变函数解决了这个问题:
function sendWithLog(PHPMailer $mail, &$debug_log, $log_level = PHPMailer_SMTP::DEBUG_CONNECTION) {
$messages = []; // <-- different variable for collecting the log messages!
$mail->SMTPDebug = $log_level ?: PHPMailer_SMTP::DEBUG_CONNECTION;
$mail->Debugoutput = function($message) use(&$messages) {
$messages[] = $message;
};
$sent = $mail->send();
$debug_log = !$sent
? $mail->ErrorInfo
. ($messages ? "\n" . implode("\n", $messages) : "")
: "";
return $sent;
}
但是,我仍然想知道它是如何首先触发错误的。
用法示例如下:
// $users -> query result from database
while($user = $users->fetch()) {
// build mail and then
$sent = sendWithLog($mail, $mail_log);
// here $mail_log should be initialized to NULL the first time by the PHP engine
// and in subsequent calls it is the log string of the previous call which should be
// reset to [] first thing inside the function call
if(!$sent) {
error_log("ERROR SENDING MAIL: $mail_log");
}
}
编辑 2
这是一个示例堆栈跟踪:
PHP Fatal error: Uncaught Error: [] operator not supported for strings in /opt/agews64/www/includes/class/AGEws/Mail.php:134
Stack trace:
0 /opt/agews64/www/includes/class/PHPMailer/SMTP.php(220): AGEws_Mail::{closure}('CLIENT -> SERVE...', 1)
1 /opt/agews64/www/includes/class/PHPMailer/SMTP.php(991): PHPMailer_SMTP->edebug('CLIENT -> SERVE...', 1)
2 /opt/agews64/www/includes/class/PHPMailer/SMTP.php(885): PHPMailer_SMTP->client_send('QUIT\r\n')
3 /opt/agews64/www/includes/class/PHPMailer/SMTP.php(827): PHPMailer_SMTP->sendCommand('QUIT', 'QUIT', 221)
4 /opt/agews64/www/includes/class/PHPMailer.php(1721): PHPMailer_SMTP->quit()
5 /opt/agews64/www/includes/class/PHPMailer.php(670): PHPMailer->smtpClose()
6 /opt/agews64/www/includes/class/AGEws/Mail.php(147): PHPMailer->__destruct()
7 /opt/agews64/www/includes/class/AGEws/Mail.php(118): AGEws_Mail::sendWithLog(NULL, 'SMTP Error: Dat...', 3)
8 /opt/agews64/www/includes/class/AGEws/Cron/Import.php(154): AGEws_Mail::prepareAndSendWithLog('SMTP Error: Dat...', Array, 'Risu in /opt/agews64/www/includes/class/AGEws/Mail.php on line 134
这 只是 太长了,无法发表评论,所以让我在这里输入:
我唯一能想到的考虑你的例子 - 我不确定这里工作的内部结构,但无论如何让我试着解释 - 是:
您将 $mail->Debugoutput
设置为可调用项,将项目附加到 $debug_log
。现在,当你在你的函数结束时将 $debug_log
设置为一个字符串($debug_log = !$sent
等), 在 字符串设置期间,它可能会发生在 implode()
期间,由于某种原因发生字符串连接时 $mail->Debugoutput
再次被调用(可能在 __toString()
期间?),它试图将数组项附加到 $debug_log
,这是不可能的,因为它被设置为一个字符串。因此错误。