first commit
This commit is contained in:
+160
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
namespace RocketTheme\Toolbox\File;
|
||||
|
||||
/**
|
||||
* Implements Gettext Mo File reader (readonly).
|
||||
*
|
||||
* @package RocketTheme\Toolbox\File
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class MoFile extends File
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $extension = '.mo';
|
||||
|
||||
protected $pos = 0;
|
||||
protected $str;
|
||||
protected $len;
|
||||
protected $endian;
|
||||
|
||||
/**
|
||||
* @var array|File[]
|
||||
*/
|
||||
static protected $instances = array();
|
||||
|
||||
/**
|
||||
* File can never be written.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function writable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent saving file.
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
*/
|
||||
public function save($data = null)
|
||||
{
|
||||
throw new \BadMethodCallException('save() not supported for .mo files.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent deleting file from filesystem.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $var
|
||||
* @return array
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function decode($var)
|
||||
{
|
||||
$this->endian = 'V';
|
||||
$this->str = $var;
|
||||
$this->len = strlen($var);
|
||||
|
||||
$magic = $this->readInt() & 0xffffffff;
|
||||
|
||||
if ($magic === 0x950412de) {
|
||||
// Low endian.
|
||||
$this->endian = 'V';
|
||||
} elseif ($magic === 0xde120495) {
|
||||
// Big endian.
|
||||
$this->endian = 'N';
|
||||
} else {
|
||||
throw new \RuntimeException('Not a Gettext file (.mo).');
|
||||
}
|
||||
|
||||
// Skip revision number.
|
||||
$rev = $this->readInt();
|
||||
// Total count.
|
||||
$total = $this->readInt();
|
||||
// Offset of original table.
|
||||
$originals = $this->readInt();
|
||||
// Offset of translation table.
|
||||
$translations = $this->readInt();
|
||||
|
||||
// Each table consists of string length and offset of the string.
|
||||
$this->seek($originals);
|
||||
$table_originals = $this->readIntArray($total * 2);
|
||||
$this->seek($translations);
|
||||
$table_translations = $this->readIntArray($total * 2);
|
||||
|
||||
$items = [];
|
||||
for ($i = 0; $i < $total; $i++) {
|
||||
$this->seek($table_originals[$i * 2 + 2]);
|
||||
|
||||
// TODO: Original string can have context concatenated on it. We do not yet support that.
|
||||
$original = $this->read($table_originals[$i * 2 + 1]);
|
||||
|
||||
if ($original) {
|
||||
$this->seek($table_translations[$i * 2 + 2]);
|
||||
|
||||
// TODO: Plural forms are stored by letting the plural of the original string follow the singular of the original string, separated through a NUL byte.
|
||||
$translated = $this->read($table_translations[$i * 2 + 1]);
|
||||
$items[$original] = $translated;
|
||||
}
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
protected function readInt()
|
||||
{
|
||||
$read = $this->read(4);
|
||||
|
||||
if ($read === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$read = unpack($this->endian, $read);
|
||||
|
||||
return array_shift($read);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $count
|
||||
* @return array
|
||||
*/
|
||||
protected function readIntArray($count)
|
||||
{
|
||||
return unpack($this->endian . $count, $this->read(4 * $count));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $bytes
|
||||
* @return string
|
||||
*/
|
||||
private function read($bytes)
|
||||
{
|
||||
$data = substr($this->str, $this->pos, $bytes);
|
||||
$this->seek($this->pos + $bytes);
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $pos
|
||||
* @return mixed
|
||||
*/
|
||||
private function seek($pos)
|
||||
{
|
||||
$this->pos = $pos < $this->len ? $pos : $this->len;
|
||||
return $this->pos;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user