Failed Conditions
Pull Request — master (#3074)
by Sergei
03:26
created

PDOConnection::trackLastInsertId()   A

Complexity

Conditions 2
Paths 4

Size

Total Lines 22
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 0
cts 0
cp 0
rs 9.2
c 0
b 0
f 0
cc 2
eloc 9
nc 4
nop 0
crap 6
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;
21
22
use Doctrine\DBAL\ParameterType;
23
use PDO;
24
use function func_get_args;
25
26
/**
27
 * PDO implementation of the Connection interface.
28
 * Used by all PDO-based drivers.
29
 *
30
 * @since 2.0
31
 */
32
class PDOConnection extends PDO implements Connection, ServerInfoAwareConnection
33
{
34
    /** @var string */
35
    private $lastInsertId = '0';
36
37
    /**
38
     * @param string      $dsn
39
     * @param string|null $user
40
     * @param string|null $password
41
     * @param array|null  $options
42
     *
43 47
     * @throws PDOException in case of an error.
44
     */
45
    public function __construct($dsn, $user = null, $password = null, array $options = null)
46 47
    {
47 46
        try {
48 46
            parent::__construct($dsn, $user, $password, $options);
49 2
            $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['Doctrine\DBAL\Driver\PDOStatement', [$this]]);
50 2
            $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
51
        } catch (\PDOException $exception) {
52 46
            throw new PDOException($exception);
53
        }
54
55
        $this->lastInsertId = new LastInsertId();
0 ignored issues
show
Documentation Bug introduced by
It seems like new Doctrine\DBAL\Driver\LastInsertId() of type Doctrine\DBAL\Driver\LastInsertId is incompatible with the declared type string of property $lastInsertId.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
56
    }
57 158
58
    /**
59
     * {@inheritdoc}
60 158
     */
61 93
    public function exec($statement)
62 93
    {
63
        try {
64
            $result = parent::exec($statement);
65
        } catch (\PDOException $exception) {
66
            throw new PDOException($exception);
67
        }
68
69
        $this->trackLastInsertId();
70
71
        return $result;
72
    }
73
74
    /**
75
     * {@inheritdoc}
76
     */
77 132
    public function getServerVersion()
78
    {
79
        return PDO::getAttribute(PDO::ATTR_SERVER_VERSION);
0 ignored issues
show
Bug Best Practice introduced by
The method PDO::getAttribute() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

79
        return PDO::/** @scrutinizer ignore-call */ getAttribute(PDO::ATTR_SERVER_VERSION);
Loading history...
80 132
    }
81 3
82 3
    /**
83
     * {@inheritdoc}
84
     */
85
    public function prepare($prepareString, $driverOptions = [])
86
    {
87
        try {
88
            return parent::prepare($prepareString, $driverOptions);
0 ignored issues
show
Bug Best Practice introduced by
The expression return parent::prepare($...String, $driverOptions) returns the type PDOStatement which is incompatible with the return type mandated by Doctrine\DBAL\Driver\Connection::prepare() of Doctrine\DBAL\Driver\Statement.

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...
89 173
        } catch (\PDOException $exception) {
90
            throw new PDOException($exception);
91 173
        }
92 173
    }
93
94
    /**
95 173
     * {@inheritdoc}
96
     */
97
    public function query()
98
    {
99 173
        try {
100
            $stmt = parent::query(...func_get_args());
0 ignored issues
show
Bug introduced by
func_get_args() is expanded, but the parameter $statement of PDO::query() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

100
            $stmt = parent::query(/** @scrutinizer ignore-type */ ...func_get_args());
Loading history...
101
102
            $this->trackLastInsertId();
103 173
104
            return $stmt;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $stmt returns the type PDOStatement which is incompatible with the return type mandated by Doctrine\DBAL\Driver\Connection::query() of Doctrine\DBAL\Driver\Statement.

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...
105
        } catch (\PDOException $exception) {
106
            throw new PDOException($exception);
107 173
        }
108 6
    }
109 6
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function quote($input, $type = ParameterType::STRING)
114
    {
115
        return parent::quote($input, $type);
116 4
    }
117
118 4
    /**
119
     * {@inheritdoc}
120
     */
121
    public function lastInsertId($name = null)
122
    {
123
        if ($name === null) {
124 2
            return $this->lastInsertId->get();
125
        }
126 2
127
        try {
128
            return $this->fetchLastInsertId($name);
129
        } catch (\PDOException $exception) {
130
            return 0;
131
        }
132 1
    }
133
134 1
    /**
135
     * {@inheritdoc}
136
     */
137
    public function requiresQueryForServerVersion()
138
    {
139
        return false;
140
    }
141
142
    /**
143
     * Tracks the last insert ID at the current state.
144
     *
145
     * If this PDO driver is not able to fetch the last insert ID for identity columns
146
     * without influencing connection state or transaction state, this is a noop method.
147
     *
148
     * @internal this method is only supposed to be used in DBAL internals
149
     *
150
     * @throws \PDOException
151
     */
152
    public function trackLastInsertId() : void
153
    {
154
        // We need to avoid unnecessary exception generation for drivers not supporting this feature,
155
        // by temporarily disabling exception mode.
156
        $originalErrorMode = $this->getAttribute(\PDO::ATTR_ERRMODE);
157
158
        $this->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
159
160
        try {
161
            $lastInsertId = $this->fetchLastInsertId(null);
162
        } finally {
163
            // Reactivate exception mode.
164
            $this->setAttribute(\PDO::ATTR_ERRMODE, $originalErrorMode);
165
        }
166
167
        if ($lastInsertId === null) {
0 ignored issues
show
introduced by
The condition $lastInsertId === null is always false.
Loading history...
168
            // In case this driver implementation does not support this feature
169
            // or an error occurred while retrieving the last insert ID, there is nothing to track here.
170
            return;
171
        }
172
173
        $this->lastInsertId->register($lastInsertId);
174
    }
175
176
    /**
177
     * Fetches the last insert ID generated by this connection.
178
     *
179
     * This method queries the database connection for the last insert ID.
180
     *
181
     * @param string|null $sequenceName The name of the sequence to retrieve the last insert ID from,
182
     *                                  if not given the overall last insert ID is returned.
183
     *
184
     * @return int The last insert ID or 0 in case the last insert ID generated on this connection is unknown.
185
     *
186
     * @throws \PDOException
187
     */
188
    protected function fetchLastInsertId(?string $sequenceName) : int
189
    {
190
        return (int) parent::lastInsertId($sequenceName);
191
    }
192
}
193