MediaWiki master
DatabaseFactory.php
Go to the documentation of this file.
1<?php
20namespace Wikimedia\Rdbms;
21
22use InvalidArgumentException;
23use Psr\Log\NullLogger;
24use Throwable;
26use Wikimedia\RequestTimeout\CriticalSectionProvider;
29
38 private $agent;
40 private $deprecationLogger;
45 private $profiler;
47 private $tracer;
49 private $csProvider;
51 private $cliMode;
53 private $debugSql;
54
55 public function __construct( array $params = [] ) {
56 $this->agent = $params['agent'] ?? '';
57 $this->deprecationLogger = $params['deprecationLogger'] ?? static function ( $msg ) {
58 trigger_error( $msg, E_USER_DEPRECATED );
59 };
60 $this->csProvider = $params['criticalSectionProvider'] ?? null;
61 $this->profiler = $params['profiler'] ?? null;
62 $this->tracer = $params['tracer'] ?? new NoopTracer();
63 $this->cliMode = $params['cliMode'] ?? ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
64 $this->debugSql = $params['debugSql'] ?? false;
65 }
66
121 public function create( $type, $params = [], $connect = Database::NEW_CONNECTED ) {
122 $class = $this->getClass( $type, $params['driver'] ?? null );
123
124 if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
125 $params += [
126 // Default configuration
127 'host' => null,
128 'user' => null,
129 'password' => null,
130 'dbname' => null,
131 'schema' => null,
132 'tablePrefix' => '',
133 'variables' => [],
134 'lbInfo' => [],
135 'serverName' => null,
136 'topologyRole' => null,
137 // Objects and callbacks
138 'srvCache' => $params['srvCache'] ?? new HashBagOStuff(),
139 'trxProfiler' => $params['trxProfiler'] ?? new TransactionProfiler(),
140 'logger' => $params['logger'] ?? new NullLogger(),
141 'errorLogger' => $params['errorLogger'] ?? static function ( Throwable $e ) {
142 trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
143 },
144 ];
145
146 $params['flags'] ??= 0;
147 if ( $this->debugSql ) {
148 $params['flags'] |= DBO_DEBUG;
149 }
150
151 $overrides = [
152 'flags' => $this->initConnectionFlags( $params['flags'] ),
153 'cliMode' => $this->cliMode,
154 'agent' => $this->agent,
155 'profiler' => $this->profiler,
156 'deprecationLogger' => $this->deprecationLogger,
157 'criticalSectionProvider' => $this->csProvider,
158 'tracer' => $this->tracer,
159 ];
160
162 $conn = new $class( array_merge( $params, $overrides ) );
163 if ( $connect === Database::NEW_CONNECTED ) {
164 $conn->initConnection();
165 }
166 } else {
167 $conn = null;
168 }
169
170 return $conn;
171 }
172
179 public function attributesFromType( $dbType, $driver = null ) {
180 static $defaults = [
181 Database::ATTR_DB_IS_FILE => false,
182 Database::ATTR_DB_LEVEL_LOCKING => false,
183 Database::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
184 ];
185
186 $class = $this->getClass( $dbType, $driver );
187 if ( class_exists( $class ) ) {
188 return call_user_func( [ $class, 'getAttributes' ] ) + $defaults;
189 } else {
190 throw new DBUnexpectedError( null, "$dbType is not a supported database type." );
191 }
192 }
193
200 protected function getClass( $dbType, $driver = null ) {
201 // For database types with built-in support, the below maps type to IDatabase
202 // implementations. For types with multiple driver implementations (PHP extensions),
203 // an array can be used, keyed by extension name. In case of an array, the
204 // optional 'driver' parameter can be used to force a specific driver. Otherwise,
205 // we auto-detect the first available driver. For types without built-in support,
206 // a class named "Database<Type>" is used, eg. DatabaseFoo for type 'foo'.
207 static $builtinTypes = [
208 'mysql' => [ 'mysqli' => DatabaseMySQL::class ],
209 'sqlite' => DatabaseSqlite::class,
210 'postgres' => DatabasePostgres::class,
211 ];
212
213 $dbType = strtolower( $dbType );
214
215 if ( !isset( $builtinTypes[$dbType] ) ) {
216 // Not a built in type, assume standard naming scheme
217 return 'Database' . ucfirst( $dbType );
218 }
219
220 $class = false;
221 $possibleDrivers = $builtinTypes[$dbType];
222 if ( is_string( $possibleDrivers ) ) {
223 $class = $possibleDrivers;
224 } elseif ( (string)$driver !== '' ) {
225 if ( !isset( $possibleDrivers[$driver] ) ) {
226 throw new InvalidArgumentException( __METHOD__ .
227 " type '$dbType' does not support driver '{$driver}'" );
228 }
229
230 $class = $possibleDrivers[$driver];
231 } else {
232 foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
233 if ( extension_loaded( $posDriver ) ) {
234 $class = $possibleClass;
235 break;
236 }
237 }
238 }
239
240 if ( $class === false ) {
241 throw new InvalidArgumentException( __METHOD__ .
242 " no viable database extension found for type '$dbType'" );
243 }
244
245 return $class;
246 }
247
253 private function initConnectionFlags( int $flags ) {
254 if ( self::fieldHasBit( $flags, IDatabase::DBO_DEFAULT ) ) {
255 // Server is configured to participate in transaction rounds in non-CLI mode
256 if ( $this->cliMode ) {
257 $flags &= ~IDatabase::DBO_TRX;
258 } else {
259 $flags |= IDatabase::DBO_TRX;
260 }
261 }
262 return $flags;
263 }
264
270 private function fieldHasBit( int $flags, int $bit ) {
271 return ( ( $flags & $bit ) === $bit );
272 }
273}
array $params
The job parameters.
Store data in a memory for the current request/process only.
Constructs Database objects.
attributesFromType( $dbType, $driver=null)
getClass( $dbType, $driver=null)
create( $type, $params=[], $connect=Database::NEW_CONNECTED)
Construct a Database subclass instance given a database type and parameters.
Detect high-contention DB queries via profiling calls.
A no-op tracer that creates no-op spans and persists no data.
const DBO_TRX
Automatically start a transaction before running a query if none is active.
const DBO_DEFAULT
Join load balancer transaction rounds (which control DBO_TRX) in non-CLI mode.
Interface to a relational database.
Definition IDatabase.php:45
Base interface for an OpenTelemetry tracer responsible for creating spans.
const DBO_DEBUG
Definition defines.php:9
const DBO_TRX
Definition defines.php:12