1<?php
2
3namespace Doctrine\DBAL\Driver;
4
5use Doctrine\DBAL\FetchMode;
6use Doctrine\DBAL\ParameterType;
7use PDO;
8use const E_USER_DEPRECATED;
9use function array_slice;
10use function assert;
11use function func_get_args;
12use function is_array;
13use function sprintf;
14use function trigger_error;
15
16/**
17 * The PDO implementation of the Statement interface.
18 * Used by all PDO-based drivers.
19 */
20class PDOStatement extends \PDOStatement implements Statement
21{
22    private const PARAM_TYPE_MAP = [
23        ParameterType::NULL         => PDO::PARAM_NULL,
24        ParameterType::INTEGER      => PDO::PARAM_INT,
25        ParameterType::STRING       => PDO::PARAM_STR,
26        ParameterType::BINARY       => PDO::PARAM_LOB,
27        ParameterType::LARGE_OBJECT => PDO::PARAM_LOB,
28        ParameterType::BOOLEAN      => PDO::PARAM_BOOL,
29    ];
30
31    private const FETCH_MODE_MAP = [
32        FetchMode::ASSOCIATIVE     => PDO::FETCH_ASSOC,
33        FetchMode::NUMERIC         => PDO::FETCH_NUM,
34        FetchMode::MIXED           => PDO::FETCH_BOTH,
35        FetchMode::STANDARD_OBJECT => PDO::FETCH_OBJ,
36        FetchMode::COLUMN          => PDO::FETCH_COLUMN,
37        FetchMode::CUSTOM_OBJECT   => PDO::FETCH_CLASS,
38    ];
39
40    /**
41     * Protected constructor.
42     */
43    protected function __construct()
44    {
45    }
46
47    /**
48     * {@inheritdoc}
49     */
50    public function setFetchMode($fetchMode, $arg2 = null, $arg3 = null)
51    {
52        $fetchMode = $this->convertFetchMode($fetchMode);
53
54        // This thin wrapper is necessary to shield against the weird signature
55        // of PDOStatement::setFetchMode(): even if the second and third
56        // parameters are optional, PHP will not let us remove it from this
57        // declaration.
58        try {
59            if ($arg2 === null && $arg3 === null) {
60                return parent::setFetchMode($fetchMode);
61            }
62
63            if ($arg3 === null) {
64                return parent::setFetchMode($fetchMode, $arg2);
65            }
66
67            return parent::setFetchMode($fetchMode, $arg2, $arg3);
68        } catch (\PDOException $exception) {
69            throw new PDOException($exception);
70        }
71    }
72
73    /**
74     * {@inheritdoc}
75     */
76    public function bindValue($param, $value, $type = ParameterType::STRING)
77    {
78        $type = $this->convertParamType($type);
79
80        try {
81            return parent::bindValue($param, $value, $type);
82        } catch (\PDOException $exception) {
83            throw new PDOException($exception);
84        }
85    }
86
87    /**
88     * {@inheritdoc}
89     */
90    public function bindParam($column, &$variable, $type = ParameterType::STRING, $length = null, $driverOptions = null)
91    {
92        $type = $this->convertParamType($type);
93
94        try {
95            return parent::bindParam($column, $variable, $type, ...array_slice(func_get_args(), 3));
96        } catch (\PDOException $exception) {
97            throw new PDOException($exception);
98        }
99    }
100
101    /**
102     * {@inheritdoc}
103     */
104    public function closeCursor()
105    {
106        try {
107            return parent::closeCursor();
108        } catch (\PDOException $exception) {
109            // Exceptions not allowed by the interface.
110            // In case driver implementations do not adhere to the interface, silence exceptions here.
111            return true;
112        }
113    }
114
115    /**
116     * {@inheritdoc}
117     */
118    public function execute($params = null)
119    {
120        try {
121            return parent::execute($params);
122        } catch (\PDOException $exception) {
123            throw new PDOException($exception);
124        }
125    }
126
127    /**
128     * {@inheritdoc}
129     */
130    public function fetch($fetchMode = null, $cursorOrientation = PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
131    {
132        $args = func_get_args();
133
134        if (isset($args[0])) {
135            $args[0] = $this->convertFetchMode($args[0]);
136        }
137
138        try {
139            return parent::fetch(...$args);
140        } catch (\PDOException $exception) {
141            throw new PDOException($exception);
142        }
143    }
144
145    /**
146     * {@inheritdoc}
147     */
148    public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
149    {
150        $args = func_get_args();
151
152        if (isset($args[0])) {
153            $args[0] = $this->convertFetchMode($args[0]);
154        }
155
156        if ($fetchMode === null && $fetchArgument === null && $ctorArgs === null) {
157            $args = [];
158        } elseif ($fetchArgument === null && $ctorArgs === null) {
159            $args = [$fetchMode];
160        } elseif ($ctorArgs === null) {
161            $args = [$fetchMode, $fetchArgument];
162        } else {
163            $args = [$fetchMode, $fetchArgument, $ctorArgs];
164        }
165
166        try {
167            $data = parent::fetchAll(...$args);
168            assert(is_array($data));
169
170            return $data;
171        } catch (\PDOException $exception) {
172            throw new PDOException($exception);
173        }
174    }
175
176    /**
177     * {@inheritdoc}
178     */
179    public function fetchColumn($columnIndex = 0)
180    {
181        try {
182            return parent::fetchColumn($columnIndex);
183        } catch (\PDOException $exception) {
184            throw new PDOException($exception);
185        }
186    }
187
188    /**
189     * Converts DBAL parameter type to PDO parameter type
190     *
191     * @param int $type Parameter type
192     */
193    private function convertParamType(int $type) : int
194    {
195        if (! isset(self::PARAM_TYPE_MAP[$type])) {
196            // TODO: next major: throw an exception
197            @trigger_error(sprintf(
198                'Using a PDO parameter type (%d given) is deprecated and will cause an error in Doctrine 3.0',
199                $type
200            ), E_USER_DEPRECATED);
201
202            return $type;
203        }
204
205        return self::PARAM_TYPE_MAP[$type];
206    }
207
208    /**
209     * Converts DBAL fetch mode to PDO fetch mode
210     *
211     * @param int $fetchMode Fetch mode
212     */
213    private function convertFetchMode(int $fetchMode) : int
214    {
215        if (! isset(self::FETCH_MODE_MAP[$fetchMode])) {
216            // TODO: next major: throw an exception
217            @trigger_error(sprintf(
218                'Using a PDO fetch mode or their combination (%d given)' .
219                ' is deprecated and will cause an error in Doctrine 3.0',
220                $fetchMode
221            ), E_USER_DEPRECATED);
222
223            return $fetchMode;
224        }
225
226        return self::FETCH_MODE_MAP[$fetchMode];
227    }
228}
229