Completed
Pull Request — master (#3429)
by Gabriel
13:29
created

SQLSrvConnection   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 23
eloc 43
dl 0
loc 158
ccs 0
cts 88
cp 0
rs 10
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A errorInfo() 0 3 1
A errorCode() 0 8 2
A requiresQueryForServerVersion() 0 3 1
A query() 0 8 1
A getServerVersion() 0 5 1
A lastInsertId() 0 10 2
A exec() 0 9 2
A rollBack() 0 4 2
A prepare() 0 3 1
A commit() 0 4 2
A __construct() 0 11 3
A beginTransaction() 0 4 2
A quote() 0 9 3
1
<?php
2
3
namespace Doctrine\DBAL\Driver\SQLSrv;
4
5
use Doctrine\DBAL\Driver\Connection;
6
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
7
use Doctrine\DBAL\ParameterType;
8
use const SQLSRV_ERR_ERRORS;
9
use function func_get_args;
10
use function is_float;
11
use function is_int;
12
use function sprintf;
13
use function sqlsrv_begin_transaction;
14
use function sqlsrv_commit;
15
use function sqlsrv_configure;
16
use function sqlsrv_connect;
17
use function sqlsrv_errors;
18
use function sqlsrv_query;
19
use function sqlsrv_rollback;
20
use function sqlsrv_rows_affected;
21
use function sqlsrv_server_info;
22
use function str_replace;
23
24
/**
25
 * SQL Server implementation for the Connection interface.
26
 */
27
class SQLSrvConnection implements Connection, ServerInfoAwareConnection
28
{
29
    /** @var resource */
30
    protected $conn;
31
32
    /** @var LastInsertId */
33
    protected $lastInsertId;
34
35
    /**
36
     * @param string  $serverName
37
     * @param mixed[] $connectionOptions
38
     *
39
     * @throws SQLSrvException
40
     */
41
    public function __construct($serverName, $connectionOptions)
42
    {
43
        if (! sqlsrv_configure('WarningsReturnAsErrors', 0)) {
44
            throw SQLSrvException::fromSqlSrvErrors();
45
        }
46
47
        $this->conn = sqlsrv_connect($serverName, $connectionOptions);
0 ignored issues
show
Documentation Bug introduced by
It seems like sqlsrv_connect($serverName, $connectionOptions) can also be of type false. However, the property $conn is declared as type resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
48
        if (! $this->conn) {
49
            throw SQLSrvException::fromSqlSrvErrors();
50
        }
51
        $this->lastInsertId = new LastInsertId();
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57
    public function getServerVersion()
58
    {
59
        $serverInfo = sqlsrv_server_info($this->conn);
60
61
        return $serverInfo['SQLServerVersion'];
62
    }
63
64
    /**
65
     * {@inheritdoc}
66
     */
67
    public function requiresQueryForServerVersion()
68
    {
69
        return false;
70
    }
71
72
    /**
73
     * {@inheritDoc}
74
     */
75
    public function prepare($sql)
76
    {
77
        return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId);
78
    }
79
80
    /**
81
     * {@inheritDoc}
82
     */
83
    public function query()
84
    {
85
        $args = func_get_args();
86
        $sql  = $args[0];
87
        $stmt = $this->prepare($sql);
88
        $stmt->execute();
89
90
        return $stmt;
91
    }
92
93
    /**
94
     * {@inheritDoc}
95
     */
96
    public function quote($value, $type = ParameterType::STRING)
97
    {
98
        if (is_int($value)) {
99
            return $value;
100
        } elseif (is_float($value)) {
101
            return sprintf('%F', $value);
102
        }
103
104
        return "'" . str_replace("'", "''", $value) . "'";
105
    }
106
107
    /**
108
     * {@inheritDoc}
109
     */
110
    public function exec($statement)
111
    {
112
        $stmt = sqlsrv_query($this->conn, $statement);
113
114
        if ($stmt === false) {
115
            throw SQLSrvException::fromSqlSrvErrors();
116
        }
117
118
        return sqlsrv_rows_affected($stmt);
119
    }
120
121
    /**
122
     * {@inheritDoc}
123
     */
124
    public function lastInsertId($name = null)
125
    {
126
        if ($name !== null) {
127
            $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?');
128
            $stmt->execute([$name]);
129
        } else {
130
            $stmt = $this->query('SELECT @@IDENTITY');
131
        }
132
133
        return $stmt->fetchColumn();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $stmt->fetchColumn() also could return the type false which is incompatible with the return type mandated by Doctrine\DBAL\Driver\Connection::lastInsertId() of string.
Loading history...
134
    }
135
136
    /**
137
     * {@inheritDoc}
138
     */
139
    public function beginTransaction()
140
    {
141
        if (! sqlsrv_begin_transaction($this->conn)) {
142
            throw SQLSrvException::fromSqlSrvErrors();
143
        }
144
    }
145
146
    /**
147
     * {@inheritDoc}
148
     */
149
    public function commit()
150
    {
151
        if (! sqlsrv_commit($this->conn)) {
152
            throw SQLSrvException::fromSqlSrvErrors();
153
        }
154
    }
155
156
    /**
157
     * {@inheritDoc}
158
     */
159
    public function rollBack()
160
    {
161
        if (! sqlsrv_rollback($this->conn)) {
162
            throw SQLSrvException::fromSqlSrvErrors();
163
        }
164
    }
165
166
    /**
167
     * {@inheritDoc}
168
     */
169
    public function errorCode()
170
    {
171
        $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
172
        if ($errors) {
173
            return $errors[0]['code'];
174
        }
175
176
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the return type mandated by Doctrine\DBAL\Driver\Connection::errorCode() of null|string.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
177
    }
178
179
    /**
180
     * {@inheritDoc}
181
     */
182
    public function errorInfo()
183
    {
184
        return sqlsrv_errors(SQLSRV_ERR_ERRORS);
185
    }
186
}
187