MediaWiki master
DatabaseFactory.php
Go to the documentation of this file.
1<?php
20namespace Wikimedia\Rdbms;
21
23use InvalidArgumentException;
24use Psr\Log\NullLogger;
25use 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 // Participate in transaction rounds if $server does not specify otherwise
147 'flags' => $this->initConnFlags( $params['flags'] ),
148 'cliMode' => $this->cliMode,
149 'agent' => $this->agent,
150 'profiler' => $this->profiler,
151 'deprecationLogger' => $this->deprecationLogger,
152 'criticalSectionProvider' => $this->csProvider,
153 ];
154
156 $conn = new $class( array_merge( $params, $overrides ) );
157 if ( $connect === Database::NEW_CONNECTED ) {
158 $conn->initConnection();
159 }
160 } else {
161 $conn = null;
162 }
163
164 return $conn;
165 }
166
173 public function attributesFromType( $dbType, $driver = null ) {
174 static $defaults = [
175 Database::ATTR_DB_IS_FILE => false,
176 Database::ATTR_DB_LEVEL_LOCKING => false,
177 Database::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
178 ];
179
180 $class = $this->getClass( $dbType, $driver );
181 if ( class_exists( $class ) ) {
182 return call_user_func( [ $class, 'getAttributes' ] ) + $defaults;
183 } else {
184 throw new DBUnexpectedError( null, "$dbType is not a supported database type." );
185 }
186 }
187
194 protected function getClass( $dbType, $driver = null ) {
195 // For database types with built-in support, the below maps type to IDatabase
196 // implementations. For types with multiple driver implementations (PHP extensions),
197 // an array can be used, keyed by extension name. In case of an array, the
198 // optional 'driver' parameter can be used to force a specific driver. Otherwise,
199 // we auto-detect the first available driver. For types without built-in support,
200 // a class named "Database<Type>" is used, eg. DatabaseFoo for type 'foo'.
201 static $builtinTypes = [
202 'mysql' => [ 'mysqli' => DatabaseMySQL::class ],
203 'sqlite' => DatabaseSqlite::class,
204 'postgres' => DatabasePostgres::class,
205 ];
206
207 $dbType = strtolower( $dbType );
208
209 if ( !isset( $builtinTypes[$dbType] ) ) {
210 // Not a built in type, assume standard naming scheme
211 return 'Database' . ucfirst( $dbType );
212 }
213
214 $class = false;
215 $possibleDrivers = $builtinTypes[$dbType];
216 if ( is_string( $possibleDrivers ) ) {
217 $class = $possibleDrivers;
218 } elseif ( (string)$driver !== '' ) {
219 if ( !isset( $possibleDrivers[$driver] ) ) {
220 throw new InvalidArgumentException( __METHOD__ .
221 " type '$dbType' does not support driver '{$driver}'" );
222 }
223
224 $class = $possibleDrivers[$driver];
225 } else {
226 foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
227 if ( extension_loaded( $posDriver ) ) {
228 $class = $possibleClass;
229 break;
230 }
231 }
232 }
233
234 if ( $class === false ) {
235 throw new InvalidArgumentException( __METHOD__ .
236 " no viable database extension found for type '$dbType'" );
237 }
238
239 return $class;
240 }
241
247 private function initConnFlags( $flags ) {
248 if ( self::fieldHasBit( $flags, IDatabase::DBO_DEFAULT ) ) {
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}
Simple store for keeping values in an associative array for the current process.
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.
Basic database interface for live and lazy-loaded relation database handles.
Definition IDatabase.php:36
const DBO_DEBUG
Definition defines.php:9
const DBO_TRX
Definition defines.php:12