MediaWiki master
DatabaseFactory.php
Go to the documentation of this file.
1<?php
6namespace Wikimedia\Rdbms;
7
8use InvalidArgumentException;
9use Psr\Log\NullLogger;
10use Throwable;
12use Wikimedia\RequestTimeout\CriticalSectionProvider;
15
24 private $agent;
26 private $deprecationLogger;
31 private $profiler;
33 private $tracer;
35 private $csProvider;
37 private $cliMode;
39 private $debugSql;
40
41 public function __construct( array $params = [] ) {
42 $this->agent = $params['agent'] ?? '';
43 $this->deprecationLogger = $params['deprecationLogger'] ?? static function ( $msg ) {
44 trigger_error( $msg, E_USER_DEPRECATED );
45 };
46 $this->csProvider = $params['criticalSectionProvider'] ?? null;
47 $this->profiler = $params['profiler'] ?? null;
48 $this->tracer = $params['tracer'] ?? new NoopTracer();
49 $this->cliMode = $params['cliMode'] ?? ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' );
50 $this->debugSql = $params['debugSql'] ?? false;
51 }
52
107 public function create( $type, $params = [], $connect = Database::NEW_CONNECTED ) {
108 $class = $this->getClass( $type, $params['driver'] ?? null );
109
110 if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
111 $params += [
112 // Default configuration
113 'host' => null,
114 'user' => null,
115 'password' => null,
116 'dbname' => null,
117 'schema' => null,
118 'tablePrefix' => '',
119 'variables' => [],
120 'lbInfo' => [],
121 'serverName' => null,
122 'topologyRole' => null,
123 // Objects and callbacks
124 'srvCache' => $params['srvCache'] ?? new HashBagOStuff(),
125 'trxProfiler' => $params['trxProfiler'] ?? new TransactionProfiler(),
126 'logger' => $params['logger'] ?? new NullLogger(),
127 'errorLogger' => $params['errorLogger'] ?? static function ( Throwable $e ) {
128 trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
129 },
130 ];
131
132 $params['flags'] ??= 0;
133 if ( $this->debugSql ) {
134 $params['flags'] |= DBO_DEBUG;
135 }
136
137 $overrides = [
138 'flags' => $this->initConnectionFlags( $params['flags'] ),
139 'cliMode' => $this->cliMode,
140 'agent' => $this->agent,
141 'profiler' => $this->profiler,
142 'deprecationLogger' => $this->deprecationLogger,
143 'criticalSectionProvider' => $this->csProvider,
144 'tracer' => $this->tracer,
145 ];
146
148 $conn = new $class( array_merge( $params, $overrides ) );
149 if ( $connect === Database::NEW_CONNECTED ) {
150 $conn->initConnection();
151 }
152 } else {
153 $conn = null;
154 }
155
156 return $conn;
157 }
158
165 public function attributesFromType( $dbType, $driver = null ) {
166 static $defaults = [
167 Database::ATTR_DB_IS_FILE => false,
168 Database::ATTR_DB_LEVEL_LOCKING => false,
169 Database::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
170 ];
171
172 $class = $this->getClass( $dbType, $driver );
173 if ( class_exists( $class ) ) {
174 return $class::getAttributes() + $defaults;
175 } else {
176 throw new DBUnexpectedError( null, "$dbType is not a supported database type." );
177 }
178 }
179
186 protected function getClass( $dbType, $driver = null ) {
187 // For database types with built-in support, the below maps type to IDatabase
188 // implementations. For types with multiple driver implementations (PHP extensions),
189 // an array can be used, keyed by extension name. In case of an array, the
190 // optional 'driver' parameter can be used to force a specific driver. Otherwise,
191 // we auto-detect the first available driver. For types without built-in support,
192 // a class named "Database<Type>" is used, eg. DatabaseFoo for type 'foo'.
193 static $builtinTypes = [
194 'mysql' => [ 'mysqli' => DatabaseMySQL::class ],
195 'sqlite' => DatabaseSqlite::class,
196 'postgres' => DatabasePostgres::class,
197 ];
198
199 $dbType = strtolower( $dbType );
200
201 if ( !isset( $builtinTypes[$dbType] ) ) {
202 // Not a built in type, assume standard naming scheme
203 return 'Database' . ucfirst( $dbType );
204 }
205
206 $class = false;
207 $possibleDrivers = $builtinTypes[$dbType];
208 if ( is_string( $possibleDrivers ) ) {
209 $class = $possibleDrivers;
210 } elseif ( (string)$driver !== '' ) {
211 if ( !isset( $possibleDrivers[$driver] ) ) {
212 throw new InvalidArgumentException( __METHOD__ .
213 " type '$dbType' does not support driver '{$driver}'" );
214 }
215
216 $class = $possibleDrivers[$driver];
217 } else {
218 foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
219 if ( extension_loaded( $posDriver ) ) {
220 $class = $possibleClass;
221 break;
222 }
223 }
224 }
225
226 if ( $class === false ) {
227 throw new InvalidArgumentException( __METHOD__ .
228 " no viable database extension found for type '$dbType'" );
229 }
230
231 return $class;
232 }
233
239 private function initConnectionFlags( int $flags ) {
240 if ( self::fieldHasBit( $flags, IDatabase::DBO_DEFAULT ) ) {
241 // Server is configured to participate in transaction rounds in non-CLI mode
242 if ( $this->cliMode ) {
243 $flags &= ~IDatabase::DBO_TRX;
244 } else {
245 $flags |= IDatabase::DBO_TRX;
246 }
247 }
248 return $flags;
249 }
250
256 private function fieldHasBit( int $flags, int $bit ) {
257 return ( ( $flags & $bit ) === $bit );
258 }
259}
const DBO_DEBUG
Definition defines.php:9
const DBO_TRX
Definition defines.php:12
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:31
Base interface for an OpenTelemetry tracer responsible for creating spans.