У меня есть следующий код, который отлично работает, чтобы делать то, для чего он предназначен. Он очищает тела всех электронных писем в папке «Входящие» по адресу электронной почты, используя IMAP, извлекает уникальный код, отправленный внутри тела, и сохраняет его в базе данных вместе с оплаченной суммой. Я надеюсь, что когда пользователь что-то покупает, он может отправить электронный перевод Interac, а затем ввести код, который мы оба получаем по электронной почте на веб-сайте, и он применит кредит электронного перевода к их аккаунт/покупка.
Однако после настройки задания cron на цикл каждые несколько минут, чтобы содержимое в базе данных оставалось свежим, оно в конечном итоге превышает пропускную способность для учетной записи в течение дня или около того (не уверен, сколько времени это заняло, но это не заняло длинный). Теперь, как я уже сказал, код работает, но, по-видимому, он очень ресурсоемкий.
Я не передаю сценарий, потому что учетная запись электронной почты уже настроена некоторое время назад, и мы используем эту учетную запись для других электронных переводов, которые я просматриваю вручную для других транзакций.
- Есть ли способ очистить его, чтобы он работал так же, но использовал меньше ресурсов/пропускной способности?
- Не лучше ли запускать его, когда пользователь вводит свой платежный код? В зависимости от количества пользователей, использующих его, это также может привести к проблемам с пропускной способностью.
- Есть ли улучшения или какие у вас есть идеи?
define("MAX_EMAIL_COUNT", $_POST['maxcount']);
/* took from https://gist.github.com/agarzon/3123118 */
function extractEmail($content) {
$regexp = '/([a-z0-9_\.\-])+\@(([a-z0-9\-])+\.)+([a-z0-9]{2,4})+/i';
preg_match_all($regexp, $content, $m);
return isset($m[0]) ? $m[0] : array ();
}
function getAddressText(&$emailList, &$nameList, $addressObject) {
$emailList="";
$nameList="";
foreach ($addressObject as $object) {
$emailList .= ';';
if (isset($object->personal)) {
$emailList .= $object->personal;
}
$nameList .= ';';
if (isset($object->mailbox) && isset($object->host)) {
$nameList .= $object->mailbox . "@" . $object->host;
}
}
$emailList = ltrim($emailList, ';');
$nameList = ltrim($nameList, ';');
}
function processMessage($mbox, $messageNumber) {
global $db;
// get imap_fetch header and put single lines into array
$header = imap_rfc822_parse_headers(imap_fetchheader($mbox, $messageNumber));
$timestamp = strtotime($header->Date);
$fromEmailList="";
$fromNameList="";
if (isset($header->from)) {
getAddressText($fromEmailList, $fromNameList, $header->from);
}
$toEmailList="";
$toNameList="";
if (isset($header->to)) {
getAddressText($toEmailList, $toNameList, $header->to);
}
$body = imap_fetchbody($mbox, $messageNumber, 1);
//echo "<pre>".print_r($body,true)."</pre>";
/* Find Reference Number */
//echo "<pre style="background-color: #A2A2A2; border: 1px solid black">$body</pre>";
$searchfor="Reference Number";
// get the file contents, assuming the file to be readable (and exist)
$contents = $body;
// escape special characters in the query
$pattern = preg_quote($searchfor, "https://codereview.stackexchange.com/");
// finalise the regular expression, matching the whole line
$pattern = "/^.*$pattern.*\$/m";
// search, and store all matching occurences in $matches
if(preg_match_all($pattern, $contents, $matches)){
$reference = trim(str_replace('Reference Number : ','',$matches[0][0]));
}
else{
}
/* Find Amount Paid */
//echo "<pre style="background-color: #A2A2A2; border: 1px solid black">$body</pre>";
$searchfor="has sent you a money transfer for the amount of";
// get the file contents, assuming the file to be readable (and exist)
$contents = $body;
// escape special characters in the query
$pattern = preg_quote($searchfor, "https://codereview.stackexchange.com/");
// finalise the regular expression, matching the whole line
$pattern = "/^.*$pattern.*\$/m";
// search, and store all matching occurences in $matches
if(preg_match_all($pattern, $contents, $matches)){
$amount = trim(preg_replace("/[^0-9\.]/", "",$matches[0][0]),'.');
}
else{
}
$bodyEmailList = implode(';', extractEmail($body));
// Delete all messages older than one year (31557600 seconds). Divide it by two for six months.
if($timestamp < time()-31557600) {
if(imap_delete($mbox,$messageNumber)) {
/*echo "<strong>";
print_r($messageNumber . ' , ' . date("F j, Y g:i A",$timestamp).' , ' . 'Deleted' . "\n");
echo "</strong>";*/
}
}
else {
if(!empty($reference) && !empty($amount)) {
if($fromNameList == "catch@payments.interac.ca" && $toNameList!='etransfers@example.com') {
$query = "SELECT * FROM `".$db->prefix."payments_etransfer` WHERE `reference_id` = '".$reference."'";
$select = $db->select($query);
if($db->num_rows($select) > 0) {
}
else {
$do = $db->insert_sql("INSERT INTO `".$db->prefix."payments_etransfer` SET
`email_id` = '".$messageNumber."',
`timestamp` = '".$timestamp."',
`reference_id` = '".$reference."',
`amount` = '".$amount."',
`sender` = '".$fromEmailList."'");
if($do) {
}
else {
echo "Error<br><blockquote><pre>";
print_r($messageNumber . ',' . $timestamp. ',' . $reference . ',$' . $amount .
',' . $fromEmailList . ',' . $fromNameList
. ',' . $toEmailList . ',' . $toNameList
. ',' . $bodyEmailList . "\n"
);
echo "</pre></blockquote>";
}
}
}
}
}
}
// imap_timeout(IMAP_OPENTIMEOUT, 300);
// Open pop mailbox
if (!$mbox = imap_open($_POST['mailbox'], $_POST['login'], $_POST['password'])) {
die('Cannot connect/check pop mail! Exiting');
}
if ($hdr = imap_check($mbox)) {
$msgCount = $hdr->Nmsgs;
} else {
echo "Failed to get mail";
exit;
}
/* echo "<pre>";
echo 'emails count=" . $msgCount . "\n\n\n";
echo "record number,from emails list,from names list,to emails list, to names list,extracted from body\n";
*/
/* might improve performance according to
http://www.php.net/manual/en/function.imap-headerinfo.php#98809
imap_headers($mbox);
*/
for ($X = $msgCount; $X > 0; $X--) {
if($X > 0) {
processMessage($mbox, $X);
}
}
/*echo "</pre>";*/
imap_expunge($mbox);
imap_close($mbox);
/*
2 ответа
Только читать новые сообщения
Из комментария:
Он циркулирует по всем электронным письмам в папке «Входящие», так что да, он читает их все несколько раз.
Я думаю, что это источник вашей проблемы с пропускной способностью. Либо
- Уменьшите частоту cron (просто, но наименее эффективно).
- Отслеживайте, какие электронные письма вы уже читали и больше не читаете.
- Измените программу, чтобы она просматривала только электронные письма, которые пришли с момента вашего последнего просмотра (самый сложный, но и самый эффективный с точки зрения пропускной способности).
Отслеживание просмотренных сообщений
Итак, в этом коде:
for ($X = $msgCount; $X > 0; $X--) { if($X > 0) { processMessage($mbox, $X); } }
Во-первых, это может быть просто
for ($X = $msgCount; $X > 0; $X--) {
processMessage($mbox, $X);
}
if ничего не делает.
Но что еще более важно, вы хотите узнать, видели ли вы сообщение ранее.
$seen = false;
for ($X = $msgCount; !$seen && ($X > 0); $X--) {
$seen = processMessage($mbox, $X);
}
Затем измените processMessage к return false; в конце. А ранее, когда вы обрабатываете электронное письмо, сохраните идентификатор электронной почты, чтобы в следующий раз, когда вы увидите это электронное письмо, вы могли return true.
$header = imap_rfc822_parse_headers(imap_fetchheader($mbox, $messageNumber));
Это можно упростить до
$header = imap_headerinfo($mbox, $messageNumber);
Это также даст вам доступ к $header->message_id, это то, что вы можете сохранить, чтобы увидеть, видели ли вы сообщение ранее. Поочередно, imap_uid также дает идентификатор сообщения.
Переместить сообщение
Другой простой вариант — переместить сообщение из папки «Входящие» в отдельную папку. Поэтому сообщения в папке «Входящие» должны быть обработаны. Сообщения в папке уже обработаны. imap_mail_move функция перемещает сообщения.
extractEmail()на самом деле генерирует массив электронной почтыспоэтому имя функции должно быть во множественном числе./[a-z0-9_]/iпроще выражается как\w. Внутри класса символов точку не нужно экранировать. Если дефис является первым или последним символом в классе символов или если он следует сразу за диапазоном символов, его не нужно экранировать. Ан@символ не является специальным символом в регулярном выражении, поэтому его не нужно экранировать. Вы не используете эти захваченные подстроки, поэтому кажется логичным опустить их или сделать их незахватываемыми, где это уместно. Просто чтобы вы знали, будут действительные электронные письма (крайние случаи), которые не будут соответствовать вашему шаблону электронной почты. Если этого достаточно для масштаба вашего проекта, то, я полагаю, вам больше не нужно тратить на это время — просто будьте осторожны, где еще вы используете этот шаблон.function extractEmails(string $content): array { $regexp = '/[\w.-]+@(?:[a-z\d-]+\.)+[a-z\d]{2,4}+/i'; return preg_match_all($regexp, $content, $m) ? $m[0] : []; }Я нахожу имя функции
getAddressText()быть семантически вводящим в заблуждение — он ничего не «получает», он изменяет первые два аргумента, которые являются ссылочными переменными. Вместо того, чтобы безоговорочно стирать данные в первых двух строках, затем выполнять кучу конкатенации строк, а затем обрезать лишнее, я считаю более аккуратным создавать массивы, а затем взрывать их. Я полагаю, если быть честным, вам никогда не нужно уважать данные, поступающие через первые два параметра, поэтому я думаю, что просто пропущу$addressObjectи вернуть двухэлементный массив.function getAddressEmailsAndLists(array $addressObjects): array { $emails = []; $names = []; foreach ($addressObject as $object) { $emails[] = $object->personal ?? ''; $names[] = isset($object->mailbox, $object->host) ? $object->mailbox . "@" . $object->host ''; } return [implode(';', $emails), implode(';', $names)], }Один его вызов будет выглядеть так:
[$fromEmailList, $fromNameList] = getAddressText($header->from);Я обеспокоен обоими случаями
$pattern = "/^.*$pattern.*\$/m";. Во-первых, экранирующая косая черта перед$означает, что вы соответствуете буквальному знаку доллара, но ваш комментарий предполагает, что вы хотите отметить конец строки. Вы имеете в виду удалить косую черту или сопоставить буквальный знак доллара?[^0-9\.]можно упростить до[^\d.]+— это будет соответствовать тому же списку символов, но будет потенциально более длинным совпадением и, следовательно, меньшим количеством замен.Ваш sql не выглядит стабильным или безопасным для меня, потому что вы не используете подготовленные операторы с заполнителями. Это похоже на серьезную проблему для ваших оболочек запросов к базе данных.
Я согласен с оценкой @mdfst13 вашего
for()циклическая логика.
