2013-04-02 3 views
9

Только что закончил выполнение этой функции. В принципе, предположим, что нужно просмотреть строку и попытаться найти любые переменные-заполнители, которые будут размещаться между двумя фигурными скобками {}. Он захватывает значение между фигурными скобками и использует его для просмотра массива, где он должен соответствовать ключу. Затем он заменяет переменную фигурного скобки в строке значением в массиве соответствующего ключа.Замена переменных-заполнителей в строке

У этого есть несколько проблем, хотя. Во-первых, когда я var_dump($matches), он помещает помещает результаты в массив внутри массива. Поэтому я должен использовать два foreach() только для получения правильных данных.

Я также чувствую себя тяжелым, и я смотрел на него, пытаясь сделать его лучше, но я несколько в тупике. Любые оптимизации, которые я пропустил?

function dynStr($str,$vars) { 
    preg_match_all("/\{[A-Z0-9_]+\}+/", $str, $matches); 
    foreach($matches as $match_group) { 
     foreach($match_group as $match) { 
      $match = str_replace("}", "", $match); 
      $match = str_replace("{", "", $match); 
      $match = strtolower($match); 
      $allowed = array_keys($vars); 
      $match_up = strtoupper($match); 
      $str = (in_array($match, $allowed)) ? str_replace("{".$match_up."}", $vars[$match], $str) : str_replace("{".$match_up."}", '', $str); 
     } 
    } 
    return $str; 
} 

$variables = array("first_name"=>"John","last_name"=>"Smith","status"=>"won"); 
$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.'; 
echo dynStr($string,$variables); 
//Would output: 'Dear John Smith, we wanted to tell you that you won the competition.' 
+0

Пожалуйста, предоставьте нам образец данных ($ ул & $) Варс – HamZa

+0

Ваш подход является весьма неэффективным. Рассмотрим альтернативный подход: a) используйте 'preg_replace_callback', чтобы вернуть значение согласованного токена из' $ vars' без миллионов вызовов 'str_replace'; б) преобразуйте каждую запись в '$ vars', чтобы включить ведущую/конечную скобку, затем подайте' $ vars' в 'strtr'. – DCoder

+0

Вы делаете это как учебное упражнение или для производства? если для производства вы можете захотеть взглянуть на библиотеку шаблонов, такую ​​как 'smarty' – dm03514

ответ

27

Я думаю, что для такой простой задачи, которую не нужно использовать регулярные выражения:

$variables = array("first_name"=>"John","last_name"=>"Smith","status"=>"won"); 
$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.'; 

foreach($variables as $key => $value){ 
    $string = str_replace('{'.strtoupper($key).'}', $value, $string); 
} 

echo $string; // Dear John Smith, we wanted to tell you that you won the competition. 
+2

Это на самом деле даже более простой, чем мой ответ, и, вероятно, достаточно. –

+2

Вау, да. Думаю, мы все слишком много думали об этом. Я проведу несколько тестов, чтобы узнать, быстрее ли это, и дайте знать. Достаточная. –

+0

@fireeyedboy Мне понравилось ваше решение 'preg_replace_callback()', я пытался выяснить, как использовать анонимную функцию и как включить переменную '$ allowed 'в область действия, а затем я увидел ваш ответ. +1: D – HamZa

6

Я думаю, вы можете значительно упростить код, с этим (если я не искажая некоторые требования):

$allowed = array("first_name"=>"John","last_name"=>"Smith","status"=>"won"); 

$resultString = preg_replace_callback(

    // the pattern, no need to escape curly brackets 
    // uses a group (the parentheses) that will be captured in $matches[ 1 ] 
    '/{([A-Z0-9_]+)}/', 

    // the callback, uses $allowed array of possible variables 
    function($matches) use ($allowed) 
    { 
     $key = strtolower($matches[ 1 ]); 
     // return the complete match (captures in $matches[ 0 ]) if no allowed value is found 
     return array_key_exists($key, $allowed) ? $allowed[ $key ] : $matches[ 0 ]; 
    }, 

    // the input string 
    $yourString 
); 

PS .: если вы хотите удалить заполнителей, которые не разрешены от входной строки, замените

return array_key_exists($key, $allowed) ? $allowed[ $key ] : $matches[ 0 ]; 

с

return array_key_exists($key, $allowed) ? $allowed[ $key ] : ''; 
+0

Похоже, что это может быть лучше, но мне любопытно, к какой функции ($ matches) use ($ allowed) 'is? Я никогда не видел «использование», и мой редактор кода (Dreamweaver) говорит, что он неверен. –

+3

