Удобный способ перевода динамического текста в приложениях - использовать заполнители, например:
$message = 'Hello I am {firstname}';
$translation = strtr($message, array('{firstname}', 'John'));
Но я хочу, чтобы он был более динамичным и просто передавал полный объект person, а пользователь приложения мог свободно использовать все, что ему удобно, от объекта person.
Итак, учитывая следующий объект:
$customer = new tstCustomer();
$customer->firstname = 'testfirst';
$customer->lastname = 'testlast';
$customer->email = 'testemail';
class tstCustomer
{
public $firstname;
public $lastname;
public $email;
public function __construct(){}
}
Я хочу, чтобы мой пользователь мог создать такую переводимую строку:
$message = '{greetings} I am {customer->firstname} {customer->lastname} and my email address is {customer->email} {customer->notexisting}.';
Теперь мне нужно только передать объект $customer
вместо всех видов отдельных свойств (и их возможных заполнителей). Это также дает возможность обхода объекта.
echo translation($message, array('{greetings}' => 'Hello World', '{customer}' => $customer));
// Output:
// Hello World I am testfirst testlast and my email address is testemail .
Я написал следующую функцию для выполнения выше:
function translation($message, $placeholders = array())
{
// catch all notices/warnings/errors due to non existent attributes
$backupHandler = set_error_handler('strtrErrorHandler');
foreach($placeholders as $placeholder => $value)
{
if(gettype($value) === 'string')
{
$message = strtr($message, array($placeholder => $value));
continue;
}
if(gettype($value) !== 'object')
continue;
$trimmed_placeholder = trim($placeholder, '{}');
$regex = '/\{'.$trimmed_placeholder.'[\-\>[\w]*]*\}/';
$matches = array();
preg_match_all($regex, $message, $matches);
// $matches should look like:
// array(1) { [0]=> array(3) { [0]=> string(21) "{customer->firstname}" [1]=> string(20) "{customer->lastname}" [2]=> string(17) "{customer->email}" } }
if(!isset($matches[0]))
continue;
foreach($matches[0] as $match)
{
$stringpath = trim($match, '{}');
$traversal = substr($stringpath, strlen($trimmed_placeholder)+2);
try
{
$message = strtr($message, array($match => $value->{$traversal}));
}
catch(Exception $e)
{
// replace the placeholder with empty string
$message = strtr($message, array($match => ''));
}
}
} // foreach $placeholders
set_error_handler($backupHandler);
return $message;
}
// catch all and convert to a catchable Exception
function strtrErrorHandler($severity, $message, $filename, $lineno) {
Yii::log($message, 'error', 'backend.test.index');
throw new Exception('');
}
Теперь мои вопросы:
- Насколько это будет безопасно? (Я надеюсь, что обход объекта ограничен, как это)
- Может ли это быть достигнуто лучше с точки зрения безопасности?
- Может ли это быть достигнуто лучше с точки зрения производительности?
- Есть еще мысли?