|
|
@@ -3,7 +3,7 @@
|
|
|
namespace App\Models\Char;
|
|
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
-use Illuminate\Support\Facades\DB;
|
|
|
+use App\Casts\BinaryTextCast;
|
|
|
|
|
|
class CharMail extends Model
|
|
|
{
|
|
|
@@ -53,170 +53,8 @@ class CharMail extends Model
|
|
|
|
|
|
public $timestamps = false;
|
|
|
|
|
|
- public function getBinLetterAttribute($value)
|
|
|
- {
|
|
|
- return $this->decodeBinaryValue($value);
|
|
|
- }
|
|
|
-
|
|
|
- public function setBinLetterAttribute($value)
|
|
|
- {
|
|
|
- // В Table_CharMail колонка binLetter хранится как VARBINARY,
|
|
|
- // поэтому передаём в UPDATE двоичные данные через HEX-литерал (0x...)
|
|
|
- // чтобы избежать имплицитного преобразования NVARCHAR -> VARBINARY.
|
|
|
- $encoded = $this->encodeBinaryValue($value); // ожидается двойной base64-ASCII
|
|
|
-
|
|
|
- // Сохраняем ASCII base64 как набор байт (varbinary) — это позволит
|
|
|
- // корректно прочитать значение и декодировать его в accessor'е.
|
|
|
- $hex = '0x' . bin2hex($encoded);
|
|
|
- $this->attributes['binLetter'] = DB::raw($hex);
|
|
|
- }
|
|
|
-
|
|
|
- public function getBinTitleAttribute($value)
|
|
|
- {
|
|
|
- return $this->decodeBinaryValue($value);
|
|
|
- }
|
|
|
-
|
|
|
- public function setBinTitleAttribute($value)
|
|
|
- {
|
|
|
- $this->attributes['binTitle'] = $this->encodeBinaryValue($value);
|
|
|
- }
|
|
|
-
|
|
|
- private function decodeBinaryValue(?string $value): ?string
|
|
|
- {
|
|
|
- // Удаляем завершающие нули – они часто встречаются в char( n ) столбцах
|
|
|
- $value = $this->stripNulls($value);
|
|
|
- if ($value === null || $value === '') {
|
|
|
- return $value;
|
|
|
- }
|
|
|
-
|
|
|
- // Пробуем декодировать максимум два раза (схема хранения – двойной base64)
|
|
|
- $decoded = $value;
|
|
|
- for ($i = 0; $i < 2; $i++) {
|
|
|
- $tmp = base64_decode($decoded, true);
|
|
|
- if ($tmp === false) {
|
|
|
- // Строка не похожа на base64 – прекращаем попытки
|
|
|
- break;
|
|
|
- }
|
|
|
- $decoded = $this->stripNulls($tmp);
|
|
|
- }
|
|
|
-
|
|
|
- return $decoded === null ? null : $this->ensureUtf8($decoded);
|
|
|
- }
|
|
|
-
|
|
|
- private function encodeBinaryValue(?string $value): ?string
|
|
|
- {
|
|
|
- $value = $this->stripNulls($value);
|
|
|
- if ($value === null || $value === '') {
|
|
|
- return $value;
|
|
|
- }
|
|
|
-
|
|
|
- // Если строка уже дважды закодирована – ничего не делаем
|
|
|
- if ($this->isBase64($value)) {
|
|
|
- $inner = base64_decode($value, true);
|
|
|
- if ($inner !== false && $this->isBase64($inner)) {
|
|
|
- return $value; // уже двойной base64
|
|
|
- }
|
|
|
- // Если только один слой – добавляем ещё один
|
|
|
- return base64_encode($value);
|
|
|
- }
|
|
|
-
|
|
|
- // Конвертируем в CP1251 (игровой клиент ожидает именно эту кодировку)
|
|
|
- $prepared = @iconv('UTF-8', 'CP1251//IGNORE', $value);
|
|
|
- if ($prepared === false) {
|
|
|
- $prepared = $value; // если iconv не смог – используем как есть
|
|
|
- }
|
|
|
-
|
|
|
- // Двойное кодирование base64
|
|
|
- return base64_encode(base64_encode($prepared));
|
|
|
- }
|
|
|
-
|
|
|
- private function stripNulls(?string $value): ?string
|
|
|
- {
|
|
|
- return $value === null ? null : rtrim($value, "\0");
|
|
|
- }
|
|
|
-
|
|
|
- // Более лояльная проверка base64 (без строгого ===, учитываем отсутствие отступов и символы \r\n)
|
|
|
- private function isBase64(string $value): bool
|
|
|
- {
|
|
|
- if ($value === '') {
|
|
|
- return false;
|
|
|
- }
|
|
|
- // Проверяем набор допустимых символов и кратность 4
|
|
|
- if (preg_match('/^[A-Za-z0-9+\/\r\n]+=*$/', $value) !== 1) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- return (strlen($value) % 4 === 0);
|
|
|
- }
|
|
|
-
|
|
|
- private function ensureUtf8(string $value): string
|
|
|
- {
|
|
|
- if ($value === '') {
|
|
|
- return '';
|
|
|
- }
|
|
|
-
|
|
|
- if (mb_check_encoding($value, 'UTF-8')) {
|
|
|
- return $this->removeControlChars($value);
|
|
|
- }
|
|
|
-
|
|
|
- $candidates = [];
|
|
|
-
|
|
|
- if (function_exists('iconv')) {
|
|
|
- $candidates[] = fn () => @iconv('CP1251', 'UTF-8//IGNORE', $value);
|
|
|
- }
|
|
|
-
|
|
|
- if (function_exists('mb_convert_encoding')) {
|
|
|
- $candidates[] = fn () => @mb_convert_encoding($value, 'UTF-8', 'CP1251');
|
|
|
- $candidates[] = fn () => @mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1');
|
|
|
- }
|
|
|
-
|
|
|
- $candidates[] = fn () => utf8_encode($value);
|
|
|
-
|
|
|
- foreach ($candidates as $candidate) {
|
|
|
- $converted = $candidate();
|
|
|
- if (is_string($converted) && mb_check_encoding($converted, 'UTF-8')) {
|
|
|
- return $this->removeControlChars($converted);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (function_exists('iconv')) {
|
|
|
- $sanitized = @iconv('UTF-8', 'UTF-8//IGNORE', $value);
|
|
|
- if (is_string($sanitized) && mb_check_encoding($sanitized, 'UTF-8')) {
|
|
|
- return $this->removeControlChars($sanitized);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return '';
|
|
|
- }
|
|
|
-
|
|
|
- private function removeControlChars(string $value): string
|
|
|
- {
|
|
|
- $clean = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $value);
|
|
|
- return is_string($clean) ? $clean : '';
|
|
|
- }
|
|
|
-
|
|
|
- public function toArray()
|
|
|
- {
|
|
|
- $array = parent::toArray();
|
|
|
-
|
|
|
- // Явно декодируем бинарные поля, на случай если accessor не сработал при сериализации
|
|
|
- if (array_key_exists('binTitle', $this->attributes)) {
|
|
|
- $array['binTitle'] = $this->decodeBinaryValue($this->attributes['binTitle']);
|
|
|
- } elseif (array_key_exists('binTitle', $array)) {
|
|
|
- $array['binTitle'] = $this->decodeBinaryValue($array['binTitle']);
|
|
|
- }
|
|
|
-
|
|
|
- if (array_key_exists('binLetter', $this->attributes)) {
|
|
|
- $array['binLetter'] = $this->decodeBinaryValue($this->attributes['binLetter']);
|
|
|
- } elseif (array_key_exists('binLetter', $array)) {
|
|
|
- $array['binLetter'] = $this->decodeBinaryValue($array['binLetter']);
|
|
|
- }
|
|
|
-
|
|
|
- array_walk_recursive($array, function (&$v) {
|
|
|
- if (is_string($v)) {
|
|
|
- $v = $this->ensureUtf8($v);
|
|
|
- }
|
|
|
- });
|
|
|
- return $array;
|
|
|
- }
|
|
|
-
|
|
|
+ protected $casts = [
|
|
|
+ 'binTitle' => BinaryTextCast::class,
|
|
|
+ 'binLetter' => BinaryTextCast::class,
|
|
|
+ ];
|
|
|
}
|