MediaWiki  master
DatabaseDomain.php
Go to the documentation of this file.
1 <?php
20 namespace Wikimedia\Rdbms;
21 
22 use InvalidArgumentException;
23 
43  private $database;
45  private $schema;
47  private $prefix;
48 
50  private $equivalentString;
51 
57  public function __construct( $database, $schema, $prefix ) {
58  if ( $database !== null && ( !is_string( $database ) || $database === '' ) ) {
59  throw new InvalidArgumentException( 'Database must be null or a non-empty string.' );
60  }
61  $this->database = $database;
62  if ( $schema !== null && ( !is_string( $schema ) || $schema === '' ) ) {
63  throw new InvalidArgumentException( 'Schema must be null or a non-empty string.' );
64  } elseif ( $database === null && $schema !== null ) {
65  throw new InvalidArgumentException( 'Schema must be null if database is null.' );
66  }
67  $this->schema = $schema;
68  if ( !is_string( $prefix ) ) {
69  throw new InvalidArgumentException( "Prefix must be a string." );
70  }
71  $this->prefix = $prefix;
72  }
73 
78  public static function newFromId( $domain ): self {
79  if ( $domain instanceof self ) {
80  return $domain;
81  }
82 
83  if ( !is_string( $domain ) ) {
84  throw new InvalidArgumentException( "Domain must be a string or " . __CLASS__ );
85  }
86 
87  $parts = array_map( [ __CLASS__, 'decode' ], explode( '-', $domain ) );
88 
89  $schema = null;
90  $prefix = '';
91 
92  if ( count( $parts ) == 1 ) {
93  $database = $parts[0];
94  } elseif ( count( $parts ) == 2 ) {
95  list( $database, $prefix ) = $parts;
96  } elseif ( count( $parts ) == 3 ) {
97  list( $database, $schema, $prefix ) = $parts;
98  } else {
99  throw new InvalidArgumentException( "Domain '$domain' has too few or too many parts." );
100  }
101 
102  if ( $database === '' ) {
103  $database = null;
104  }
105 
106  if ( $schema === '' ) {
107  $schema = null;
108  }
109 
110  $instance = new self( $database, $schema, $prefix );
111  $instance->equivalentString = $domain;
112 
113  return $instance;
114  }
115 
119  public static function newUnspecified() {
120  return new self( null, null, '' );
121  }
122 
127  public function equals( $other ) {
128  if ( $other instanceof self ) {
129  return (
130  $this->database === $other->database &&
131  $this->schema === $other->schema &&
132  $this->prefix === $other->prefix
133  );
134  }
135 
136  return ( $this->getId() === $other );
137  }
138 
153  public function isCompatible( $other ) {
154  if ( $this->isUnspecified() ) {
155  return true; // even the prefix doesn't matter
156  }
157 
158  $other = self::newFromId( $other );
159 
160  return (
161  ( $this->database === $other->database || $this->database === null ) &&
162  ( $this->schema === $other->schema || $this->schema === null ) &&
163  $this->prefix === $other->prefix
164  );
165  }
166 
171  public function isUnspecified() {
172  return (
173  $this->database === null && $this->schema === null && $this->prefix === ''
174  );
175  }
176 
180  public function getDatabase() {
181  return $this->database;
182  }
183 
187  public function getSchema() {
188  return $this->schema;
189  }
190 
194  public function getTablePrefix() {
195  return $this->prefix;
196  }
197 
201  public function getId(): string {
202  if ( $this->equivalentString === null ) {
203  $this->equivalentString = $this->convertToString();
204  }
205 
206  return $this->equivalentString;
207  }
208 
212  private function convertToString(): string {
213  $parts = [ (string)$this->database ];
214  if ( $this->schema !== null ) {
215  $parts[] = $this->schema;
216  }
217  if ( $this->prefix != '' || $this->schema !== null ) {
218  // If there is a schema, then we need the prefix to disambiguate.
219  // For engines like Postgres that use schemas, this awkwardness is hopefully
220  // avoided since it is easy to have one DB per server (to avoid having many users)
221  // and use schema/prefix to have wiki farms. For example, a domain schemes could be
222  // wiki-<project>-<language>, e.g. "wiki-fitness-es"/"wiki-sports-fr"/"wiki-news-en".
223  $parts[] = $this->prefix;
224  }
225 
226  return implode( '-', array_map( [ __CLASS__, 'encode' ], $parts ) );
227  }
228 
229  private static function encode( $decoded ) {
230  $encoded = '';
231 
232  $length = strlen( $decoded );
233  for ( $i = 0; $i < $length; ++$i ) {
234  $char = $decoded[$i];
235  if ( $char === '-' ) {
236  $encoded .= '?h';
237  } elseif ( $char === '?' ) {
238  $encoded .= '??';
239  } else {
240  $encoded .= $char;
241  }
242  }
243 
244  return $encoded;
245  }
246 
247  private static function decode( $encoded ) {
248  $decoded = '';
249 
250  $length = strlen( $encoded );
251  for ( $i = 0; $i < $length; ++$i ) {
252  $char = $encoded[$i];
253  if ( $char === '?' ) {
254  $nextChar = $encoded[$i + 1] ?? null;
255  if ( $nextChar === 'h' ) {
256  $decoded .= '-';
257  ++$i;
258  } elseif ( $nextChar === '?' ) {
259  $decoded .= '?';
260  ++$i;
261  } else {
262  $decoded .= $char;
263  }
264  } else {
265  $decoded .= $char;
266  }
267  }
268 
269  return $decoded;
270  }
271 
275  public function __toString() {
276  return $this->getId();
277  }
278 }
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Definition: WebStart.php:82
Class to handle database/schema/prefix specifications for IDatabase.
isCompatible( $other)
Check whether the domain $other meets the specifications of this domain.
__construct( $database, $schema, $prefix)