使用可转换字符串中的占位符遍历对象/访问对象属性

时间:2019-04-11 14:36:13

标签: php translation eval traversal variable-variables

在应用程序中翻译动态文本的便捷方法是使用占位符,如下所示:

$message = 'Hello I am {firstname}';
$translation = strtr($message, array('{firstname}', 'John'));

但是我希望它更具动态性,只需传递一个完整的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('');
}

现在,我的问题:

  1. 这将有多安全? (我希望这样可以限制对象的遍历)
  2. 就安全性而言,能否做到更好?
  3. 在性能方面能否做到更好?
  4. 还有其他想法吗?

0 个答案:

没有答案