Файл: library/XenForo/Phrase.php
Строк: 244
<?php
/**
* Phrase rendering class.
*
* @package XenForo_Core
*/
class XenForo_Phrase
{
/**
* Cached phrase data. Key is the phrase name; value is the phrase text.
*
* @var array
*/
protected static $_phraseCache = array();
/**
* A list of phrases that still need to be loaded. Key is the phrase name.
*
* @var array
*/
protected static $_toLoad = array();
/**
* The function that should be called to escape phrase parameters. This should
* be set by the view renderer. Set it to false to disable escaping completely.
*
* @param false|callback
*/
protected static $_escapeCallback = 'htmlspecialchars';
/**
* Name of the phrase to load.
*
* @var string
*/
protected $_phraseName;
/**
* Key-value params to make available in the phrase.
*
* @var array
*/
protected $_params = array();
/**
* Whether the params should be inserted into the phrase raw or escaped.
* If this is true or false, it applies to all params. If it is an array,
* the individual params are looked up as keys and the value is treated as a
* a boolean; params that aren't set will be escaped.
*
* @var boolean|array
*/
protected $_insertParamsEscaped = true;
/**
* Controls whether the phrase name should be output when the value is invalid.
*
* @var boolean
*/
protected $_phraseNameOnInvalid = true;
/**
* The ID of the language that phrases will be retrieved from.
*
* @var integer
*/
protected static $_languageId = 0;
/**
* Constructor
*
* @param string|array Phrase name (or, array(phraseName, arg1 => x, arg2 => y...)
* @param array Key-value parameters
* @param boolean|array See {@link $_insertParamsEscaped}
*/
public function __construct($phraseName, array $params = array(), $insertParamsEscaped = true)
{
// deal with all data being passed through the $phraseName parameter
if (is_array($phraseName))
{
$phraseKey = $phraseName[0];
unset($phraseName[0]);
$params = array_merge($phraseName, $params);
$phraseName = $phraseKey;
}
else if ($phraseName instanceof XenForo_Phrase)
{
$phraseKey = $phraseName->getPhraseName();
$params = array_merge($phraseName->getParams(), $params);
$phraseName = $phraseKey;
}
$this->_phraseName = $phraseName;
if ($params)
{
$this->setParams($params);
}
$this->setInsertParamsEscaped($insertParamsEscaped);
self::preloadPhrase($phraseName);
}
/**
* Sets the language ID that phrases will be retrieved from.
*
* @param integer $languageId
*/
public static function setLanguageId($languageId)
{
self::$_languageId = intval($languageId);
}
/**
* Gets the language ID that phrases will be retrieived from.
*
* @return integer
*/
public static function getLanguageId()
{
return self::$_languageId;
}
/**
* Add an array of params to the phrase. Overwrites parameters with the same name.
*
* @param array
*/
public function setParams(array $params)
{
$this->_params = array_merge($this->_params, $params);
}
/**
* Gets the params.
*
* @return array
*/
public function getParams()
{
return $this->_params;
}
/**
* Gets the phrase name.
*
* @return string
*/
public function getPhraseName()
{
return $this->_phraseName;
}
/**
* Sets whether inserted parameters should be automatically escaped.
* @see $_insertParamsEscaped
*
* @param array|boolean
*/
public function setInsertParamsEscaped($insertParamsEscaped)
{
if (is_array($insertParamsEscaped))
{
if (is_array($this->_insertParamsEscaped))
{
$this->_insertParamsEscaped = array_merge($this->_insertParamsEscaped, $insertParamsEscaped);
}
else
{
$this->_insertParamsEscaped = $insertParamsEscaped;
}
}
else
{
$this->_insertParamsEscaped = (bool)$insertParamsEscaped;
}
}
/**
* Sets the value for the phrase name on invalid setting. This controls the behavior
* with an invalid phrase name. If true, output phrase name; otherwise, nothing.
*
* @param boolean $value
*/
public function setPhraseNameOnInvalid($value)
{
$this->_phraseNameOnInvalid = (bool)$value;
}
/**
* Renders the specified phrase and returns the output.
*
* @param boolean|null $phraseNameOnInvalid If the phrase is invalid, return the phrase name (otherwise, empty string); if null, use default setting
*
* @return string
*/
public function render($phraseNameOnInvalid = null)
{
$phrase = self::_loadPhrase($this->_phraseName);
if (!is_string($phrase))
{
if ($phraseNameOnInvalid === null)
{
$phraseNameOnInvalid = $this->_phraseNameOnInvalid;
}
return ($phraseNameOnInvalid ? $this->_phraseName : '');
}
if (empty($this->_params))
{
return $phrase;
}
$phrase = preg_replace_callback('/{([a-z0-9_-]+)}/i', array($this, '_replaceParam'), $phrase);
return $phrase;
}
/**
* Callback function for regular expression to replace a named parameter with a value.
*
* @param array Match array. Looks in key "param".
*
* @return string Replaced value
*/
protected function _replaceParam($match)
{
$paramName = $match[1];
if (!array_key_exists($paramName, $this->_params))
{
return $match[0];
}
return $this->_escapeParam($paramName, $this->_params[$paramName]);
}
/**
* Escapes the named parameter if necessary, based on {@link $_insertParamsEscaped).
*
* @param string Name of parameter
* @param string Value of parameter
*
* @return string Escaped parameter
*/
protected function _escapeParam($paramName, $paramValue)
{
if (!self::$_escapeCallback || $paramValue instanceof XenForo_Phrase)
{
return $paramValue;
}
if (is_array($this->_insertParamsEscaped))
{
if (!isset($this->_insertParamsEscaped[$paramName]) || $this->_insertParamsEscaped[$paramName] !== false)
{
// escape selective and this one is to be escaped or wasn't explicitly disabled
$paramValue = call_user_func(self::$_escapeCallback, $paramValue);
}
}
else if ($this->_insertParamsEscaped !== false)
{
// escape all
$paramValue = call_user_func(self::$_escapeCallback, $paramValue);
}
return $paramValue;
}
/**
* Implicit string cast renders the phrase.
*
* @return string
*/
public function __toString()
{
try
{
return $this->render();
}
catch (Exception $e)
{
XenForo_Error::logException($e, false, "Phrase to string error:");
XenForo_Error::unexpectedException($e);
}
}
/**
* Load the named phrase.
*
* @param string Phrase name
*
* @return string Compiled version of the phrase
*/
protected static function _loadPhrase($phraseName)
{
if (!isset(self::$_phraseCache[$phraseName]))
{
self::loadPhrases();
if (!isset(self::$_phraseCache[$phraseName]))
{
// couldn't find this phrase
self::$_phraseCache[$phraseName] = false;
}
}
return self::$_phraseCache[$phraseName];
}
/**
* Bulk load all phrases that are required.
*/
public static function loadPhrases()
{
if (!self::$_toLoad)
{
return;
}
$db = XenForo_Application::getDb();
$phrases = $db->fetchPairs('
SELECT title, phrase_text
FROM xf_phrase_compiled
WHERE language_id = ?
AND title IN (' . $db->quote(array_keys(self::$_toLoad)) . ')
', self::$_languageId);
self::$_phraseCache = array_merge(self::$_phraseCache, $phrases);
self::$_toLoad = array();
}
/**
* Specify a phrase that needs to be preloaded for use later. This is useful
* if you think a render is going to be called before the phrase you require
* is to be used.
*
* @param string Phrase to preload
*/
public static function preloadPhrase($phraseName)
{
if (!isset(self::$_phraseCache[$phraseName]))
{
self::$_toLoad[$phraseName] = true;
}
}
/**
* Manually sets a phrase. This is primarily useful for testing.
*
* @param string Name of the phrase
* @param string Value for the phrase
*/
public static function setPhrase($phraseName, $phraseValue)
{
self::$_phraseCache[$phraseName] = $phraseValue;
}
/**
* Manually sets the cache values for collection of phrases.
*
* @param array $phrases
*/
public static function setPhrases(array $phrases)
{
self::$_phraseCache = array_merge(self::$_phraseCache, $phrases);
}
/**
* Resets the phrase system state.
*/
public static function reset()
{
self::$_phraseCache = array();
self::$_toLoad = array();
}
/**
* Sets the param escaping callback function.
*
* @param false|callback $callback
*/
public static function setEscapeCallback($callback)
{
self::$_escapeCallback = $callback;
}
}