@TylerDusty: это [анонимная функция] (http://php.net/manual/en/functions.anonymous.php), для которой требуется PHP 5.3. – DCoder

+2

@TylerDusty Обратный вызов - это [закрытие] (http://www.php.net/manual/en/functions.anonymous.php). Конструкция 'use' используется для импорта переменной из определяющей области, так что ее можно использовать внутри закрытия. И действительно, как уже упоминал DCoder, это функция PHP 5.3+. –

3

Это функция, которую я использую:

function searchAndReplace($search, $replace){ 
    preg_match_all("/\{(.+?)\}/", $search, $matches); 

    if (isset($matches[1]) && count($matches[1]) > 0){ 
     foreach ($matches[1] as $key => $value) { 
      if (array_key_exists($value, $replace)){ 
       $search = preg_replace("/\{$value\}/", $replace[$value], $search); 
      } 
     } 
    } 
    return $search; 
} 


$array = array(
'FIRST_NAME' => 'John', 
'LAST_NAME' => 'Smith', 
'STATUS' => 'won' 
); 

$paragraph = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.'; 

// outputs: Dear John Smith, we wanted to tell you that you won the competition. 

Просто передайте ему текст для поиска и . массив с заменами в

9

Я надеюсь, я не слишком поздно, чтобы вступить в партию - вот как я бы это сделать:

function template_substitution($template, $data) { 
    $placeholders = array_keys($data); 
    foreach ($placeholders as &$placeholder) { 
     $placeholder = strtoupper("{{$placeholder}}"); 
    } 
    return str_replace($placeholders, array_values($data), $template); 
} 

$variables = array(
    'first_name' => 'John', 
    'last_name' => 'Smith', 
    'status' => 'won', 
); 

$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you have {STATUS} the competition.'; 

echo template_substitution($string, $variables); 

И, если случайно вы могли бы сделать ваши $variables ключи в соответствии с вашими заполнители точно, решение становится до смешного простым: (см. strtr() в PHP руководстве)

$variables = array(
    '{FIRST_NAME}' => 'John', 
    '{LAST_NAME}' => 'Smith', 
    '{STATUS}' => 'won', 
); 

$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you have {STATUS} the competition.'; 

echo strtr($string, $variables); 

Принимая во внимание характер языка PHP, я считаю, что этот подход должен обеспечить наилучшую производительность из всех перечисленных в этом потоке.

+0

Я согласен - производительность в стороне, это также самый читаемый и обслуживаемый IMHO. Мой ответ на другой вопрос (http://stackoverflow.com/a/36781566/224707) также показывает другие преимущества такого подхода, такие как способность программно повышать значения замещения перед обработкой (например, кодирование или их использование) ... – Nick

1
/** 
    replace placeholders with object 
**/ 
$user = new stdClass(); 
$user->first_name = 'Nick'; 
$user->last_name = 'Trom'; 

$message = 'This is a {{first_name}} of a user. The user\'s {{first_name}} is replaced as well as the user\'s {{last_name}}.'; 

preg_match_all('/{{([0-9A-Za-z_]+)}}/', $message, $matches); 

foreach($matches[1] as $match) 
{ 
    if(isset($user->$match)) 
     $rep = $user->$match; 
    else 
     $rep = ''; 

    $message = str_replace('{{'.$match.'}}', $rep, $message); 
} 

echo $message; 
2

Всего главы вверх для будущих людей, которые выпадут на этой странице: Все ответы (в том числе принятого ответа) с использованием foreach петель и/или метода str_replace восприимчивы к замене старого доброго Джонни {STATUS} с Johnny won.

Подходящий подход Dabbler preg_replace_callback и второй вариант U-D13 (но не первый) являются единственными в настоящее время. Я вижу, что они не уязвимы для этого, но поскольку у меня нет достаточной репутации, чтобы добавить комментарий I Я просто напишу совершенно другой ответ, я думаю.

Если ваши значения замены содержат пользовательский ввод, более безопасным решением является использование функции strtr вместо str_replace, чтобы избежать повторной замены любых заполнителей, которые могут отображаться в ваших значениях.

$string = 'Dear {FIRST_NAME} {LAST_NAME}, we wanted to tell you that you {STATUS} the competition.'; 
$variables = array(
    "first_name"=>"John", 
    // Note the value here 
    "last_name"=>"{STATUS}", 
    "status"=>"won" 
); 

// bonus one-liner for transforming the placeholders 
// but it's ugly enough I broke it up into multiple lines anyway :) 
$replacement = array_combine(
    array_map(function($k) { return '{'.strtoupper($k).'}'; }, array_keys($variables)), 
    array_values($variables) 
); 

echo strtr($string, $replacement); 

Выходы: Dear John {STATUS}, we wanted to tell you that you won the competition. принимая во внимание, str_replace выходы: Dear John won, we wanted to tell you that you won the competition.