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