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;
27
36 private $agent;
38 private $deprecationLogger;
43 private $profiler;
45 private $csProvider;
47 private $cliMode;
49 private $debugSql;
50
51 public function __construct( array $params = [] ) {
52 $this->agent = $params['agent'] ?? '';
53 $this->deprecationLogger = $params['deprecationLogger'] ?? static function ( $msg ) {
54 trigger_error( $msg, E_USER_DEPRECATED );
55 };
56 $this->csProvider = $params['criticalSectionProvider'] ?? null;
57 $this->profiler = $params['profiler'] ?? null;
58 $this->cliMode = $params['cliMode'] ?? ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
59 $this->debugSql = $params['debugSql'] ?? false;
60 }
61
115 public function create( $type, $params = [], $connect = Database::NEW_CONNECTED ) {
116 $class = $this->getClass( $type, $params['driver'] ?? null );
117
118 if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
119 $params += [
120 // Default configuration
121 'host' => null,
122 'user' => null,
123 'password' => null,
124 'dbname' => null,
125 'schema' => null,
126 'tablePrefix' => '',
127 'variables' => [],
128 'lbInfo' => [],
129 'serverName' => null,
130 'topologyRole' => null,
131 // Objects and callbacks
132 'srvCache' => $params['srvCache'] ?? new HashBagOStuff(),
133 'trxProfiler' => $params['trxProfiler'] ?? new TransactionProfiler(),
134 'logger' => $params['logger'] ?? new NullLogger(),
135 'errorLogger' => $params['errorLogger'] ?? static function ( Throwable $e ) {
136 trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
137 },
138 ];
139
140 $params['flags'] ??= 0;
141 if ( $this->debugSql ) {
142 $params['flags'] |= DBO_DEBUG;
143 }
144
145 $overrides = [
146 'flags' => $this->initConnectionFlags( $params['flags'] ),
147 'cliMode' => $this->cliMode,
148 'agent' => $this->agent,
149 'profiler' => $this->profiler,
150 'deprecationLogger' => $this->deprecationLogger,
151 'criticalSectionProvider' => $this->csProvider,
152 ];
153
155 $conn = new $class( array_merge( $params, $overrides ) );
156 if ( $connect === Database::NEW_CONNECTED ) {
157 $conn->initConnection();
158 }
159 } else {
160 $conn = null;
161 }
162
163 return $conn;
164 }
165
172 public function attributesFromType( $dbType, $driver = null ) {
173 static $defaults = [
174 Database::ATTR_DB_IS_FILE => false,
175 Database::ATTR_DB_LEVEL_LOCKING => false,
176 Database::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
177 ];
178
179 $class = $this->getClass( $dbType, $driver );
180 if ( class_exists( $class ) ) {
181 return call_user_func( [ $class, 'getAttributes' ] ) + $defaults;
182 } else {
183 throw new DBUnexpectedError( null, "$dbType is not a supported database type." );
184 }
185 }
186
193 protected function getClass( $dbType, $driver = null ) {
194 // For database types with built-in support, the below maps type to IDatabase
195 // implementations. For types with multiple driver implementations (PHP extensions),
196 // an array can be used, keyed by extension name. In case of an array, the
197 // optional 'driver' parameter can be used to force a specific driver. Otherwise,
198 // we auto-detect the first available driver. For types without built-in support,
199 // a class named "Database<Type>" is used, eg. DatabaseFoo for type 'foo'.
200 static $builtinTypes = [
201 'mysql' => [ 'mysqli' => DatabaseMySQL::class ],
202 'sqlite' => DatabaseSqlite::class,
203 'postgres' => DatabasePostgres::class,
204 ];
205
206 $dbType = strtolower( $dbType );
207
208 if ( !isset( $builtinTypes[$dbType] ) ) {
209 // Not a built in type, assume standard naming scheme
210 return 'Database' . ucfirst( $dbType );
211 }
212
213 $class = false;
214 $possibleDrivers = $builtinTypes[$dbType];
215 if ( is_string( $possibleDrivers ) ) {
216 $class = $possibleDrivers;
217 } elseif ( (string)$driver !== '' ) {
218 if ( !isset( $possibleDrivers[$driver] ) ) {
219 throw new InvalidArgumentException( __METHOD__ .
220 " type '$dbType' does not support driver '{$driver}'" );
221 }
222
223 $class = $possibleDrivers[$driver];
224 } else {
225 foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
226 if ( extension_loaded( $posDriver ) ) {
227 $class = $possibleClass;
228 break;
229 }
230 }
231 }
232
233 if ( $class === false ) {
234 throw new InvalidArgumentException( __METHOD__ .
235 " no viable database extension found for type '$dbType'" );
236 }
237
238 return $class;
239 }
240
246 private function initConnectionFlags( int $flags ) {
247 if ( self::fieldHasBit( $flags, IDatabase::DBO_DEFAULT ) ) {
248 // Server is configured to participate in transaction rounds in non-CLI mode
249 if ( $this->cliMode ) {
250 $flags &= ~IDatabase::DBO_TRX;
251 } else {
252 $flags |= IDatabase::DBO_TRX;
253 }
254 }
255 return $flags;
256 }
257
263 private function fieldHasBit( int $flags, int $bit ) {
264 return ( ( $flags & $bit ) === $bit );
265 }
266}
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.
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:48
const DBO_DEBUG
Definition defines.php:9
const DBO_TRX
Definition defines.php:12