Skip to content

Commit

Permalink
Refactor language loading
Browse files Browse the repository at this point in the history
* Remove statics and bring cache into class vars.
* Reduce repetition.
* Introduce 'force' parameter in available() method.
* Use Lang library for plugin loading too.
* Fix language update on upgrade.
* Attempt to reduce query counts and improve performance when updating multiple languages by using multi_query. Still runs like a dog with 4+ languages. Must be more we can do.
  • Loading branch information
Bloke committed Dec 12, 2017
1 parent 4e7da43 commit ee213d8
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 85 deletions.
14 changes: 11 additions & 3 deletions textpattern/update/_update.php
Expand Up @@ -107,16 +107,24 @@ function newest_file()
}

// Update any out-of-date installed languages.
$txpLang = Txp::get('\Textpattern\L10n\Lang');
$installed_langs = $txpLang->available(TEXTPATTERN_LANG_INSTALLED | TEXTPATTERN_LANG_ACTIVE);
// Have to refresh the cache first by reloading everything.
$time = time();
$txpLang = Txp::get('\Textpattern\L10n\Lang');
$installed_langs = $txpLang->available(
TEXTPATTERN_LANG_INSTALLED | TEXTPATTERN_LANG_ACTIVE,
TEXTPATTERN_LANG_INSTALLED | TEXTPATTERN_LANG_ACTIVE | TEXTPATTERN_LANG_AVAILABLE
);

foreach ($installed_langs as $lang_code => $info) {
$db_lastmod = isset($info['db_lastmod']) ? $info['db_lastmod'] : 0;
$file_lastmod = isset($info['file_lastmod']) ? $info['file_lastmod'] : $time;

// Reinstall any out-of-date languages and update the DB stamps in the
// cache, just in case we're on the Languages panel so it doesn't report
// the languages as being stale.
if (($file_lastmod > $db_lastmod)) {
$txpLang->installFile($lang_code);
$txpLang->available(TEXTPATTERN_LANG_AVAILABLE, TEXTPATTERN_LANG_INSTALLED | TEXTPATTERN_LANG_AVAILABLE);
}
}

Expand All @@ -126,7 +134,7 @@ function newest_file()
if (!$txp_is_dev) {
remove_pref('version', 'publish');
create_pref('version', $dbversion, 'publish', PREF_HIDDEN);
Txp::get('Textpattern\Admin\Tools')->removeFiles(txpath, 'setup');
Txp::get('\Textpattern\Admin\Tools')->removeFiles(txpath, 'setup');
}

// Invite optional third parties to the update experience
Expand Down
164 changes: 104 additions & 60 deletions textpattern/vendors/Textpattern/L10n/Lang.php
Expand Up @@ -38,16 +38,40 @@ class Lang implements \Textpattern\Container\ReusableInterface
* @var string
*/

protected $lang_dir = null;
protected $langDirectory = null;

/**
* List of files in the $lang_dir.
* List of files in the $langDirectory.
*
* @var array
*/

protected $files = array();

/**
* The currently active language designator.
*
* @var string
*/

protected $activeLang = null;

/**
* Metadata for languages installed in the database.
*
* @var array
*/

protected $dbLangs = array();

/**
* Metadata for all available languages in the filesystem.
*
* @var array
*/

protected $allLangs = array();

/**
* List of strings that have been loaded.
*
Expand All @@ -56,19 +80,27 @@ class Lang implements \Textpattern\Container\ReusableInterface

protected $strings = array();

/**
* Date format to use for the lastmod column.
*
* @var string
*/

protected $lastmodFormat = 'YmdHis';

/**
* Constructor.
*
* @param string $lang_dir Language directory to use
* @param string $langDirectory Language directory to use
*/

