1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace Atk4\Data\Persistence\Sql;
6:
7: use Atk4\Data\Exception;
8: use Doctrine\DBAL\Platforms\OraclePlatform;
9: use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
10: use Doctrine\DBAL\Platforms\SQLServerPlatform;
11:
12: trait BinaryTypeCompatibilityTypecastTrait
13: {
14: private function binaryTypeValueGetPrefixConst(): string
15: {
16: return 'atk__binary__u5f8mzx4vsm8g2c9__';
17: }
18:
19: private function binaryTypeValueEncode(string $value): string
20: {
21: $hex = bin2hex($value);
22:
23: return $this->binaryTypeValueGetPrefixConst() . hash('crc32b', $hex) . $hex;
24: }
25:
26: private function binaryTypeValueIsEncoded(string $value): bool
27: {
28: return str_starts_with($value, $this->binaryTypeValueGetPrefixConst());
29: }
30:
31: private function binaryTypeValueDecode(string $value): string
32: {
33: if (!$this->binaryTypeValueIsEncoded($value)) {
34: throw new Exception('Unexpected unencoded binary value');
35: }
36:
37: $hexCrc = substr($value, strlen($this->binaryTypeValueGetPrefixConst()), 8);
38: $hex = substr($value, strlen($this->binaryTypeValueGetPrefixConst()) + 8);
39: if ((strlen($hex) % 2) !== 0 || $hexCrc !== hash('crc32b', $hex)) {
40: throw new Exception('Unexpected binary value crc');
41: }
42:
43: $res = hex2bin($hex);
44: if ($this->binaryTypeValueIsEncoded($res)) {
45: throw new Exception('Unexpected double encoded binary value');
46: }
47:
48: return $res;
49: }
50:
51: private function binaryTypeIsEncodeNeeded(string $type): bool
52: {
53: // binary values for PostgreSQL and MSSQL databases are stored natively, but we need
54: // to encode first to hold the binary type info for PDO parameter type binding
55:
56: $platform = $this->getDatabasePlatform();
57: if ($platform instanceof PostgreSQLPlatform
58: || $platform instanceof SQLServerPlatform
59: || $platform instanceof OraclePlatform
60: ) {
61: if (in_array($type, ['binary', 'blob'], true)) {
62: return true;
63: }
64: }
65:
66: return false;
67: }
68:
69: /**
70: * @param scalar $value
71: */
72: private function binaryTypeIsDecodeNeeded(string $type, $value): bool
73: {
74: if ($this->binaryTypeIsEncodeNeeded($type)) {
75: // always decode for Oracle platform to assert the value is always encoded,
76: // on other platforms, binary values are stored natively
77: if ($this->getDatabasePlatform() instanceof OraclePlatform || $this->binaryTypeValueIsEncoded($value)) {
78: return true;
79: }
80: }
81:
82: return false;
83: }
84: }
85: