MediaWiki  master
DatabaseFactory.php
Go to the documentation of this file.
1 <?php
20 namespace Wikimedia\Rdbms;
21 
22 use HashBagOStuff;
23 use InvalidArgumentException;
24 use Psr\Log\NullLogger;
25 use Throwable;
26 use 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 
113  public function create( $type, $params = [], $connect = Database::NEW_CONNECTED ) {
114  $class = $this->getClass( $type, $params['driver'] ?? null );
115 
116  if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
117  $params += [
118  // Default configuration
119  'host' => null,
120  'user' => null,
121  'password' => null,
122  'dbname' => null,
123  'schema' => null,
124  'tablePrefix' => '',
125  'variables' => [],
126  'lbInfo' => [],
127  'serverName' => null,
128  'topologyRole' => null,
129  // Objects and callbacks
130  'srvCache' => $params['srvCache'] ?? new HashBagOStuff(),
131  'trxProfiler' => $params['trxProfiler'] ?? new TransactionProfiler(),
132  'logger' => $params['logger'] ?? new NullLogger(),
133  'errorLogger' => $params['errorLogger'] ?? static function ( Throwable $e ) {
134  trigger_error( get_class( $e ) . ': ' . $e->getMessage(), E_USER_WARNING );
135  },
136  ];
137 
138  $params['flags'] ??= 0;
139  if ( $this->debugSql ) {
140  $params['flags'] |= DBO_DEBUG;
141  }
142 
143  $overrides = [
144  // Participate in transaction rounds if $server does not specify otherwise
145  'flags' => $this->initConnFlags( $params['flags'] ),
146  'cliMode' => $this->cliMode,
147  'agent' => $this->agent,
148  'profiler' => $this->profiler,
149  'deprecationLogger' => $this->deprecationLogger,
150  'criticalSectionProvider' => $this->csProvider,
151  ];
152 
154  $conn = new $class( array_merge( $params, $overrides ) );
155  if ( $connect === Database::NEW_CONNECTED ) {
156  $conn->initConnection();
157  }
158  } else {
159  $conn = null;
160  }
161 
162  return $conn;
163  }
164 
171  public function attributesFromType( $dbType, $driver = null ) {
172  static $defaults = [
173  Database::ATTR_DB_IS_FILE => false,
174  Database::ATTR_DB_LEVEL_LOCKING => false,
175  Database::ATTR_SCHEMAS_AS_TABLE_GROUPS => false
176  ];
177 
178  $class = $this->getClass( $dbType, $driver );
179  if ( class_exists( $class ) ) {
180  return call_user_func( [ $class, 'getAttributes' ] ) + $defaults;
181  } else {
182  throw new DBUnexpectedError( null, "$dbType is not a supported database type." );
183  }
184  }
185 
192  protected function getClass( $dbType, $driver = null ) {
193  // For database types with built-in support, the below maps type to IDatabase
194  // implementations. For types with multiple driver implementations (PHP extensions),
195  // an array can be used, keyed by extension name. In case of an array, the
196  // optional 'driver' parameter can be used to force a specific driver. Otherwise,
197  // we auto-detect the first available driver. For types without built-in support,
198  // a class named "Database<Type>" is used, eg. DatabaseFoo for type 'foo'.
199  static $builtinTypes = [
200  'mysql' => [ 'mysqli' => DatabaseMySQL::class ],
201  'sqlite' => DatabaseSqlite::class,
202  'postgres' => DatabasePostgres::class,
203  ];
204 
205  $dbType = strtolower( $dbType );
206 
207  if ( !isset( $builtinTypes[$dbType] ) ) {
208  // Not a built in type, assume standard naming scheme
209  return 'Database' . ucfirst( $dbType );
210  }
211 
212  $class = false;
213  $possibleDrivers = $builtinTypes[$dbType];
214  if ( is_string( $possibleDrivers ) ) {
215  $class = $possibleDrivers;
216  } elseif ( (string)$driver !== '' ) {
217  if ( !isset( $possibleDrivers[$driver] ) ) {
218  throw new InvalidArgumentException( __METHOD__ .
219  " type '$dbType' does not support driver '{$driver}'" );
220  }
221 
222  $class = $possibleDrivers[$driver];
223  } else {
224  foreach ( $possibleDrivers as $posDriver => $possibleClass ) {
225  if ( extension_loaded( $posDriver ) ) {
226  $class = $possibleClass;
227  break;
228  }
229  }
230  }
231 
232  if ( $class === false ) {
233  throw new InvalidArgumentException( __METHOD__ .
234  " no viable database extension found for type '$dbType'" );
235  }
236 
237  return $class;
238  }
239 
245  private function initConnFlags( $flags ) {
246  if ( self::fieldHasBit( $flags, IDatabase::DBO_DEFAULT ) ) {
247  if ( $this->cliMode ) {
248  $flags &= ~IDatabase::DBO_TRX;
249  } else {
250  $flags |= IDatabase::DBO_TRX;
251  }
252  }
253  return $flags;
254  }
255 
261  private function fieldHasBit( int $flags, int $bit ) {
262  return ( ( $flags & $bit ) === $bit );
263  }
264 }
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