|
@@ -31,16 +31,49 @@ class BinaryTextCast implements CastsAttributes
|
|
|
return $value;
|
|
return $value;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ $firstByte = ord($value[0]);
|
|
|
|
|
+ if ($firstByte === 0) {
|
|
|
|
|
+ return $this->ensureUtf8(substr($value, 1));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($firstByte === 1) {
|
|
|
|
|
+ $payload = substr($value, 1);
|
|
|
|
|
+ $decoded = base64_decode($payload, true);
|
|
|
|
|
+ return $this->ensureUtf8($decoded !== false ? $decoded : $payload);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
$decoded = $value;
|
|
$decoded = $value;
|
|
|
|
|
+ $hadDecode = false;
|
|
|
|
|
+
|
|
|
for ($i = 0; $i < 2; $i++) {
|
|
for ($i = 0; $i < 2; $i++) {
|
|
|
$tmp = base64_decode($decoded, true);
|
|
$tmp = base64_decode($decoded, true);
|
|
|
if ($tmp === false) {
|
|
if ($tmp === false) {
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ $hadDecode = true;
|
|
|
$decoded = $this->stripNulls($tmp);
|
|
$decoded = $this->stripNulls($tmp);
|
|
|
|
|
+ if ($decoded === null || $decoded === '') {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!$this->isBase64($decoded)) {
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return $decoded === null ? null : $this->ensureUtf8($decoded);
|
|
|
|
|
|
|
+ if ($hadDecode && is_string($decoded)) {
|
|
|
|
|
+ return $this->ensureUtf8($decoded);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($this->isBase64($value)) {
|
|
|
|
|
+ $single = base64_decode($value, true);
|
|
|
|
|
+ if ($single !== false) {
|
|
|
|
|
+ return $this->ensureUtf8($single);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $this->ensureUtf8($value);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -59,42 +92,66 @@ class BinaryTextCast implements CastsAttributes
|
|
|
|
|
|
|
|
// Определяем максимально допустимую длину VARBINARY для данной колонки
|
|
// Определяем максимально допустимую длину VARBINARY для данной колонки
|
|
|
$maxLen = $this->getVarbinaryMaxLength($model, $key);
|
|
$maxLen = $this->getVarbinaryMaxLength($model, $key);
|
|
|
|
|
+ $raw = $this->prepareRawPayload($value);
|
|
|
|
|
|
|
|
- // Готовим "сырой" CP1251 (или то, что пришло в base64) и итоговый двойной base64
|
|
|
|
|
- $raw = null;
|
|
|
|
|
- if ($this->isBase64($value)) {
|
|
|
|
|
- $inner = base64_decode($value, true);
|
|
|
|
|
- if ($inner !== false && $this->isBase64($inner)) {
|
|
|
|
|
- // Значение уже двойной base64: пробуем восстановить сырой текст
|
|
|
|
|
- $raw = base64_decode($inner, true);
|
|
|
|
|
- $encoded = $value; // пока используем исходный двойной base64
|
|
|
|
|
- } else {
|
|
|
|
|
- // Только один слой base64 → сырой текст после одного декодирования
|
|
|
|
|
- $raw = $inner !== false ? $inner : null;
|
|
|
|
|
- $encoded = base64_encode($value);
|
|
|
|
|
|
|
+ $candidates = [
|
|
|
|
|
+ base64_encode(base64_encode($raw)),
|
|
|
|
|
+ "\x01" . base64_encode($raw),
|
|
|
|
|
+ "\x00" . $raw,
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ $payload = null;
|
|
|
|
|
+
|
|
|
|
|
+ foreach ($candidates as $candidate) {
|
|
|
|
|
+ if (!is_int($maxLen) || strlen($candidate) <= $maxLen) {
|
|
|
|
|
+ $payload = $candidate;
|
|
|
|
|
+ break;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- if ($raw === null) {
|
|
|
|
|
- // Готовим сырой CP1251 из UTF-8
|
|
|
|
|
- $prepared = function_exists('iconv') ? @iconv('UTF-8', 'CP1251//IGNORE', $value) : $value;
|
|
|
|
|
- if ($prepared === false) {
|
|
|
|
|
- $prepared = $value;
|
|
|
|
|
|
|
+ if ($payload === null) {
|
|
|
|
|
+ if (!is_int($maxLen) || $maxLen <= 0) {
|
|
|
|
|
+ return DB::raw('0x');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $allowedRaw = max(0, $maxLen - 1);
|
|
|
|
|
+ $raw = substr($raw, 0, $allowedRaw);
|
|
|
|
|
+ $payload = "\x00" . $raw;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return DB::raw('0x' . bin2hex($payload));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private function prepareRawPayload(string $value): string
|
|
|
|
|
+ {
|
|
|
|
|
+ if ($value === '') {
|
|
|
|
|
+ return '';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if ($this->isBase64($value)) {
|
|
|
|
|
+ $first = base64_decode($value, true);
|
|
|
|
|
+ if (is_string($first)) {
|
|
|
|
|
+ $first = $this->stripNulls($first) ?? '';
|
|
|
|
|
+ if ($first !== '' && $this->isBase64($first)) {
|
|
|
|
|
+ $second = base64_decode($first, true);
|
|
|
|
|
+ if (is_string($second)) {
|
|
|
|
|
+ $second = $this->stripNulls($second) ?? '';
|
|
|
|
|
+ return $second;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return $first;
|
|
|
}
|
|
}
|
|
|
- $raw = $prepared;
|
|
|
|
|
- $encoded = base64_encode(base64_encode($prepared));
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Если известна максимальная длина и результат не помещается,
|
|
|
|
|
- // пересчитываем кодирование для усечения по сырой длине так, чтобы двойной base64 гарантированно влез
|
|
|
|
|
- if (is_int($maxLen) && strlen($encoded) > $maxLen) {
|
|
|
|
|
- $maxRaw = $this->maxRawLenForDoubleBase64($maxLen);
|
|
|
|
|
- $raw = substr($raw, 0, max(0, $maxRaw));
|
|
|
|
|
- $encoded = base64_encode(base64_encode($raw));
|
|
|
|
|
|
|
+ if (function_exists('iconv')) {
|
|
|
|
|
+ $converted = @iconv('UTF-8', 'CP1251//IGNORE', $value);
|
|
|
|
|
+ if (is_string($converted)) {
|
|
|
|
|
+ return $converted;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // MSSQL VARBINARY требует явного бинарного литерала
|
|
|
|
|
- return DB::raw('0x' . bin2hex($encoded));
|
|
|
|
|
|
|
+ return $value;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -212,20 +269,4 @@ class BinaryTextCast implements CastsAttributes
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * Вычисляет максимально допустимую длину "сырого" текста (в байтах),
|
|
|
|
|
- * чтобы длина двойного base64 не превышала $maxLen.
|
|
|
|
|
- */
|
|
|
|
|
- private function maxRawLenForDoubleBase64(int $maxLen): int
|
|
|
|
|
- {
|
|
|
|
|
- // Итеративный безопасный расчёт
|
|
|
|
|
- for ($n = $maxLen; $n >= 0; $n--) {
|
|
|
|
|
- $b1 = 4 * (int) ceil($n / 3);
|
|
|
|
|
- $b2 = 4 * (int) ceil($b1 / 3);
|
|
|
|
|
- if ($b2 <= $maxLen) {
|
|
|
|
|
- return $n;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return 0;
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|