public function __construct($lang_dir = null)
public function __construct($langDirectory = null)
{
if ($lang_dir === null) {
$lang_dir = txpath.DS.'lang'.DS;
if ($langDirectory === null) {
$langDirectory = txpath.DS.'lang'.DS;
}

$this->lang_dir = $lang_dir;
$this->langDirectory = $langDirectory;

if (!$this->files) {
$this->files = $this->files();
Expand Down Expand Up @@ -100,13 +132,13 @@ public function installed()

public function files()
{
if (!is_dir($this->lang_dir) || !is_readable($this->lang_dir)) {
trigger_error('Lang directory is not accessible: '.$this->lang_dir, E_USER_WARNING);
if (!is_dir($this->langDirectory) || !is_readable($this->langDirectory)) {
trigger_error('Lang directory is not accessible: '.$this->langDirectory, E_USER_WARNING);

return array();
}

return glob($this->lang_dir.'*.{txt,textpack,ini}', GLOB_BRACE);
return glob($this->langDirectory.'*.{txt,textpack,ini}', GLOB_BRACE);
}

/**
Expand Down Expand Up @@ -186,35 +218,34 @@ public function fetchMeta($file)
* Depending on the flags, the returned array can contain active,
* installed or available language metadata.
*
* @param int $flags Determine which type of information to return
* @param int $force Force update the given information, even if it's already populated
* @return array
*/

public function available($flags = TEXTPATTERN_LANG_AVAILABLE)
public function available($flags = TEXTPATTERN_LANG_AVAILABLE, $force = 0)
{
static $active_lang = null;
static $in_db = array();
static $allLangs = array();

if ($active_lang === null) {
$active_lang = get_pref('language', TEXTPATTERN_DEFAULT_LANG, true);
if ($force & TEXTPATTERN_LANG_ACTIVE || $this->activeLang === null) {
$this->activeLang = get_pref('language', TEXTPATTERN_DEFAULT_LANG, true);
$this->activeLang = \Txp::get('\Textpattern\L10n\Locale')->validLocale($this->activeLang);
}

if (!$in_db) {
if ($force & TEXTPATTERN_LANG_INSTALLED || !$this->dbLangs) {
// We need a value here for the language itself, not for each one of the rows.
$in_db = safe_rows(
$this->dbLangs = safe_rows(
"lang, UNIX_TIMESTAMP(MAX(lastmod)) AS lastmod",
'txp_lang',
"owner = '' GROUP BY lang ORDER BY lastmod DESC"
);
}

if (!$allLangs) {
if ($force & TEXTPATTERN_LANG_AVAILABLE || !$this->allLangs) {
$currently_lang = array();
$installed_lang = array();
$available_lang = array();

foreach ($in_db as $language) {
if ($language['lang'] === $active_lang) {
foreach ($this->dbLangs as $language) {
if ($language['lang'] === $this->activeLang) {
$currently_lang[$language['lang']] = array(
'db_lastmod' => $language['lastmod'],
'type' => 'active',
Expand Down Expand Up @@ -250,7 +281,7 @@ public function available($flags = TEXTPATTERN_LANG_AVAILABLE)
}
}

$allLangs = array(
$this->allLangs = array(
'active' => $currently_lang,
'installed' => $installed_lang,
'available' => $available_lang,
Expand All @@ -260,15 +291,15 @@ public function available($flags = TEXTPATTERN_LANG_AVAILABLE)
$out = array();

if ($flags & TEXTPATTERN_LANG_ACTIVE) {
$out = array_merge($out, $allLangs['active']);
$out = array_merge($out, $this->allLangs['active']);
}

if ($flags & TEXTPATTERN_LANG_INSTALLED) {
$out = array_merge($out, $allLangs['installed']);
$out = array_merge($out, $this->allLangs['installed']);
}

if ($flags & TEXTPATTERN_LANG_AVAILABLE) {
$out = array_merge($out, $allLangs['available']);
$out = array_merge($out, $this->allLangs['available']);
}

return $out;
Expand Down Expand Up @@ -347,48 +378,19 @@ public function installFile($lang_code)
$langpack = array_merge($fallpack, $langpack);
}

$now = date('YmdHis');

if ($langpack) {
$exists = safe_column('name', 'txp_lang', "lang='".doSlash($lang_code)."'");

foreach ($langpack as $translation) {
extract(doSlash($translation));

$where = "lang = '{$lang}' AND name = '{$name}'";
$lastmod = empty($lastmod) ? $now : date('YmdHis', $lastmod);
$fields = "lastmod = '{$lastmod}', data = '{$data}', event = '{$event}', owner = '{$owner}'";

if (!empty($exists[$name])) {
$r = safe_update(
'txp_lang',
$fields,
$where
);
} else {
$r = safe_insert(
'txp_lang',
$fields .", lang = '{$lang}', name = '{$name}'"
);
}
}

return true;
}

return false;
return $this->upsertPack($langpack, $lang_code);
}

/**
* Installs localisation strings from a Textpack.
*
* @param string $textpack The Textpack to install
* @param bool $add_new_langs If TRUE, installs strings for any included language
* @param bool $addNewLangs If TRUE, installs strings for any included language
* @return int Number of installed strings
* @package L10n
*/

public function installTextpack($textpack, $add_new_langs = false)
public function installTextpack($textpack, $addNewLangs = false)
{
$parser = new \Textpattern\Textpack\Parser();
$parser->setLanguage(get_pref('language', TEXTPATTERN_DEFAULT_LANG));
Expand All @@ -400,12 +402,12 @@ public function installTextpack($textpack, $add_new_langs = false)

$installed_langs = $this->installed();
$done = 0;
$now = date('YmdHis');
$now = date($this->lastmodFormat);

foreach ($textpack as $translation) {
extract($translation);

if (!$add_new_langs && !in_array($lang, $installed_langs)) {
if (!$addNewLangs && !in_array($lang, $installed_langs)) {
continue;
}

Expand All @@ -428,6 +430,48 @@ public function installTextpack($textpack, $add_new_langs = false)
return $done;
}

/**
* Insert or update a language pack.
*
* @param array $langpack The language pack to store
* @return result set
*/

public function upsertPack($langpack, $lang_code)
{
if ($langpack) {
$now = date($this->lastmodFormat);
$lang_code = doSlash($lang_code);

$exists = safe_column('name', 'txp_lang', "lang='".$lang_code."'");
$sql = array();
$inserts = array();

foreach ($langpack as $translation) {
extract(doSlash($translation));

$lastmod = empty($lastmod) ? $now : date($this->lastmodFormat, $lastmod);

if (!empty($exists[$name])) {
$where = "lang = '{$lang}' AND name = '{$name}'";
$fields = "event = '{$event}', owner = '{$owner}', data = '{$data}', lastmod = '{$lastmod}'";
$sql[] = "UPDATE ".PFX."txp_lang SET $fields WHERE $where";
} else {
$fields = array("{$lang}", "{$name}", "{$event}", "{$owner}", "{$data}", "{$lastmod}");
$inserts[] = '('.join(', ', quote_list($fields)).')';
}
}

if ($inserts) {
$sql[] = "INSERT INTO ".PFX."txp_lang (lang, name, event, owner, data, lastmod) VALUES".join(', ', $inserts);
}

return safe_query($sql);
}

return false;
}

/**
* Fetches the given language's strings from the database as an array.
*
Expand Down
23 changes: 1 addition & 22 deletions textpattern/vendors/Textpattern/Plugin/Plugin.php
Expand Up @@ -291,28 +291,7 @@ public function installTextpack($name, $reset = false)
$langpack = array_merge($textpack[$fallback], $textpack[$lang]);
}

$lang = doSlash($lang);
$exists = safe_column('name', 'txp_lang', "lang='{$lang}'");

foreach ($langpack as $name => $item) {
$name = doSlash($name);
$event = doSlash($item['event']);
$data = doSlash($item['data']);
$fields = "lastmod = NOW(), data = '{$data}', event = '{$event}', owner = '{$owner}'";

if (! empty($exists[$name])) {
safe_update(
'txp_lang',
$fields,
"lang = '{$lang}' AND name = '{$name}'"
);
} else {
safe_insert(
'txp_lang',
$fields . ", lang = '{$lang}', name = '{$name}'"
);
}
}
\Txp::get('\Textpattern\L10n\Lang')->upsertPack($langpack, $lang);
}
}

Expand Down

0 comments on commit ee213d8

Please sign in to comment.