Вы сказали:
Какие варианты решения Отследить предупреждения и исправить
Есть предупреждение приходят иногда
[24-Oct-2025 09:12:11 Etc/GMT-3] PHP Notice: iconv(): Detected an incomplete multibyte character in input string in I:\Inetpub\promo-ltd.ru\control.traceline\imap\lib\class.imap.php on line 416
[24-Oct-2025 09:12:11 Etc/GMT-3] PHP Notice: iconv(): Detected an illegal character in input string in I:\Inetpub\promo-ltd.ru\control.traceline\imap\lib\class.imap.php on line 416
[24-Oct-2025 09:12:11 Etc/GMT-3] PHP Notice: iconv(): Detected an incomplete multibyte character in input string in I:\Inetpub\promo-ltd.ru\control.traceline\imap\lib\class.imap.php on line 416
[24-Oct-2025 09:12:11 Etc/GMT-3] PHP Notice: iconv(): Detected an illegal character in input string in I:\Inetpub\promo-ltd.ru\control.traceline\imap\lib\class.imap.php on line 416
приходит файл с именем того вида _utf-8_B_0JPQsNGA0LDQvdGC0LjQudC90L7QtSDQotGA0LXQudGB0.....
сам класс
<?php
/*
* This class can be used to retrieve messages from an IMAP, POP3 and NNTP server
* @author Kiril Kirkov
* GitHub: https://github.com/kirilkirkov
* Usage example:
1. $imap = new Imap();
2. $connection_result = $imap->connect('{imap.gmail.com:993/imap/ssl}INBOX', 'user@gmail.com', 'secret_password');
if ($connection_result !== true) {
echo $connection_result; //Error message!
exit;
}
3. $messages = $imap->getMessages('text'); //Array of messages
* in $attachments_dir property set directory for attachments
* in the __destructor set errors log
*/
class Imap {
private $imapStream;
private $plaintextMessage;
private $htmlMessage;
private $emails;
private $errors = array();
private $attachments = array();
private $attachments_dir = 'attachments';//ERROR
public function connect($hostname, $username, $password) {
$connection = imap_open($hostname, $username, $password) or die('Cannot connect to Mail: ' . imap_last_error());
$this->imapStream = $connection;
return true;
}
public function getMessages($type = 'text', $uid = null, $setcont='ALL') {
$this->attachments_dir = rtrim($this->attachments_dir, '/');
$stream = $this->imapStream;
$emails = imap_search($stream, $setcont);//'ALL'
$messages = array();
if ($emails) {
$this->emails = $emails;
$this->attachments = array();
if(is_null($uid))
$uid = $_GET['id'];
$messages[] = $this->loadMessage($uid, $type);
}
return array(
"status" => "success",
"data" => array_reverse($messages)
);
}
/*
* Give message_numer from
* returned array from - getMessages
* if you want to delete message by UID, not Message number
* set FT_UID to $uid.
* Example $imap->deleteMessage(221, FT_UID); - 221 is uid
*/
public function deleteMessage($messageId, $uid = 0) {
imap_delete($this->imapStream, $messageId, $uid);
}
public function getFiles($r) { //save attachments to directory
/*
$r['file'] = str_replace("UTF-8''",'',$r['file']);
$r['file'] = urldecode($r['file']);
$r['file'] = preg_replace("/[^A-Za-zА-Яа-я0-9-_.]+/u", "_", $r['file']);*/
$this->attachments_dir = $r['attachments_dir'];
$pullPath = $this->attachments_dir . '/' . $r['path'] .'/'. $r['file'];
//file_put_contents('_!!!_DEBUG-86nt8.txt',var_export($pullPath,1),FILE_APPEND);
$res = true;
if (file_exists($pullPath)) {
$res = false;
} elseif (!is_dir($this->attachments_dir)) {
$this->errors[] = 'Cant find directory for email attachments! Message ID:' . $r['uid'];
return false;
} elseif (!is_writable($this->attachments_dir)) {
$this->errors[] = 'Attachments directory is not writable! Message ID:' . $r['uid'];
return false;
}
if (!is_dir($this->attachments_dir . '/' . $r['path'] .'/')) {
mkdir($this->attachments_dir . '/' . $r['path'] .'/');//mkdir($dirname, 0755, true);
}
if($res && !preg_match('/\.php/i', $r['file']) && !preg_match('/\.cgi/i', $r['file']) && !preg_match('/\.exe/i', $r['file']) && !preg_match('/\.dll/i', $r['file']) && !preg_match('/\.mobileconfig/i', $r['file'])){
//fopen($pullPath, 'w');//!!!!!!!!!!!!!!!!!!!!!!!!!
if (($filePointer = fopen($pullPath, 'w')) == false) {
$this->errors[] = 'Cant open file at imap class to save attachment file! Message ID:' . $r['uid'];
return false;
}
switch ($r['encoding']) {
case 3: //base64
$streamFilter = stream_filter_append($filePointer, 'convert.base64-decode', STREAM_FILTER_WRITE);
break;
case 4: //quoted-printable
$streamFilter = stream_filter_append($filePointer, 'convert.quoted-printable-decode', STREAM_FILTER_WRITE);
break;
default:
$streamFilter = null;
}
imap_savebody($this->imapStream, $filePointer, $r['uid'], $r['part'], FT_UID);
if ($streamFilter) {
stream_filter_remove($streamFilter);
}
fclose($filePointer);
return array("status" => "success", "path" => $r['path'], 'file'=>$r['file']);//return array("status" => "success", "path" => $pullPath);
}else{
return array("status" => "success", "path" => $r['path'], 'file'=>$r['file']);//return array("status" => "success", "path" => $pullPath);
}
}
private function loadMessage($uid, $type) {
$overview = $this->getOverview($uid);
$array = array();
$array['uid'] = $overview->uid;
$array['subject'] = isset($overview->subject) ? $this->decode($overview->subject) : '';
$array['date'] = date('Y-m-d h:i:sa', strtotime($overview->date));
$headers = $this->getHeaders($uid);
$array['from'] = isset($headers->from) ? $this->processAddressObject($headers->from) : array('');
$array['cc'] = isset($headers->cc) ? $this->processAddressObject($headers->cc) : array('');
$array['references'] = isset($overview->references) ? $overview->references : 0;
$structure = $this->getStructure($uid);
if (!isset($structure->parts)) { // not multipart
$this->processStructure($uid, $structure);
} else { // multipart
foreach ($structure->parts as $id => $part) {
$this->processStructure($uid, $part, $id + 1);
}
}
// После обработки всех частей, сохраняем mapping CID → файл
//$array['cid_mapping'] = array_column($this->attachments, 'filename_cid', 'cid');
$array['cid_mapping'] = array_column($this->attachments, 'file', 'cid');
// После обработки всех частей:
if ($type === 'html' && !empty($this->htmlMessage)) {
// Создаем маппинг CID → файлы
$cidMapping = [];
foreach ($this->attachments as $attachment) {
if (!empty($attachment['cid'])) {
$cidMapping[$attachment['cid']] = [
'file' => $attachment['file'] = preg_replace("/[^A-Za-zА-Яа-я0-9-_.]+/u", "_", $attachment['file']),
'path' => date('Y-m-d'), // или другой путь из saveToDirectory
'cidi' => $array['cid_mapping']
];
}
}
// Обрабатываем HTML
$this->htmlMessage = $this->processCIDImages($this->htmlMessage, $cidMapping);
}
$array['message'] = $type == 'text' ? $this->plaintextMessage : $this->htmlMessage;// $email->getMessages('html',$value->uid) ..$tab->message
$array['attachments'] = $this->attachments;
return $array;
}
private function processStructure($uid, $structure, $partIdentifier = null) {
$parameters = $this->getParametersFromStructure($structure);
if ((isset($parameters['name']) || (isset($parameters['name*']) || isset($parameters['filename'])) || isset($parameters['filename*'])) || (isset($structure->subtype) && strtolower($structure->subtype) == 'rfc822')
) {
if (isset($parameters['filename'])) {
$this->setFileName($parameters['filename']);
} elseif (isset($parameters['name'])) {
$this->setFileName($parameters['name']);
} elseif (isset($parameters['filename*'])) {
$this->setFileName($parameters['filename*']);
} elseif (isset($parameters['name*'])) {
$this->setFileName($parameters['name*']);
}
// Добавляем обработку Content-ID Извлекаем CID (удаляем угловые скобки если есть)
$contentId = isset($parameters['id']) ? trim($parameters['id'], '<>') : null;
//$filename = $this->getAttachmentFilename($parameters);
//$this->setFileName($filename);
$this->encoding = $structure->encoding;
$result_save = $this->saveToDirectory($uid, $partIdentifier);
// Добавляем Content-ID в информацию о вложении
$result_save['cid'] = $contentId;
$this->attachments[] = $result_save;
} elseif ($structure->type == 0 || $structure->type == 1) {
$messageBody = isset($partIdentifier) ?
imap_fetchbody($this->imapStream, $uid, $partIdentifier, FT_UID | FT_PEEK) : imap_body($this->imapStream, $uid, FT_UID | FT_PEEK);
//file_put_contents('../_!!!_DEBUG-imap_fetchbody-index.txt',var_export($messageBody,1),FILE_APPEND);
$messageBody = $this->decodeMessage($messageBody, $structure->encoding);
//file_put_contents('../_!!!_DEBUG-decodeMessage-index.txt',var_export($messageBody,1),FILE_APPEND);
/*$body = imap_qprint($messageBody);
file_put_contents('../_!!!_DEBUG-FINALmessageBody1-index.txt',var_export($body,1),FILE_APPEND);
$messageBodyTEST0 = imap_utf8($body);
file_put_contents('../_!!!_DEBUG-FINALmessageBody2-index.txt',var_export($messageBodyTEST0,1),FILE_APPEND);
$messageBodyTEST1 = iconv($parameters['charset'], 'UTF-8//TRANSLIT', $messageBody);
file_put_contents('../_!!!_DEBUG-FINALmessageBody3-index.txt',var_export($messageBodyTEST1,1),FILE_APPEND);
$messageBodyTEST2 = mb_convert_encoding($messageBody, 'UTF-8', $parameters['charset']);
file_put_contents('../_!!!_DEBUG-FINALmessageBody4-index.txt',var_export($messageBodyTEST2,1),FILE_APPEND);*/
// Передаем HTML-контент для проверки метатегов
$charset = $this->detectCharset($messageBody, $messageBody);//TEST 28.05.25
// Принудительная проверка русских кодировок
//$charset = $this->detectRussianEncoding($messageBody) ?: $this->detectCharset($messageBody, $messageBody);
if (mb_check_encoding($messageBody, $charset)) {
if(!is_null($charset))
$messageBody = mb_convert_encoding($messageBody, 'UTF-8', $charset);
//file_put_contents('../_!!!_DEBUG-FINALmessageBody5-index.txt',var_export($messageBody,1),FILE_APPEND);
}
$parameters__charset='';
if(!empty($parameters['charset']))
$parameters__charset=$parameters['charset'];
file_put_contents(__DIR__ .'/../../crm/__!!!_DEBUG-encodingUIDdetectCharset-index.txt',var_export(
['uid'=>$uid, 'encoding'=>$structure->encoding, 'detectCharset'=>$this->detectCharset($messageBody),
'parameterscharset'=>$parameters__charset,'charsetHTMLtag'=>$charset,'structure'=>$structure ],1),FILE_APPEND);
if (!empty($parameters['charset']) && strtolower($parameters['charset']) !== 'UTF-8') {
if (function_exists('mb_convert_encoding')) {
if (!in_array($parameters['charset'], mb_list_encodings())) {//нет кодировки
if ($structure->encoding === 0) {
$parameters['charset'] = 'US-ASCII';
} /*else {
if ($structure->encoding === 4) {
//$parameters['charset'] = 'koi8-r';
$parameters['charset'] = $this->detectCharset($messageBody);//TEST 25.05.25
}
if($parameters['charset']!='windows-1251' && $parameters['charset'] != 'koi8-r')
$parameters['charset'] = 'UTF-8';
//$parameters['charset'] = $this->detectCharset($messageBody);//TEST 25.05.25
else{
//$body = imap_qprint($messageBody);
//$messageBody = imap_utf8($body);
//file_put_contents('../_!!!_DEBUG-encoding1111IMAP-index.txt',var_export($body,1),FILE_APPEND);
//file_put_contents('../_!!!_DEBUG-encoding222IMAP-index.txt',var_export($messageBody,1),FILE_APPEND);
$parameters['charset'] = $this->detectCharset($messageBody);//TEST 25.05.25
}
//file_put_contents('../_!!!_DEBUG-parametersIMAP-index.txt',var_export($parameters['charset'],1),FILE_APPEND);//if($parameters['charset']=='koi8-r')
//file_put_contents('../_!!!_DEBUG-encodingIMAP-index.txt',var_export($structure->encoding,1),FILE_APPEND);//if($parameters['charset']=='koi8-r')
}*/
if($parameters['charset'] !== 'GBK' && $parameters['charset']!='windows-1251' && $parameters['charset'] != 'koi8-r')//03.06.25 добавил && $parameters['charset'] != 'koi8-r'
$parameters['charset'] = $this->detectCharset($messageBody);// $parameters['charset'] = 'UTF-8';
}
//file_put_contents('../_!!!_DEBUG-encodingIMAP11111111111111-index.txt',var_export($parameters['charset'],1),FILE_APPEND);//if($parameters['charset']=='koi8-r')
if(is_null($charset))//if($parameters['charset']!='windows-1251' && $charset != 'Windows-1251')
$messageBody = mb_convert_encoding($messageBody, 'UTF-8', $parameters['charset']);
/*else
$messageBody = iconv($parameters['charset'], 'UTF-8//TRANSLIT', $messageBody);*/
} else {
$messageBody = iconv($parameters['charset'], 'UTF-8//TRANSLIT', $messageBody);
}
}
if (strtolower($structure->subtype) === 'plain' || ($structure->type == 1 && strtolower($structure->subtype) !== 'alternative')) {
$this->plaintextMessage = '';
$this->plaintextMessage .= trim(htmlentities($messageBody));
$this->plaintextMessage = nl2br($this->plaintextMessage);
} elseif (strtolower($structure->subtype) === 'html') {
$this->htmlMessage = '';
$this->htmlMessage .= $messageBody;
}
//file_put_contents('../_!!!_DEBUG-FINALmessageBody-index.txt',var_export($messageBody,1),FILE_APPEND);
}
if (isset($structure->parts)) {
foreach ($structure->parts as $partIndex => $part) {
$partId = $partIndex + 1;
if (isset($partIdentifier))
$partId = $partIdentifier . '.' . $partId;
$this->processStructure($uid, $part, $partId);
}
}
}
private function processCIDImages($html, $cidMapping) {
foreach ($cidMapping as $cid => $fileInfo) {
if (!empty($cid)) {
// Формируем относительный путь к файлу
//$filePath = $this->attachments_dir . '/' . $fileInfo['path'] . '/' . $fileInfo['file'];//
/*$html = str_replace(
'cid:' . $cid,
$this->attachments_dir . '/' . date('Y-m-d') . '/' . $fileInfo, //$fileInfo['file'] filename
$html
);*/
// Заменяем все вхождения CID
/*$html = str_replace(
'src="cid:' . $cid . '"',
'src="' . $filePath . '"',
$html
);*/
$html = str_replace(
'cid:' . $cid,
'cid:'.$fileInfo['file'].'@'.$cid,
$html
);
}
}
//file_put_contents('../_!!!_DEBUG-cidMapping.txt',var_export($cidMapping,1),FILE_APPEND);
return $html;
}
private function isAttachment($structure, $parameters) {
// Проверяем по наличию имени файла или специальному subtype
return (isset($parameters['name']) ||
isset($parameters['filename']) ||
(isset($structure->subtype) &&
strtolower($structure->subtype) == 'rfc822'));
}
private function getAttachmentFilename($parameters) {
if (isset($parameters['filename*'])) {
return $this->decode($parameters['filename*']);
} elseif (isset($parameters['name*'])) {
return $this->decode($parameters['name*']);
} elseif (isset($parameters['filename'])) {
return $parameters['filename'];
} elseif (isset($parameters['name'])) {
return $parameters['name'];
}
return uniqid() . '.dat'; // fallback
}
private function setFileName($text) {
$this->filename = $this->decode($text);
}
/*
* save attachments to directory
*/
private function saveToDirectory($uid, $partIdentifier) { //save attachments to directory
$array = array();
$array['part'] = $partIdentifier;
if(empty($this->filename))
$array['file'] = 'noname12.eml';
else
$array['file'] = $this->filename;
$array['encoding'] = $this->encoding;
// Генерируем уникальное имя файла, если нужно
/*$fileExt = pathinfo($this->filename, PATHINFO_EXTENSION);
$uniqueName = uniqid() . ($fileExt ? '.' . $fileExt : '');
$array['filename_cid'] = $uniqueName;*/
//$array['filename_cid'] = $this->filename;
// Сохраняем файл
/*$r = [
'uid' => $uid,
'part' => $partIdentifier,
'file' => $uniqueName,
'path' => date('Y-m-d'),
'encoding' => $this->encoding
];*/
//$this->getFiles($r);//path не известен ?
return $array;
}
private function decodeMessage($data, $encoding) {
if (!is_numeric($encoding)) {
$encoding = strtolower($encoding);
}
switch (true) {
# 8BIT
case $encoding === 1:
return quoted_printable_decode(imap_8bit($data));
# BINARY
case $encoding === 2:
return imap_binary($data);
# BASE64
case $encoding === 'base64':
case $encoding === 3:
return base64_decode($data);
# QUOTED-PRINTABLE
case $encoding === 'quoted-printable':
case $encoding === 4:
return quoted_printable_decode($data);
default:
return $data;
}
}
/*private function decodeMessage($data, $encoding) {
// Декодируем содержимое
$decoded = parent::decodeMessage($data, $encoding);
// Дополнительная обработка кодировки
if (!mb_check_encoding($decoded, 'UTF-8')) {
$charset = $this->detectCharset($decoded);
$decoded = mb_convert_encoding($decoded, 'UTF-8', $charset);
}
// Чистка невидимых символов
return preg_replace('/[^\P{C}\n\r\t]+/u', '', $decoded);
}*/
private function getParametersFromStructure($structure) {
$parameters = array();
// Основные параметры
if (isset($structure->parameters)) {
foreach ($structure->parameters as $param) {
$parameters[strtolower($param->attribute)] = $param->value;
}
}
// Дополнительные параметры (для RFC 2231)
if (isset($structure->dparameters)) {
foreach ($structure->dparameters as $param) {
$parameters[strtolower($param->attribute)] = $param->value;
}
}
// Специально для Content-ID
if (isset($structure->id)) {
$parameters['id'] = $structure->id;
}
return $parameters;
}
private function getOverview($uid) {
$results = imap_fetch_overview($this->imapStream, $uid, FT_UID);
$messageOverview = array_shift($results);
if (!isset($messageOverview->date)) {
$messageOverview->date = null;
}
return $messageOverview;
}
private function decode($text) {
if (null === $text) {
return null;//что то сделать '' типо нет текста
}
$result = '';
foreach (imap_mime_header_decode($text) as $word) {
$ch = 'default' === $word->charset ? 'ascii' : $word->charset;
$result .= iconv($ch, 'utf-8', $word->text);
//текстировать бывает ошибка
//PHP Notice: iconv(): Detected an incomplete multibyte character in input string in line 237
//PHP Notice: iconv(): Detected an illegal character in input string in line 237
/*
$encoding = mb_detect_encoding($text, mb_detect_order(), false);
if($encoding == "UTF-8") {
$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');
}
$out = iconv(mb_detect_encoding($text, mb_detect_order(), false), "UTF-8//IGNORE", $text);*/
}
if($result=='')
$result = $this->fallbackDecode($text);//'error5g9kd4uniqid'.date('Y-m-d');
return $result;
}
private function decodeHeader($text) {
if (empty($text)) {
return '';
}
// Обработка MIME-заголовков (RFC 2047)
if (preg_match('/=\?([^?]+)\?(Q|B)\?([^?]+)\?=/i', $text)) {
$decoded = iconv_mime_decode($text, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, 'UTF-8');
return $decoded !== false ? $decoded : $this->fallbackDecode($text);
}
return $this->fallbackDecode($text);
}
private function fallbackDecode($text) {
// Попробуем определить кодировку
$encoding = mb_detect_encoding($text, [
'UTF-8', 'Windows-1251', 'KOI8-R', 'ISO-8859-1', 'ISO-8859-5'
], true);
if ($encoding === false) {
$encoding = 'auto';// 'Windows-1251'; // Дефолтная для русских писем
}
// Конвертируем в UTF-8 с обработкой ошибок
return mb_convert_encoding($text, 'UTF-8', $encoding);
}
/*private function sanitizeFilename($filename) {
// Декодируем заголовок
$cleanName = $this->decodeHeader($filename);
// Удаляем опасные символы
$cleanName = preg_replace([
'/[^\p{L}\p{N}\s\-_\.]/u', // Разрешаем буквы, цифры, пробелы, -_.
'/\s+/', // Множественные пробелы
'/\.\.+/', // Множественные точки
'/^-+/', // Дефисы в начале
'/-+$/' // Дефисы в конце
], ['', ' ', '.', '', ''], $cleanName);
// Обрезаем длинное имя
$cleanName = mb_substr($cleanName, 0, 100);
// Если после обработки имя пустое - генерируем
if (empty($cleanName)) {
return uniqid('file_') . '.dat';
}
return $cleanName;
}
private function getAttachmentFilename($parameters) {
$filename = '';
// Приоритеты получения имени файла
$sources = [
$parameters['filename*'] ?? null,
$parameters['name*'] ?? null,
$parameters['filename'] ?? null,
$parameters['name'] ?? null
];
foreach ($sources as $source) {
if (!empty($source)) {
$filename = $this->sanitizeFilename($source);
break;
}
}
// Если имя не найдено в параметрах
if (empty($filename)) {
$ext = $this->getFileExtension($parameters);
return uniqid('file_') . ($ext ? '.' . $ext : '');
}
return $filename;
}
private function getFileExtension($parameters) {
// Определяем по Content-Type
if (isset($parameters['type'])) {
$mime = strtolower($parameters['type']);
$map = [
'image/jpeg' => 'jpg',
'image/png' => 'png',
'application/pdf' => 'pdf'
// ... дополните по необходимости
];
return $map[$mime] ?? null;
}
return null;
}*/
/*private function normalizeCharset($charset) {
//$charset = strtolower(trim($charset));
// Унифицируем распространённые варианты написания
$aliases = [
'koi8-r' => 'KOI8-R',
'koi8r' => 'KOI8-R',
'koi8u' => 'KOI8-U',
'windows-1251' => 'Windows-1251',
'win-1251' => 'Windows-1251',
'cp1251' => 'Windows-1251',
'iso-8859-5' => 'ISO-8859-5',
'iso8859-5' => 'ISO-8859-5',
'utf-8' => 'UTF-8',
'utf8' => 'UTF-8',
'shift_jis' => 'Shift_JIS',
'euc-jp' => 'EUC-JP',
'gb2312' => 'GB2312',
'big5' => 'BIG-5'
];
return $aliases[$charset] ?? strtoupper($charset);
}*/
private function normalizeCharsetEncodings($charset) {
//$charset = strtolower(trim($charset));
$extendedAliases = [
'ibm866' => 'IBM866',
'cp866' => 'IBM866',
'dos-866' => 'IBM866'
// ... остальные алиасы
];
// Специальные случаи, которые не совпадают с mb_list_encodings()
$specialCases = [
'koi8-r' => 'KOI8-R',
'koi8r' => 'KOI8-R',
'koi8u' => 'KOI8-U',
'windows-1251' => 'Windows-1251',
'win-1251' => 'Windows-1251',
'cp1251' => 'Windows-1251',
'iso-8859-5' => 'ISO-8859-5',
'iso8859-5' => 'ISO-8859-5',
'utf-8' => 'UTF-8',
'utf8' => 'UTF-8',
'shift_jis' => 'Shift_JIS',
'euc-jp' => 'EUC-JP',
'gb2312' => 'GB2312',
'big5' => 'BIG-5'
];
// Проверяем специальные случаи
if (isset($specialCases[$charset])) {
//return $specialCases[$charset] ?? strtoupper($charset);
return $specialCases[$charset];
}
// Получаем все поддерживаемые кодировки
//$supportedEncodings = array_map('strtolower', mb_list_encodings());
// Ищем совпадение среди стандартных кодировок
foreach (mb_list_encodings() as $enc) {
if (strtolower($enc) === $charset) {
return $enc; // Возвращаем в оригинальном регистре из mb_list_encodings()
}
}
return null; // Неизвестная кодировка
}
private function isValidCharset($charset) {
// Полный список поддерживаемых кодировок
$supported = [
'UTF-8', 'Windows-1251', 'KOI8-R', 'KOI8-U', 'ISO-8859-5',
'ISO-8859-1', 'Shift_JIS', 'EUC-JP', 'GB2312', 'BIG-5',
'Windows-1252', 'ISO-8859-2', 'ISO-8859-15'
];
return in_array($charset, $supported, true);
}
private function detectCharsetFromMeta($htmlContent) {
if (preg_match('/<meta[^>]+charset=["\']?([^"\'\s>]+)/i', $htmlContent, $matches)) {
$rawCharset = strtolower(trim($matches[1]));//trim($matches[1]);
//$normalized = $this->normalizeCharset($rawCharset);
$normalized = $this->normalizeCharsetEncodings($rawCharset);
//какой то определенный стандарт кодировок!
/*if ($this->isValidCharset($normalized)) {
return $normalized; // Возвращаем в корректном регистре
}*/
if ($normalized !== null) {
// Лучше исключить эти псевдо-кодировки:
$excluded = ['BASE64', 'UUENCODE', 'HTML-ENTITIES', 'Quoted-Printable'];
//if ($normalized && in_array($normalized, mb_list_encodings()))
return $normalized;
}
}
return null;
}
/*private function detectCharset($text, $htmlContent = '') {
// 1. Проверяем HTML-метатег если есть контент
if (!empty($htmlContent) && preg_match('/<meta[^>]+charset=["\']?([^"\'\s>]+)/i', $htmlContent, $matches)) {
$metaCharset = strtolower(trim($matches[1]));
if (in_array($metaCharset, ['koi8-r', 'windows-1251', 'iso-8859-5'])) {
return $metaCharset;
}
}
// Автоопределение с приоритетом русских кодировок
$charset = mb_detect_encoding($text, [
'UTF-8',
'Windows-1251',
'KOI8-R',
'ISO-8859-5',
'ISO-8859-1', 'ASCII'
], true);
return $charset ?: 'auto';//'Windows-1251';
}*/
private function detectCharset($text, $htmlContent = '') {
// 1. Пробуем определить из метатега
if (!empty($htmlContent)) {
$metaCharset = $this->detectCharsetFromMeta($htmlContent);
/*if ($metaCharset !== null) {
return $metaCharset; // Уже в правильном регистре
}*/
return $metaCharset;
}
// TEXT !!!!!!!! Проверка русских кодировок в первую очередь
/*if ($russianEnc = $this->detectRussianEncoding($text)) {
return $russianEnc;
}*/
// 2. Автоопределение (существующая логика)
//return $this->strictDetectEncoding($text); //НЕ ПРОБОВАЛ! По идеи лучше должно быть
/*if ($exotic = $this->detectExoticEncoding($text)) {
return $exotic;
}*/
// Автоопределение с приоритетом русских кодировок
$charset = mb_detect_encoding($text, [
'UTF-8',
'Windows-1251',
'KOI8-R',
'ISO-8859-5',
'ISO-8859-1'
], true);
return $charset ?: 'Windows-1251';//'auto';//'Windows-1251';
}
private function isValidEncoding($text, $encoding) {
// Конвертируем туда-обратно и сравниваем
$converted = mb_convert_encoding(
mb_convert_encoding($text, 'UTF-8', $encoding),
$encoding,
'UTF-8'
);
return $text === $converted;
}
private function strictDetectEncoding($text) {
// Сначала проверяем русские кодировки
if ($russianEnc = $this->detectRussianEncoding($text)) {
return $russianEnc;
}
$encodings = ['UTF-8', 'Windows-1251', 'KOI8-R', 'ISO-8859-5', 'ISO-8859-1'];
foreach ($encodings as $enc) {
if ($this->isValidEncoding($text, $enc)) {
return $enc;
}
}
// Fallback с проверкой русских символов
/*if (preg_match('/[\x80-\xFF]/', $text)) {
return 'Windows-1251';
}*/
return 'UTF-8';//или лучше auto !!!!!! Пробовать тестить нужно
}
private function detectExoticEncoding($text) {
// Специфические паттерны для редких кодировок
$patterns = [
'/\xFD[\x80-\xFF]/' => 'IBM866', // Русская DOS-кодировка
'/\xA3[\x80-\xFF]/' => 'KOI8-U', // Украинская KOI8
'/\xB3[\x80-\xFF]/' => 'ISO-8859-5' // Альтернативная кириллица
];
foreach ($patterns as $pattern => $encoding) {
if (preg_match($pattern, $text)) {
return $encoding;
}
}
// Проверка через статистику символов
$stats = count_chars($text, 1);
$ranges = [
'Windows-1251' => [192, 255],
'KOI8-R' => [224, 255],
'IBM866' => [128, 175]
];
foreach ($ranges as $enc => $range) {
$matches = 0;
foreach ($stats as $ord => $count) {
if ($ord >= $range[0] && $ord <= $range[1]) {
$matches += $count;
}
}
if ($matches / strlen($text) > 0.3) { // 30% символов в диапазоне
return $enc;
}
}
return null;
}
private function detectRussianEncoding($text) {
// Специальная проверка для повторяющихся Ї
if (preg_match('/\xD2[\x80-\xFF]{5,}/', $text)) { // Ї в Windows-1251
return 'Windows-1251';
}
// Fallback с проверкой русских символов
if (preg_match('/[\x80-\xFF]/', $text)) {
return 'Windows-1251';
}
// Статистика символов для русских кодировок
$ranges = [
'Windows-1251' => [192, 255], // Основной диапазон кириллицы
'KOI8-R' => [224, 255], // KOI8-R специфичный диапазон
'IBM866' => [128, 175] // DOS-кодировка
];
$counts = [];
foreach ($ranges as $enc => $range) {
$counts[$enc] = 0;
}
// Анализируем каждый символ
$length = strlen($text);
for ($i = 0; $i < $length; $i++) {
$ord = ord($text[$i]);
foreach ($ranges as $enc => $range) {
if ($ord >= $range[0] && $ord <= $range[1]) {
$counts[$enc]++;
}
}
}
// Выбираем кодировку с максимальным совпадением
arsort($counts);
$bestMatch = key($counts);
// Если найдено достаточное количество русских символов
if ($counts[$bestMatch] > 0 && $counts[$bestMatch] / $length > 0.3) {
return $bestMatch;
}
return null;
}
private function processAddressObject($addresses) {
$outputAddresses = array();
if (is_array($addresses))
foreach ($addresses as $address) {
if (property_exists($address, 'mailbox') && $address->mailbox != 'undisclosed-recipients') {
$currentAddress = array();
$currentAddress['address'] = $address->mailbox . '@' . $address->host;
if (isset($address->personal)) {
$currentAddress['name'] = $this->decode($address->personal);
}
$outputAddresses = $currentAddress;
}
}
return $outputAddresses;
}
private function getHeaders($uid) {
$rawHeaders = $this->getRawHeaders($uid);
$headerObject = imap_rfc822_parse_headers($rawHeaders);
if (isset($headerObject->date)) {
$headerObject->udate = strtotime($headerObject->date);
} else {
$headerObject->date = null;
$headerObject->udate = null;
}
$this->headers = $headerObject;
return $this->headers;
}
private function getRawHeaders($uid) {
$rawHeaders = imap_fetchheader($this->imapStream, $uid, FT_UID);
return $rawHeaders;
}
private function getStructure($uid) {
$structure = imap_fetchstructure($this->imapStream, $uid, FT_UID);
return $structure;
}
public function __destruct() {
if (!empty($this->errors)) {
foreach ($this->errors as $error) {
//SAVE YOUR LOG OF ERRORS
}
}
}
}