aimeos /
aimeos-base
| 1 | <?php |
||
| 2 | |||
| 3 | /** |
||
| 4 | * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0 |
||
| 5 | * @copyright Aimeos (aimeos.org), 2015-2025 |
||
| 6 | * @package Base |
||
| 7 | * @subpackage DB |
||
| 8 | */ |
||
| 9 | |||
| 10 | |||
| 11 | namespace Aimeos\Base\DB\Connection; |
||
| 12 | |||
| 13 | |||
| 14 | /** |
||
| 15 | * Database connection class for \PDO connections. |
||
| 16 | * |
||
| 17 | * @package Base |
||
| 18 | * @subpackage DB |
||
| 19 | */ |
||
| 20 | class PDO extends Base implements Iface |
||
| 21 | { |
||
| 22 | private ?\PDO $connection = null; |
||
| 23 | private int $txnumber = 0; |
||
| 24 | private array $stmts = []; |
||
| 25 | |||
| 26 | |||
| 27 | /** |
||
| 28 | * Initializes the PDO connection object. |
||
| 29 | * |
||
| 30 | * @param array $params Associative list of connection parameters |
||
| 31 | */ |
||
| 32 | public function __construct( array $params ) |
||
| 33 | { |
||
| 34 | if( !isset( $params['dsn'] ) ) { |
||
| 35 | $params['dsn'] = $this->dsn( $params ); |
||
| 36 | } |
||
| 37 | |||
| 38 | parent::__construct( $params ); |
||
| 39 | |||
| 40 | $this->stmts = $params['stmt'] ?? []; |
||
| 41 | $this->connect(); |
||
| 42 | } |
||
| 43 | |||
| 44 | |||
| 45 | /** |
||
| 46 | * Closes the connection to the database server |
||
| 47 | * |
||
| 48 | * @return \Aimeos\Base\DB\Connection\Iface Connection instance for method chaining |
||
| 49 | */ |
||
| 50 | public function close() : Iface |
||
| 51 | { |
||
| 52 | if( $this->inTransaction() ) { |
||
| 53 | $this->rollback(); |
||
| 54 | } |
||
| 55 | |||
| 56 | unset( $this->connection ); |
||
| 57 | return $this; |
||
| 58 | } |
||
| 59 | |||
| 60 | |||
| 61 | /** |
||
| 62 | * Connects (or reconnects) to the database server |
||
| 63 | * |
||
| 64 | * @return \Aimeos\Base\DB\Connection\Iface Connection instance for method chaining |
||
| 65 | */ |
||
| 66 | public function connect() : Iface |
||
| 67 | { |
||
| 68 | $param = $this->getParameters(); |
||
| 69 | $param['driverOptions'][\PDO::ATTR_CASE] = \PDO::CASE_NATURAL; |
||
| 70 | $param['driverOptions'][\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_EXCEPTION; |
||
| 71 | $param['driverOptions'][\PDO::ATTR_ORACLE_NULLS] = \PDO::NULL_NATURAL; |
||
| 72 | $param['driverOptions'][\PDO::ATTR_STRINGIFY_FETCHES] = false; |
||
| 73 | |||
| 74 | $pdo = new \PDO( $param['dsn'], $param['username'] ?? '', $param['password'] ?? '', $param['driverOptions'] ); |
||
| 75 | $conn = $this->connection; |
||
| 76 | |||
| 77 | $this->connection = $pdo; |
||
| 78 | $this->txnumber = 0; |
||
| 79 | |||
| 80 | unset( $conn ); |
||
| 81 | |||
| 82 | foreach( $this->stmts as $stmt ) { |
||
| 83 | $this->create( $stmt )->execute()->finish(); |
||
| 84 | } |
||
| 85 | |||
| 86 | return $this; |
||
| 87 | } |
||
| 88 | |||
| 89 | |||
| 90 | /** |
||
| 91 | * Creates a \PDO database statement. |
||
| 92 | * |
||
| 93 | * @param string $sql SQL statement, maybe with place holders |
||
| 94 | * @return \Aimeos\Base\DB\Statement\Iface PDO statement object |
||
| 95 | * @throws \Aimeos\Base\DB\Exception if type is invalid or the \PDO object throws an exception |
||
| 96 | */ |
||
| 97 | public function create( string $sql ) : \Aimeos\Base\DB\Statement\Iface |
||
| 98 | { |
||
| 99 | try |
||
| 100 | { |
||
| 101 | if( strpos( $sql, '?' ) === false ) { |
||
| 102 | return new \Aimeos\Base\DB\Statement\PDO\Simple( $this, $sql ); |
||
| 103 | } |
||
| 104 | |||
| 105 | return new \Aimeos\Base\DB\Statement\PDO\Prepared( $this, $sql ); |
||
| 106 | } |
||
| 107 | catch( \PDOException $e ) |
||
| 108 | { |
||
| 109 | throw new \Aimeos\Base\DB\Exception( $e->getMessage(), $e->getCode(), $e->errorInfo ); |
||
| 110 | } |
||
| 111 | } |
||
| 112 | |||
| 113 | |||
| 114 | /** |
||
| 115 | * Returns the underlying connection object |
||
| 116 | * |
||
| 117 | * @return \PDO Underlying connection object |
||
| 118 | */ |
||
| 119 | public function getRawObject() |
||
| 120 | { |
||
| 121 | return $this->connection; |
||
| 122 | } |
||
| 123 | |||
| 124 | |||
| 125 | /** |
||
| 126 | * Checks if a transaction is currently running |
||
| 127 | * |
||
| 128 | * @return bool True if transaction is currently running, false if not |
||
| 129 | */ |
||
| 130 | public function inTransaction() : bool |
||
| 131 | { |
||
| 132 | return $this->connection->inTransaction(); |
||
|
0 ignored issues
–
show
|
|||
| 133 | } |
||
| 134 | |||
| 135 | |||
| 136 | /** |
||
| 137 | * Starts a transaction for this connection. |
||
| 138 | * |
||
| 139 | * Transactions can't be nested and a new transaction can only be started |
||
| 140 | * if the previous transaction was committed or rolled back before. |
||
| 141 | * |
||
| 142 | * @return \Aimeos\Base\DB\Connection\Iface Connection instance for method chaining |
||
| 143 | */ |
||
| 144 | public function begin() : Iface |
||
| 145 | { |
||
| 146 | if( $this->txnumber === 0 ) |
||
| 147 | { |
||
| 148 | if( $this->connection->beginTransaction() === false ) { |
||
| 149 | throw new \Aimeos\Base\DB\Exception( 'Unable to start new transaction' ); |
||
| 150 | } |
||
| 151 | } |
||
| 152 | |||
| 153 | $this->txnumber++; |
||
| 154 | return $this; |
||
| 155 | } |
||
| 156 | |||
| 157 | |||
| 158 | /** |
||
| 159 | * Commits the changes done inside of the transaction to the storage. |
||
| 160 | * |
||
| 161 | * @return \Aimeos\Base\DB\Connection\Iface Connection instance for method chaining |
||
| 162 | */ |
||
| 163 | public function commit() : Iface |
||
| 164 | { |
||
| 165 | if( $this->txnumber === 1 ) |
||
| 166 | { |
||
| 167 | if( $this->connection->commit() === false ) { |
||
| 168 | throw new \Aimeos\Base\DB\Exception( 'Failed to commit transaction' ); |
||
| 169 | } |
||
| 170 | } |
||
| 171 | |||
| 172 | $this->txnumber--; |
||
| 173 | return $this; |
||
| 174 | } |
||
| 175 | |||
| 176 | |||
| 177 | /** |
||
| 178 | * Discards the changes done inside of the transaction. |
||
| 179 | * |
||
| 180 | * @return \Aimeos\Base\DB\Connection\Iface Connection instance for method chaining |
||
| 181 | */ |
||
| 182 | public function rollback() : Iface |
||
| 183 | { |
||
| 184 | if( $this->txnumber === 1 ) |
||
| 185 | { |
||
| 186 | if( $this->connection->rollBack() === false ) { |
||
| 187 | throw new \Aimeos\Base\DB\Exception( 'Failed to roll back transaction' ); |
||
| 188 | } |
||
| 189 | } |
||
| 190 | |||
| 191 | $this->txnumber--; |
||
| 192 | return $this; |
||
| 193 | } |
||
| 194 | |||
| 195 | |||
| 196 | /** |
||
| 197 | * Returns the connection DSN |
||
| 198 | * |
||
| 199 | * @param array $params Associative list of connection parameters |
||
| 200 | * @return string Connection DSN |
||
| 201 | */ |
||
| 202 | protected function dsn( array $params ) : string |
||
| 203 | { |
||
| 204 | $adapter = $params['adapter'] ?? 'mysql'; |
||
| 205 | $host = $params['host'] ?? null; |
||
| 206 | $port = $params['port'] ?? null; |
||
| 207 | $sock = $params['socket'] ?? null; |
||
| 208 | $dbase = $params['database'] ?? null; |
||
| 209 | |||
| 210 | $dsn = $adapter . ':'; |
||
| 211 | |||
| 212 | if( $adapter === 'sqlsrv' ) |
||
| 213 | { |
||
| 214 | $dsn .= 'Database=' . $dbase; |
||
| 215 | $dsn .= isset( $host ) ? ';Server=' . $host . ( isset( $port ) ? ',' . $port : '' ) : ''; |
||
| 216 | } |
||
| 217 | elseif( $sock == null ) |
||
| 218 | { |
||
| 219 | $dsn .= 'dbname=' . $dbase; |
||
| 220 | $dsn .= isset( $host ) ? ';host=' . $host : ''; |
||
| 221 | $dsn .= isset( $port ) ? ';port=' . $port : ''; |
||
| 222 | } |
||
| 223 | else |
||
| 224 | { |
||
| 225 | $dsn .= 'dbname=' . $dbase . ';unix_socket=' . $sock; |
||
| 226 | } |
||
| 227 | |||
| 228 | return $dsn; |
||
| 229 | } |
||
| 230 | } |
||
| 231 |
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.
This is most likely a typographical error or the method has been renamed.