Skip to content

Commit d1a35fa

Browse files
tw2066Chance-fyi
andauthored
Added package database-sqlserver. (#7717)
Co-authored-by: Chance <chance.fyii@gmail.com>
0 parents  commit d1a35fa

17 files changed

+2794
-0
lines changed

.gitattributes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/tests export-ignore
2+
/.github export-ignore
3+
/types export-ignore
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name: Close Pull Request
2+
3+
on:
4+
pull_request_target:
5+
types: [ opened ]
6+
7+
jobs:
8+
run:
9+
uses: hyperf/.github/.github/workflows/close-pull-request.yml@master

.github/workflows/release.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
on:
2+
push:
3+
# Sequence of patterns matched against refs/tags
4+
tags:
5+
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
6+
7+
name: Release
8+
9+
jobs:
10+
release:
11+
uses: hyperf/.github/.github/workflows/release.yml@master

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) Hyperf
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

composer.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"name": "hyperf/database-sqlserver",
3+
"type": "library",
4+
"description": "The sqlserver driver for hyperf/database.",
5+
"license": "MIT",
6+
"keywords": [
7+
"php",
8+
"hyperf",
9+
"database",
10+
"sqlserver"
11+
],
12+
"homepage": "https://hyperf.io",
13+
"support": {
14+
"docs": "https://hyperf.wiki",
15+
"issues": "https://github.com/hyperf/hyperf/issues",
16+
"pull-request": "https://github.com/hyperf/hyperf/pulls",
17+
"source": "https://github.com/hyperf/hyperf"
18+
},
19+
"require": {
20+
"php": ">=8.2",
21+
"hyperf/collection": "~3.2.0",
22+
"hyperf/context": "~3.2.0",
23+
"hyperf/database": "~3.2.0",
24+
"hyperf/event": "~3.2.0",
25+
"hyperf/stringable": "~3.2.0",
26+
"hyperf/support": "~3.2.0"
27+
},
28+
"autoload": {
29+
"psr-4": {
30+
"Hyperf\\Database\\Sqlsrv\\": "src/"
31+
}
32+
},
33+
"autoload-dev": {
34+
"psr-4": {
35+
"HyperfTest\\Database\\Sqlsrv\\": "tests"
36+
}
37+
},
38+
"config": {
39+
"sort-packages": true
40+
},
41+
"extra": {
42+
"hyperf": {
43+
"config": "Hyperf\\Database\\Sqlsrv\\ConfigProvider"
44+
}
45+
}
46+
}

