1: | <?php declare(strict_types=1); |
2: | |
3: | namespace Salient\Db; |
4: | |
5: | use Salient\Contract\Core\Entity\Readable; |
6: | use Salient\Core\Concern\ReadsProtectedProperties; |
7: | use Salient\Utility\Env; |
8: | use Salient\Utility\Format; |
9: | use Salient\Utility\Get; |
10: | use Salient\Utility\Reflect; |
11: | use ADOConnection; |
12: | use RuntimeException; |
13: | use UnexpectedValueException; |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: | |
21: | |
22: | |
23: | |
24: | |
25: | |
26: | |
27: | |
28: | final class DbConnector implements Readable |
29: | { |
30: | use ReadsProtectedProperties; |
31: | |
32: | |
33: | protected $Name; |
34: | |
35: | protected $Driver; |
36: | |
37: | protected $Dsn; |
38: | |
39: | protected $Hostname; |
40: | |
41: | protected $Port; |
42: | |
43: | protected $Username; |
44: | |
45: | protected $Password; |
46: | |
47: | protected $Database; |
48: | |
49: | protected $Schema; |
50: | |
51: | private $AdodbDriver; |
52: | |
53: | |
54: | |
55: | |
56: | |
57: | |
58: | |
59: | |
60: | |
61: | |
62: | |
63: | |
64: | |
65: | |
66: | |
67: | |
68: | |
69: | public function __construct(string $name, ?int $driver = null) |
70: | { |
71: | $driver ??= Get::arrayKey(Env::get("{$name}_driver")); |
72: | |
73: | if (is_string($driver)) { |
74: | |
75: | $driver = Reflect::getConstantValue(DbDriver::class, $driver, true); |
76: | } |
77: | |
78: | $this->Name = $name; |
79: | $this->Driver = $driver; |
80: | $this->Dsn = Env::getNullable("{$name}_dsn", null); |
81: | $this->Hostname = Env::getNullable("{$name}_hostname", null); |
82: | $this->Port = Env::getNullableInt("{$name}_port", null); |
83: | $this->Username = Env::getNullable("{$name}_username", null); |
84: | $this->Password = Env::getNullable("{$name}_password", null); |
85: | $this->Database = Env::getNullable("{$name}_database", null); |
86: | $this->Schema = Env::getNullable("{$name}_schema", null); |
87: | |
88: | $this->AdodbDriver = DbDriver::toAdodbDriver($driver); |
89: | } |
90: | |
91: | |
92: | |
93: | |
94: | private function getConnectionString(array $attributes, bool $enclose = true): string |
95: | { |
96: | $parts = []; |
97: | foreach ($attributes as $keyword => $value) { |
98: | if (is_bool($value)) { |
99: | $value = Format::bool($value); |
100: | } else { |
101: | $value = (string) $value; |
102: | } |
103: | if (($enclose && strpos($value, '}') !== false) |
104: | || (!$enclose && strpos($value, ';') !== false)) { |
105: | throw new UnexpectedValueException(sprintf( |
106: | 'Illegal character in attribute: %s', |
107: | $keyword, |
108: | )); |
109: | } |
110: | $parts[] = sprintf( |
111: | $enclose ? '%s={%s}' : '%s=%s', |
112: | $keyword, |
113: | $value, |
114: | ); |
115: | } |
116: | |
117: | return implode(';', $parts); |
118: | } |
119: | |
120: | public function getConnection(int $timeout = 15): ADOConnection |
121: | { |
122: | $db = $this->throwOnFailure( |
123: | ADONewConnection($this->AdodbDriver), |
124: | 'Error connecting to database', |
125: | ); |
126: | |
127: | $db->SetFetchMode(ADODB_FETCH_ASSOC); |
128: | |
129: | switch ($this->Driver) { |
130: | case DbDriver::DB2: |
131: | if (!Env::has('DB2CODEPAGE')) { |
132: | |
133: | Env::set('DB2CODEPAGE', '1208'); |
134: | } |
135: | |
136: | if ($this->Dsn !== null) { |
137: | $db->Connect($this->Dsn); |
138: | } else { |
139: | $db->Connect( |
140: | $this->getConnectionString([ |
141: | 'driver' => Env::get('odbc_db2_driver', 'Db2'), |
142: | 'hostname' => $this->Hostname, |
143: | 'protocol' => 'tcpip', |
144: | 'port' => $this->Port, |
145: | 'database' => $this->Database, |
146: | 'uid' => $this->Username, |
147: | 'pwd' => $this->Password, |
148: | 'connecttimeout' => $timeout, |
149: | ], false) |
150: | ); |
151: | } |
152: | |
153: | if ($this->Schema !== null) { |
154: | $db->Execute( |
155: | 'SET SCHEMA = ' . $db->Param('schema'), |
156: | ['schema' => $this->Schema] |
157: | ); |
158: | } |
159: | break; |
160: | |
161: | case DbDriver::MSSQL: |
162: | $db->setConnectionParameter('CharacterSet', 'UTF-8'); |
163: | $db->setConnectionParameter( |
164: | 'TrustServerCertificate', |
165: | Env::getBool('mssql_trust_server_certificate', false), |
166: | ); |
167: | $db->setConnectionParameter('LoginTimeout', $timeout); |
168: | |
169: | default: |
170: | $db->Connect( |
171: | (string) $this->Hostname, |
172: | (string) $this->Username, |
173: | (string) $this->Password, |
174: | (string) $this->Database, |
175: | ); |
176: | break; |
177: | } |
178: | |
179: | return $db; |
180: | } |
181: | |
182: | |
183: | |
184: | |
185: | |
186: | |
187: | |
188: | |
189: | |
190: | |
191: | private static function throwOnFailure($result, string $message, ...$args) |
192: | { |
193: | if ($result === false) { |
194: | throw new RuntimeException(sprintf($message, ...$args)); |
195: | } |
196: | |
197: | return $result; |
198: | } |
199: | } |
200: | |