MediaWiki  master
DatabaseDomain.php
Go to the documentation of this file.
1 <?php
21 namespace Wikimedia\Rdbms;
22 
24 
42  private $database;
44  private $schema;
46  private $prefix;
47 
50 
56  public function __construct( $database, $schema, $prefix ) {
57  if ( $database !== null && ( !is_string( $database ) || $database === '' ) ) {
58  throw new InvalidArgumentException( 'Database must be null or a non-empty string.' );
59  }
60  $this->database = $database;
61  if ( $schema !== null && ( !is_string( $schema ) || $schema === '' ) ) {
62  throw new InvalidArgumentException( 'Schema must be null or a non-empty string.' );
63  } elseif ( $database === null && $schema !== null ) {
64  throw new InvalidArgumentException( 'Schema must be null if database is null.' );
65  }
66  $this->schema = $schema;
67  if ( !is_string( $prefix ) ) {
68  throw new InvalidArgumentException( "Prefix must be a string." );
69  }
70  $this->prefix = $prefix;
71  }
72 
77  public static function newFromId( $domain ) {
78  if ( $domain instanceof self ) {
79  return $domain;
80  }
81 
82  $parts = array_map( [ __CLASS__, 'decode' ], explode( '-', $domain ) );
83 
84  $schema = null;
85  $prefix = '';
86 
87  if ( count( $parts ) == 1 ) {
88  $database = $parts[0];
89  } elseif ( count( $parts ) == 2 ) {
90  list( $database, $prefix ) = $parts;
91  } elseif ( count( $parts ) == 3 ) {
92  list( $database, $schema, $prefix ) = $parts;
93  } else {
94  throw new InvalidArgumentException( "Domain '$domain' has too few or too many parts." );
95  }
96 
97  if ( $database === '' ) {
98  $database = null;
99  }
100 
101  if ( $schema === '' ) {
102  $schema = null;
103  }
104 
105  $instance = new self( $database, $schema, $prefix );
106  $instance->equivalentString = (string)$domain;
107 
108  return $instance;
109  }
110 
114  public static function newUnspecified() {
115  return new self( null, null, '' );
116  }
117 
122  public function equals( $other ) {
123  if ( $other instanceof self ) {
124  return (
125  $this->database === $other->database &&
126  $this->schema === $other->schema &&
127  $this->prefix === $other->prefix
128  );
129  }
130 
131  return ( $this->getId() === $other );
132  }
133 
148  public function isCompatible( $other ) {
149  if ( $this->isUnspecified() ) {
150  return true; // even the prefix doesn't matter
151  }
152 
153  $other = self::newFromId( $other );
154 
155  return (
156  ( $this->database === $other->database || $this->database === null ) &&
157  ( $this->schema === $other->schema || $this->schema === null ) &&
158  $this->prefix === $other->prefix
159  );
160  }
161 
166  public function isUnspecified() {
167  return (
168  $this->database === null && $this->schema === null && $this->prefix === ''
169  );
170  }
171 
175  public function getDatabase() {
176  return $this->database;
177  }
178 
182  public function getSchema() {
183  return $this->schema;
184  }
185 
189  public function getTablePrefix() {
190  return $this->prefix;
191  }
192 
196  public function getId() {
197  if ( $this->equivalentString === null ) {
198  $this->equivalentString = $this->convertToString();
199  }
200 
202  }
203 
207  private function convertToString() {
208  $parts = [ (string)$this->database ];
209  if ( $this->schema !== null ) {
210  $parts[] = $this->schema;
211  }
212  if ( $this->prefix != '' || $this->schema !== null ) {
213  // If there is a schema, then we need the prefix to disambiguate.
214  // For engines like Postgres that use schemas, this awkwardness is hopefully
215  // avoided since it is easy to have one DB per server (to avoid having many users)
216  // and use schema/prefix to have wiki farms. For example, a domain schemes could be
217  // wiki-<project>-<language>, e.g. "wiki-fitness-es"/"wiki-sports-fr"/"wiki-news-en".
218  $parts[] = $this->prefix;
219  }
220 
221  return implode( '-', array_map( [ __CLASS__, 'encode' ], $parts ) );
222  }
223 
224  private static function encode( $decoded ) {
225  $encoded = '';
226 
227  $length = strlen( $decoded );
228  for ( $i = 0; $i < $length; ++$i ) {
229  $char = $decoded[$i];
230  if ( $char === '-' ) {
231  $encoded .= '?h';
232  } elseif ( $char === '?' ) {
233  $encoded .= '??';
234  } else {
235  $encoded .= $char;
236  }
237  }
238 
239  return $encoded;
240  }
241 
242  private static function decode( $encoded ) {
243  $decoded = '';
244 
245  $length = strlen( $encoded );
246  for ( $i = 0; $i < $length; ++$i ) {
247  $char = $encoded[$i];
248  if ( $char === '?' ) {
249  $nextChar = $encoded[$i + 1] ?? null;
250  if ( $nextChar === 'h' ) {
251  $decoded .= '-';
252  ++$i;
253  } elseif ( $nextChar === '?' ) {
254  $decoded .= '?';
255  ++$i;
256  } else {
257  $decoded .= $char;
258  }
259  } else {
260  $decoded .= $char;
261  }
262  }
263 
264  return $decoded;
265  }
266 
270  function __toString() {
271  return $this->getId();
272  }
273 }
string $equivalentString
Cache of convertToString()
isCompatible( $other)
Check whether the domain $other meets the specifications of this domain.
__construct( $database, $schema, $prefix)
Class to handle database/schema/prefix specifications for IDatabase.