Обход свойств объекта / объекта доступа с заполнителями в переводимой строке - PullRequest
0 голосов
/ 11 апреля 2019

Удобный способ перевода динамического текста в приложениях - использовать заполнители, например:

$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('');
}

Теперь мои вопросы:

  1. Насколько это будет безопасно? (Я надеюсь, что обход объекта ограничен, как это)
  2. Может ли это быть достигнуто лучше с точки зрения безопасности?
  3. Может ли это быть достигнуто лучше с точки зрения производительности?
  4. Есть еще мысли?
...