Failed Conditions
Pull Request — develop (#3367)
by Benjamin
14:32
created

SQLSrvConnection::quote()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1.037

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 3
cp 0.6667
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1.037
1
<?php
2
3
namespace Doctrine\DBAL\Driver\SQLSrv;
4
5
use Doctrine\DBAL\Driver\Connection;
6
use Doctrine\DBAL\Driver\DriverException;
7
use Doctrine\DBAL\Driver\ResultStatement;
8
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
9
use Doctrine\DBAL\Driver\Statement as DriverStatement;
10
use const SQLSRV_ERR_ERRORS;
11
use function sqlsrv_begin_transaction;
12
use function sqlsrv_commit;
13
use function sqlsrv_configure;
14
use function sqlsrv_connect;
15
use function sqlsrv_errors;
16
use function sqlsrv_query;
17
use function sqlsrv_rollback;
18
use function sqlsrv_rows_affected;
19
use function sqlsrv_server_info;
20
use function str_replace;
21
22
/**
23
 * SQL Server implementation for the Connection interface.
24
 */
25
class SQLSrvConnection implements Connection, ServerInfoAwareConnection
26
{
27
    /** @var resource */
28
    protected $conn;
29
30
    /** @var LastInsertId */
31
    protected $lastInsertId;
32
33
    /**
34
     * @param string  $serverName
35
     * @param mixed[] $connectionOptions
36
     *
37
     * @throws DriverException
38
     */
39 26
    public function __construct($serverName, $connectionOptions)
40
    {
41 26
        if (! sqlsrv_configure('WarningsReturnAsErrors', 0)) {
42
            throw self::exceptionFromSqlSrvErrors();
43
        }
44
45 26
        $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...
46 26
        if (! $this->conn) {
47 1
            throw self::exceptionFromSqlSrvErrors();
48
        }
49 26
        $this->lastInsertId = new LastInsertId();
50 26
    }
51
52
    /**
53
     * {@inheritdoc}
54
     */
55 23
    public function getServerVersion()
56
    {
57 23
        $serverInfo = sqlsrv_server_info($this->conn);
58
59 23
        return $serverInfo['SQLServerVersion'];
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65 24
    public function requiresQueryForServerVersion()
66
    {
67 24
        return false;
68
    }
69
70
    /**
71
     * {@inheritDoc}
72
     */
73 244
    public function prepare(string $sql) : DriverStatement
74
    {
75 244
        return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId);
76
    }
77
78
    /**
79
     * {@inheritDoc}
80
     */
81 180
    public function query(string $sql) : ResultStatement
82
    {
83 180
        $stmt = $this->prepare($sql);
84 180
        $stmt->execute();
85
86 180
        return $stmt;
87
    }
88
89
    /**
90
     * {@inheritDoc}
91
     */
92 7
    public function quote(string $value) : string
93
    {
94 7
        return "'" . str_replace("'", "''", $value) . "'";
95
    }
96
97
    /**
98
     * {@inheritDoc}
99
     */
100 162
    public function exec(string $statement) : int
101
    {
102 162
        $stmt = sqlsrv_query($this->conn, $statement);
103
104 162
        if ($stmt === false) {
105 103
            throw self::exceptionFromSqlSrvErrors();
106
        }
107
108 137
        return sqlsrv_rows_affected($stmt);
109
    }
110
111
    /**
112
     * {@inheritDoc}
113
     */
114 5
    public function lastInsertId(?string $name = null) : string
115
    {
116 5
        if ($name !== null) {
117 2
            $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?');
118 2
            $stmt->execute([$name]);
119
        } else {
120 3
            $stmt = $this->query('SELECT @@IDENTITY');
121
        }
122
123 5
        $result = $stmt->fetchColumn();
124
125 5
        if ($name !== null) {
126 2
            if ($result === false) {
127 2
                throw DriverException::noSuchSequence($name);
128
            }
129
        } else {
130 3
            if ($result === null) {
131 1
                throw DriverException::noInsertId();
132
            }
133
        }
134
135 3
        return (string) $result;
136
    }
137
138
    /**
139
     * {@inheritDoc}
140
     */
141 15
    public function beginTransaction() : void
142
    {
143 15
        if (! sqlsrv_begin_transaction($this->conn)) {
144
            throw self::exceptionFromSqlSrvErrors();
145
        }
146 15
    }
147
148
    /**
149
     * {@inheritDoc}
150
     */
151 6
    public function commit() : void
152
    {
153 6
        if (! sqlsrv_commit($this->conn)) {
154
            throw self::exceptionFromSqlSrvErrors();
155
        }
156 6
    }
157
158
    /**
159
     * {@inheritDoc}
160
     */
161 10
    public function rollBack() : void
162
    {
163 10
        if (! sqlsrv_rollback($this->conn)) {
164
            throw self::exceptionFromSqlSrvErrors();
165
        }
166 10
    }
167
168
    /**
169
     * {@inheritDoc}
170
     */
171
    public function errorCode()
172
    {
173
        $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
174
        if ($errors) {
175
            return $errors[0]['code'];
176
        }
177
178
        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...
179
    }
180
181
    /**
182
     * {@inheritDoc}
183
     */
184
    public function errorInfo()
185
    {
186
        return sqlsrv_errors(SQLSRV_ERR_ERRORS);
187
    }
188
189
    /**
190
     * Helper method to turn sql server errors into exception.
191
     *
192
     * @return DriverException
193
     */
194 105
    public static function exceptionFromSqlSrvErrors() : DriverException
195
    {
196 105
        $errors    = sqlsrv_errors(SQLSRV_ERR_ERRORS);
197 105
        $message   = '';
198 105
        $sqlState  = null;
199 105
        $errorCode = null;
200
201 105
        foreach ($errors as $error) {
202 105
            $message .= 'SQLSTATE [' . $error['SQLSTATE'] . ', ' . $error['code'] . ']: ' . $error['message'] . "\n";
203
204 105
            if ($sqlState === null) {
205 105
                $sqlState = $error['SQLSTATE'];
206
            }
207
208 105
            if ($errorCode !== null) {
209 1
                continue;
210
            }
211
212 105
            $errorCode = $error['code'];
213
        }
214 105
        if (! $message) {
215
            $message = 'SQL Server error occurred but no error message was retrieved from driver.';
216
        }
217
218 105
        return new DriverException(rtrim($message), $sqlState, $errorCode);
219
    }
220
}
221