Passed
Push — tests-better-coverage ( 54ea4a...911183 )
by Michael
24:49
created

SQLSrvConnection::prepare()   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
dl 0
loc 3
ccs 2
cts 3
cp 0.6667
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 1
crap 1.037
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Driver\SQLSrv;
21
22
use Doctrine\DBAL\Driver\Connection;
23
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
24
use Doctrine\DBAL\ParameterType;
25
use const SQLSRV_ERR_ERRORS;
26
use function func_get_args;
27
use function is_float;
28
use function is_int;
29
use function sprintf;
30
use function sqlsrv_begin_transaction;
31
use function sqlsrv_commit;
32
use function sqlsrv_configure;
33
use function sqlsrv_connect;
34
use function sqlsrv_errors;
35
use function sqlsrv_query;
36
use function sqlsrv_rollback;
37
use function sqlsrv_rows_affected;
38
use function sqlsrv_server_info;
39
use function str_replace;
40
41
/**
42
 * SQL Server implementation for the Connection interface.
43
 *
44
 * @since 2.3
45
 * @author Benjamin Eberlei <[email protected]>
46
 */
47
class SQLSrvConnection implements Connection, ServerInfoAwareConnection
48
{
49
    /**
50
     * @var resource
51
     */
52
    protected $conn;
53
54
    /**
55
     * @var \Doctrine\DBAL\Driver\SQLSrv\LastInsertId
56
     */
57
    protected $lastInsertId;
58
59
    /**
60
     * @param string $serverName
61
     * @param array  $connectionOptions
62
     *
63
     * @throws \Doctrine\DBAL\Driver\SQLSrv\SQLSrvException
64
     */
65 24
    public function __construct($serverName, $connectionOptions)
66
    {
67 24
        if ( ! sqlsrv_configure('WarningsReturnAsErrors', 0)) {
68
            throw SQLSrvException::fromSqlSrvErrors();
69
        }
70
71 24
        $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...
72 24
        if ( ! $this->conn) {
73 1
            throw SQLSrvException::fromSqlSrvErrors();
74
        }
75 24
        $this->lastInsertId = new LastInsertId();
76 24
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81 23
    public function getServerVersion()
82
    {
83 23
        $serverInfo = sqlsrv_server_info($this->conn);
84
85 23
        return $serverInfo['SQLServerVersion'];
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91 24
    public function requiresQueryForServerVersion()
92
    {
93 24
        return false;
94
    }
95
96
    /**
97
     * {@inheritDoc}
98
     */
99 229
    public function prepare($sql)
100
    {
101 229
        return new SQLSrvStatement($this->conn, $sql, $this->lastInsertId);
102
    }
103
104
    /**
105
     * {@inheritDoc}
106
     */
107 167
    public function query()
108
    {
109 167
        $args = func_get_args();
110 167
        $sql = $args[0];
111 167
        $stmt = $this->prepare($sql);
112 167
        $stmt->execute();
113
114 167
        return $stmt;
115
    }
116
117
    /**
118
     * {@inheritDoc}
119
     * @license New BSD, code from Zend Framework
120
     */
121 8
    public function quote($value, $type = ParameterType::STRING)
122
    {
123 8
        if (is_int($value)) {
124 2
            return $value;
125 7
        } elseif (is_float($value)) {
126
            return sprintf('%F', $value);
127
        }
128
129 7
        return "'" . str_replace("'", "''", $value) . "'";
130
    }
131
132
    /**
133
     * {@inheritDoc}
134
     */
135 150
    public function exec($statement)
136
    {
137 150
        $stmt = sqlsrv_query($this->conn, $statement);
138
139 150
        if (false === $stmt) {
140 98
            throw SQLSrvException::fromSqlSrvErrors();
141
        }
142
143 125
        return sqlsrv_rows_affected($stmt);
144
    }
145
146
    /**
147
     * {@inheritDoc}
148
     */
149 3
    public function lastInsertId($name = null)
150
    {
151 3
        if ($name !== null) {
152 1
            $stmt = $this->prepare('SELECT CONVERT(VARCHAR(MAX), current_value) FROM sys.sequences WHERE name = ?');
153 1
            $stmt->execute([$name]);
154
        } else {
155 2
            $stmt = $this->query('SELECT @@IDENTITY');
156
        }
157
158 3
        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...
159
    }
160
161
    /**
162
     * {@inheritDoc}
163
     */
164 15
    public function beginTransaction()
165
    {
166 15
        if ( ! sqlsrv_begin_transaction($this->conn)) {
167
            throw SQLSrvException::fromSqlSrvErrors();
168
        }
169 15
    }
170
171
    /**
172
     * {@inheritDoc}
173
     */
174 6
    public function commit()
175
    {
176 6
        if ( ! sqlsrv_commit($this->conn)) {
177
            throw SQLSrvException::fromSqlSrvErrors();
178
        }
179 6
    }
180
181
    /**
182
     * {@inheritDoc}
183
     */
184 10
    public function rollBack()
185
    {
186 10
        if ( ! sqlsrv_rollback($this->conn)) {
187
            throw SQLSrvException::fromSqlSrvErrors();
188
        }
189 10
    }
190
191
    /**
192
     * {@inheritDoc}
193
     */
194
    public function errorCode()
195
    {
196
        $errors = sqlsrv_errors(SQLSRV_ERR_ERRORS);
197
        if ($errors) {
198
            return $errors[0]['code'];
199
        }
200
201
        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...
202
    }
203
204
    /**
205
     * {@inheritDoc}
206
     */
207
    public function errorInfo()
208
    {
209
        return sqlsrv_errors(SQLSRV_ERR_ERRORS);
210
    }
211
}
212