Hoa central
Locale.php
Go to the documentation of this file.
1 <?php
2 
37 namespace Hoa\Locale;
38 
39 use Hoa\Core;
40 
49 class Locale
50 {
56  const TYPE_LANGTAG = 0;
57 
63  const TYPE_PRIVATEUSE = 1;
64 
70  const TYPE_GRANDFATHERED = 2;
71 
77  protected static $_default = null;
78 
84  protected $_localizer = null;
85 
91  protected $_type = 0;
92 
98  protected $_language = null;
99 
105  protected $_script = null;
106 
112  protected $_region = null;
113 
119  protected $_variant = null;
120 
126  protected $_extension = null;
127 
133  protected $_privateuse = null;
134 
140  protected $_grandfathered = null;
141 
142 
143 
150  public function __construct($localizer = null)
151  {
152  if (!is_object($localizer)) {
153  $localizer = new Localizer\Coerce($localizer);
154  }
155 
156  $this->setLocalizer($localizer);
157 
158  return;
159  }
160 
167  public static function setDefault($locale)
168  {
169  $old = static::$_default;
170  static::$_default = $locale;
171 
172  return $old;
173  }
174 
180  public static function getDefault()
181  {
182  return static::$_default;
183  }
184 
191  public function setLocalizer(Localizer $localizer)
192  {
193  $this->reset();
194 
195  $old = $this->_localizer;
196  $this->_localizer = $localizer;
197 
198  $this->computeLocale();
199 
200  return $old;
201  }
202 
208  public function getLocalizer()
209  {
210  return $this->_localizer;
211  }
212 
219  protected function computeLocale()
220  {
221  $locale = $this->getLocalizer()->getLocale() ?: static::getDefault();
222 
223  if (empty($locale)) {
224  throw new Exception('No locale was found.', 0);
225  }
226 
227  $parsed = static::parse($locale);
228 
229  if (false === $parsed) {
230  throw new Exception('Locale %s is not well-formed.', 1, $locale);
231  }
232 
233  if (isset($parsed['grandfathered'])) {
234  $this->_type = static::TYPE_GRANDFATHERED;
235  $this->_grandfathered = $parsed['grandfathered'];
236  } elseif (isset($parsed['privateuse'])) {
237  $this->_type = static::TYPE_PRIVATEUSE;
238  $this->_privateuse = $parsed['privateuse'];
239  } else {
240  $this->_type = static::TYPE_LANGTAG;
241  list(
242  $this->_language,
243  $this->_script,
244  $this->_region,
245  $this->_variant,
246  $this->_extension,
247  $this->_privateuse
248  ) = array_values($parsed['langtag']);
249  }
250 
251  return;
252  }
253 
261  public static function parse($locale)
262  {
263 
264  // RFC4646
265  $match = preg_match(
266  '#^
267  (
268  (?<r_langtag>
269  (?<language>[a-z]{2,3})
270  (?<script>\-[a-z]{4})?
271  (?<region>\-(?:[a-z]{2}|[0-9]{4}))?
272  (?<variant>(?:\-(?:[a-z]{2}|[0-9]{3}))+)?
273  (?<extension>(?:\-(?:[a-wy-z]|\d)\-[a-z0-9]{2,8})+)?
274  (?<privateuse>\-x\-[a-z0-9]{1,8})?
275  )
276  | (?<r_privateuse>x\-[a-z0-9]{1,8})
277  | (?<r_grandfathered>[a-z]{1,3}(\-[a-z0-9]{2,8}){1,2})
278  )
279  $#ix',
280  $locale,
281  $matches
282  );
283 
284  if (0 === $match) {
285  return false;
286  }
287 
288  if (isset($matches['r_grandfathered'])) {
289  return [
290  'grandfathered' => $matches['r_grandfathered']
291  ];
292  }
293 
294  if (isset($matches['r_privateuse'])) {
295  return [
296  'privateuse' => substr($matches['r_privateuse'], 2)
297  ];
298  }
299 
300  $out = [
301  'language' => $matches['language'],
302  'script' => null,
303  'region' => null,
304  'variant' => [],
305  'extension' => [],
306  'privateuse' => null
307  ];
308 
309  if (!empty($matches['script'])) {
310  $out['script'] = substr($matches['script'], 1);
311  }
312 
313  if (!empty($matches['region'])) {
314  $out['region'] = substr($matches['region'], 1);
315  }
316 
317  if (!empty($matches['variant'])) {
318  $out['variant'] = explode('-', substr($matches['variant'], 1));
319  }
320 
321  if (!empty($matches['extension'])) {
322  $handle = preg_split(
323  '/-(?=.-)/',
324  $matches['extension'],
325  -1,
326  PREG_SPLIT_NO_EMPTY
327  );
328 
329  foreach ($handle as $value) {
330  list($extensionName, $extensionValue) = explode('-', $value);
331  $out['extension'][$extensionName] = $extensionValue;
332  }
333  }
334 
335  if (!empty($matches['privateuse'])) {
336  $out['privateuse'] = substr($matches['privateuse'], 3);
337  }
338 
339  return ['langtag' => $out];
340  }
341 
347  public function getType()
348  {
349  return $this->_type;
350  }
351 
357  public function getLanguage()
358  {
359  return $this->_language;
360  }
361 
367  public function getScript()
368  {
369  return $this->_script;
370  }
371 
377  public function getRegion()
378  {
379  return $this->_region;
380  }
381 
387  public function getVariants()
388  {
389  return $this->_variant;
390  }
391 
397  public function getExtensions()
398  {
399  return $this->_extension;
400  }
401 
407  public function getPrivateUse()
408  {
409  return $this->_privateuse;
410  }
411 
417  public function getGrandfathered()
418  {
419  return $this->_grandfathered;
420  }
421 
427  protected function reset()
428  {
429  $class = new \ReflectionClass(get_class($this));
430  $object = new \ReflectionObject($this);
431  $defaultProperties = $class->getDefaultProperties();
432  $properties = $object->getProperties();
433 
434  foreach ($properties as $property) {
435  $name = $property->getName();
436 
437  if ('_default' === $name) {
438  continue;
439  }
440 
441  $property->setAccessible(true);
442  $property->setValue(
443  $this,
444  array_key_exists($name, $defaultProperties)
445  ? $defaultProperties[$name]
446  : null
447  );
448  }
449 
450  return;
451  }
452 }
453 
457 Core\Consistency::flexEntity('Hoa\Locale\Locale');
const TYPE_PRIVATEUSE
Definition: Locale.php:63
static getDefault()
Definition: Locale.php:180
const TYPE_GRANDFATHERED
Definition: Locale.php:70
const TYPE_LANGTAG
Definition: Locale.php:56
static parse($locale)
Definition: Locale.php:261
static $_default
Definition: Locale.php:77
static setDefault($locale)
Definition: Locale.php:167
setLocalizer(Localizer $localizer)
Definition: Locale.php:191
__construct($localizer=null)
Definition: Locale.php:150