src/ConfigProvider.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://hyperf.wiki
9+
* @contact group@hyperf.io
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace Hyperf\Database\Sqlsrv;
14+
15+
use Hyperf\Database\Sqlsrv\Connectors\SqlServerConnector;
16+
use Hyperf\Database\Sqlsrv\Listener\RegisterConnectionListener;
17+
18+
class ConfigProvider
19+
{
20+
public function __invoke(): array
21+
{
22+
return [
23+
'dependencies' => [
24+
'db.connector.sqlsrv' => SqlServerConnector::class,
25+
],
26+
'listeners' => [
27+
RegisterConnectionListener::class,
28+
],
29+
];
30+
}
31+
}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://hyperf.wiki
9+
* @contact group@hyperf.io
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace Hyperf\Database\Sqlsrv\Connectors;
14+
15+
use Exception;
16+
use Hyperf\Collection\Arr;
17+
use Hyperf\Database\Connectors\Connector;
18+
use Hyperf\Database\Connectors\ConnectorInterface;
19+
use Hyperf\Database\Sqlsrv\Exception\InvalidDriverException;
20+
use PDO;
21+
22+
class SqlServerConnector extends Connector implements ConnectorInterface
23+
{
24+
/**
25+
* The PDO connection options.
26+
*
27+
* @var array
28+
*/
29+
protected $options = [
30+
PDO::ATTR_CASE => PDO::CASE_NATURAL,
31+
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
32+
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
33+
PDO::ATTR_STRINGIFY_FETCHES => false,
34+
];
35+
36+
/**
37+
* Establish a database connection.
38+
*
39+
* @throws Exception
40+
*/
41+
public function connect(array $config): PDO
42+
{
43+
$options = $this->getOptions($config);
44+
45+
$connection = $this->createConnection($this->getDsn($config), $config, $options);
46+
47+
$this->configureIsolationLevel($connection, $config);
48+
49+
return $connection;
50+
}
51+
52+
/**
53+
* Set the connection transaction isolation level.
54+
*
55+
* https://learn.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql
56+
*/
57+
protected function configureIsolationLevel(PDO $connection, array $config): void
58+
{
59+
if (! isset($config['isolation_level'])) {
60+
return;
61+
}
62+
63+
$connection->prepare(
64+
"SET TRANSACTION ISOLATION LEVEL {$config['isolation_level']}"
65+
)->execute();
66+
}
67+
68+
/**
69+
* Create a DSN string from a configuration.
70+
*/
71+
protected function getDsn(array $config): string
72+
{
73+
// First we will create the basic DSN setup as well as the port if it is in
74+
// in the configuration options. This will give us the basic DSN we will
75+
// need to establish the PDO connections and return them back for use.
76+
if ($this->prefersOdbc($config)) {
77+
return $this->getOdbcDsn($config);
78+
}
79+
throw new InvalidDriverException('Coroutines processing is now only supported for pdo_odbc.');
80+
// if (in_array('sqlsrv', $this->getAvailableDrivers())) {
81+
// return $this->getSqlSrvDsn($config);
82+
// }
83+
// return $this->getDblibDsn($config);
84+
}
85+
86+
/**
87+
* Determine if the database configuration prefers ODBC.
88+
*/
89+
protected function prefersOdbc(array $config): bool
90+
{
91+
return in_array('odbc', $this->getAvailableDrivers())
92+
&& ($config['odbc'] ?? null) === true;
93+
}
94+
95+
/**
96+
* Get the DSN string for a DbLib connection.
97+
*/
98+
protected function getDblibDsn(array $config): string
99+
{
100+
return $this->buildConnectString('dblib', array_merge([
101+
'host' => $this->buildHostString($config, ':'),
102+
'dbname' => $config['database'],
103+
], Arr::only($config, ['appname', 'charset', 'version'])));
104+
}
105+
106+
/**
107+
* Get the DSN string for an ODBC connection.
108+
*/
109+
protected function getOdbcDsn(array $config): string
110+
{
111+
return isset($config['odbc_datasource_name'])
112+
? 'odbc:' . $config['odbc_datasource_name'] : '';
113+
}
114+
115+
/**
116+
* Get the DSN string for a SqlSrv connection.
117+
*/
118+
protected function getSqlSrvDsn(array $config): string
119+
{
120+
$arguments = [
121+
'Server' => $this->buildHostString($config, ','),
122+
];
123+
124+
if (isset($config['database'])) {
125+
$arguments['Database'] = $config['database'];
126+
}
127+
128+
if (isset($config['readonly'])) {
129+
$arguments['ApplicationIntent'] = 'ReadOnly';
130+
}
131+
132+
if (isset($config['pooling']) && $config['pooling'] === false) {
133+
$arguments['ConnectionPooling'] = '0';
134+
}
135+
136+
if (isset($config['appname'])) {
137+
$arguments['APP'] = $config['appname'];
138+
}
139+
140+
if (isset($config['encrypt'])) {
141+
$arguments['Encrypt'] = $config['encrypt'];
142+
}
143+
144+
if (isset($config['trust_server_certificate'])) {
145+
$arguments['TrustServerCertificate'] = $config['trust_server_certificate'];
146+
}
147+
148+
if (isset($config['multiple_active_result_sets']) && $config['multiple_active_result_sets'] === false) {
149+
$arguments['MultipleActiveResultSets'] = 'false';
150+
}
151+
152+
if (isset($config['transaction_isolation'])) {
153+
$arguments['TransactionIsolation'] = $config['transaction_isolation'];
154+
}
155+
156+
if (isset($config['multi_subnet_failover'])) {
157+
$arguments['MultiSubnetFailover'] = $config['multi_subnet_failover'];
158+
}
159+
160+
if (isset($config['column_encryption'])) {
161+
$arguments['ColumnEncryption'] = $config['column_encryption'];
162+
}
163+
164+
if (isset($config['key_store_authentication'])) {
165+
$arguments['KeyStoreAuthentication'] = $config['key_store_authentication'];
166+
}
167+
168+
if (isset($config['key_store_principal_id'])) {
169+
$arguments['KeyStorePrincipalId'] = $config['key_store_principal_id'];
170+
}
171+
172+
if (isset($config['key_store_secret'])) {
173+
$arguments['KeyStoreSecret'] = $config['key_store_secret'];
174+
}
175+
176+
if (isset($config['login_timeout'])) {
177+
$arguments['LoginTimeout'] = $config['login_timeout'];
178+
}
179+
180+
if (isset($config['authentication'])) {
181+
$arguments['Authentication'] = $config['authentication'];
182+
}
183+
184+
return $this->buildConnectString('sqlsrv', $arguments);
185+
}
186+
187+
/**
188+
* Build a connection string from the given arguments.
189+
*/
190+
protected function buildConnectString(string $driver, array $arguments): string
191+
{
192+
return $driver . ':' . implode(';', array_map(function ($key) use ($arguments) {
193+
return sprintf('%s=%s', $key, $arguments[$key]);
194+
}, array_keys($arguments)));
195+
}
196+
197+
/**
198+
* Build a host string from the given configuration.
199+
*/
200+
protected function buildHostString(array $config, string $separator): string
201+
{
202+
if (empty($config['port'])) {
203+
return $config['host'];
204+
}
205+
206+
return $config['host'] . $separator . $config['port'];
207+
}
208+
209+
/**
210+
* Get the available PDO drivers.
211+
*/
212+
protected function getAvailableDrivers(): array
213+
{
214+
return PDO::getAvailableDrivers();
215+
}
216+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://hyperf.wiki
9+
* @contact group@hyperf.io
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace Hyperf\Database\Sqlsrv\Exception;
14+
15+
class InvalidArgumentException extends \InvalidArgumentException
16+
{
17+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* This file is part of Hyperf.
6+
*
7+
* @link https://www.hyperf.io
8+
* @document https://hyperf.wiki
9+
* @contact group@hyperf.io
10+
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
11+
*/
12+
13+
namespace Hyperf\Database\Sqlsrv\Exception;
14+
15+
use PDOException;
16+
17+
class InvalidDriverException extends PDOException
18+
{
19+
}

0 commit comments

Comments
 (0)