Subversion Repositories oidplus

Rev

Rev 316 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
295 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
5
 * Copyright 2019 Daniel Marschall, ViaThinkSoft
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
20
class OIDplusDatabaseConnectionPDO extends OIDplusDatabaseConnection {
21
        private $conn = null;
22
        private $last_error = null; // we need that because PDO divides prepared statement errors and normal query errors, but we have only one "error()" method
23
 
348 daniel-mar 24
        public static function getPlugin(): OIDplusDatabasePlugin {
25
                return new OIDplusDatabasePluginPDO();
26
        }
27
 
295 daniel-mar 28
        public function doQuery(string $sql, /*?array*/ $prepared_args=null): OIDplusQueryResult {
29
                $this->last_error = null;
30
                if (is_null($prepared_args)) {
31
                        $res = $this->conn->query($sql);
32
 
33
                        if ($res === false) {
34
                                $this->last_error = $this->conn->errorInfo()[2];
35
                                throw new OIDplusSQLException($sql, $this->error());
36
                        } else {
37
                                return new OIDplusQueryResultPDO($res);
38
                        }
39
                } else {
40
                        // TEST: Emulate the prepared statement
41
                        /*
42
                        foreach ($prepared_args as $arg) {
43
                                $needle = '?';
44
                                $replace = "'$arg'"; // TODO: types
45
                                $pos = strpos($sql, $needle);
46
                                if ($pos !== false) {
47
                                        $sql = substr_replace($sql, $replace, $pos, strlen($needle));
48
                                }
49
                        }
50
                        return OIDplusQueryResultPDO($this->conn->query($sql));
51
                        */
52
 
53
                        if (!is_array($prepared_args)) {
54
                                throw new OIDplusException("'prepared_args' must be either NULL or an ARRAY.");
55
                        }
56
 
57
                        foreach ($prepared_args as &$value) {
58
                                // We need to manually convert booleans into strings, because there is a
59
                                // 14 year old bug that hasn't been adressed by the PDO developers:
60
                                // https://bugs.php.net/bug.php?id=57157
61
                                // Note: We are using '1' and '0' instead of 'true' and 'false' because MySQL converts boolean to tinyint(1)
62
                                if (is_bool($value)) $value = $value ? '1' : '0';
63
                        }
64
 
65
                        $ps = $this->conn->prepare($sql);
66
                        if (!$ps) {
67
                                $this->last_error = $ps->errorInfo()[2];
68
                                throw new OIDplusSQLException($sql, 'Cannot prepare statement: '.$this->error());
69
                        }
70
                        $this->prepare_cache[$sql] = $ps;
71
 
72
                        if (!$ps->execute($prepared_args)) {
73
                                $this->last_error = $ps->errorInfo()[2];
74
                                throw new OIDplusSQLException($sql, $this->error());
75
                        }
76
                        return new OIDplusQueryResultPDO($ps);
77
                }
78
        }
79
 
80
        public function insert_id(): int {
81
                return $this->conn->lastInsertId();
82
        }
83
 
84
        public function error(): string {
85
                $err = $this->last_error;
86
                if ($err == null) $err = '';
87
                return $err;
88
        }
89
 
90
        protected function doConnect()/*: void*/ {
91
                if (!class_exists('PDO')) throw new OIDplusConfigInitializationException('PHP extension "PDO" not installed');
92
 
93
                try {
94
                        $options = [
316 daniel-mar 95
                            PDO::ATTR_ERRMODE            => PDO::ERRMODE_SILENT,
295 daniel-mar 96
                            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
97
                            PDO::ATTR_EMULATE_PREPARES   => true,
98
                        ];
99
 
100
                        // Try connecting to the database
101
                        $dsn      = OIDplus::baseConfig()->getValue('PDO_DSN',      'mysql:host=localhost;dbname=oidplus;CHARSET=UTF8');
102
                        $username = OIDplus::baseConfig()->getValue('PDO_USERNAME', 'root');
103
                        $password = OIDplus::baseConfig()->getValue('PDO_PASSWORD', '');
104
                        $this->conn = new PDO($dsn, $username, $password, $options);
105
                } catch (PDOException $e) {
106
                        $message = $e->getMessage();
107
                        throw new OIDplusConfigInitializationException('Connection to the database failed! '.$message);
108
                }
109
 
110
                $this->last_error = null;
111
 
112
                $this->query("SET NAMES 'utf8'");
113
        }
114
 
115
        protected function doDisconnect()/*: void*/ {
116
                $this->conn = null; // the connection will be closed by removing the reference
117
        }
118
 
119
        private $intransaction = false;
120
 
121
        public function transaction_supported(): bool {
122
                return true;
123
        }
124
 
125
        public function transaction_level(): int {
126
                return $this->intransaction ? 1 : 0;
127
        }
128
 
129
        public function transaction_begin()/*: void*/ {
130
                if ($this->intransaction) throw new OIDplusException("Nested transactions are not supported by this database plugin.");
131
                $this->conn->beginTransaction();
132
                $this->intransaction = true;
133
        }
134
 
135
        public function transaction_commit()/*: void*/ {
136
                $this->conn->commit();
137
                $this->intransaction = false;
138
        }
139
 
140
        public function transaction_rollback()/*: void*/ {
141
                $this->conn->rollBack();
142
                $this->intransaction = false;
143
        }
144
}