Intereting Posts
Почему «echo strcmp ('60», «100»), «в php-выходе 5? Ручка laravel 4 не найдена в Route :: model Разрешение отклонено mkdir для cron и браузера Понимание логики foreach со ссылками – Почему первый элемент изменен на «два», второй на «три», а третий на «три3»? Eclipse indigo PDT 3.0 получил Применение информации из одной таблицы в отдельную таблицу в той же базе данных? Должен ли использоваться PDO :: ATTR_PERSISTENT каждый раз? Если первый пост, стиль по-разному – WordPress AWS SDK для PHP: ошибка получения учетных данных с сервера метаданных профиля экземпляра Paypal SDK Adaptive Payments Неизвестный шифр в списке: TLSv1 Как создать таблицу символов как сохранить <br/> теги при использовании Dom в php для анализа html-документа? Справка по началу сеанса PHP PHP Regex для получения идентификатора видео youtube? Инициализация объявлений свойств класса PHP с помощью простых выражений дает синтаксическую ошибку

Рекурсивная замена переменных PHP

Я пишу код, чтобы рекурсивно заменить предопределенные переменные изнутри данной строки. Переменные имеют префикс с символом «%». Строки ввода, начинающиеся с '^', должны быть оценены.

Например, предполагая массив переменных, таких как:

$vars['a'] = 'This is a string'; $vars['b'] = '123'; $vars['d'] = '%c'; // Note that $vars['c'] has not been defined $vars['e'] = '^5 + %d'; $vars['f'] = '^11 + %e + %b*2'; $vars['g'] = '^date(\'l\')'; $vars['h'] = 'Today is %g.'; $vars['input_digits'] = '*****'; $vars['code'] = '%input_digits'; 

Следующий код приведет к:

 a) $str = '^1 + %c'; $rc = _expand_variables($str, $vars); // Result: $rc == 1 b) $str = '^%a != NULL'; $rc = _expand_variables($str, $vars); // Result: $rc == 1 c) $str = '^3+%f + 3'; $rc = _expand_variables($str, $vars); // Result: $rc == 262 d) $str = '%h'; $rc = _expand_variables($str, $vars); // Result: $rc == 'Today is Monday' e) $str = 'Your code is: %code'; $rc = _expand_variables($str, $vars); // Result: $rc == 'Your code is: *****' 

Любые предложения о том, как это сделать? Я много дней пытался это сделать, но добился лишь частичного успеха. К сожалению, моей последней попытке удалось создать «ошибку сегментации»!

Помощь будет очень признательна!

Обратите внимание, что нет проверки против кругового включения, которая просто приведет к бесконечному циклу. (Пример: $vars['s'] = '%s'; ..) Поэтому убедитесь, что ваши данные свободны от таких конструкций. Прокомментированный код

  // if(!is_numeric($expanded) || (substr($expanded.'',0,1)==='0' // && strpos($expanded.'', '.')===false)) { .. // } 

может использоваться или пропускаться. Если он пропущен, любая замена цитируется, если строка $str будет оценена позже! Но так как PHP автоматически преобразует строки в числа (или я должен сказать, что он пытается это сделать?), Пропуская код, он не должен приводить к каким-либо проблемам. Обратите внимание, что булевские значения не поддерживаются! (Также нет автоматического преобразования, выполняемого PHP, которое преобразует строки, такие как «true» или «false» в соответствующие логические значения!)

  <? $vars['a'] = 'This is a string'; $vars['b'] = '123'; $vars['d'] = '%c'; $vars['e'] = '^5 + %d'; $vars['f'] = '^11 + %e + %b*2'; $vars['g'] = '^date(\'l\')'; $vars['h'] = 'Today is %g.'; $vars['i'] = 'Zip: %j'; $vars['j'] = '01234'; $vars['input_digits'] = '*****'; $vars['code'] = '%input_digits'; function expand($str, $vars) { $regex = '/\%(\w+)/'; $eval = substr($str, 0, 1) == '^'; $res = preg_replace_callback($regex, function($matches) use ($eval, $vars) { if(isset($vars[$matches[1]])) { $expanded = expand($vars[$matches[1]], $vars); if($eval) { // Special handling since $str is going to be evaluated .. // if(!is_numeric($expanded) || (substr($expanded.'',0,1)==='0' // && strpos($expanded.'', '.')===false)) { $expanded = "'$expanded'"; // } } return $expanded; } else { // Variable does not exist in $vars array if($eval) { return 'null'; } return $matches[0]; } }, $str); if($eval) { ob_start(); $expr = substr($res, 1); if(eval('$res = ' . $expr . ';')===false) { ob_end_clean(); die('Not a correct PHP-Expression: '.$expr); } ob_end_clean(); } return $res; } echo expand('^1 + %c',$vars); echo '<br/>'; echo expand('^%a != NULL',$vars); echo '<br/>'; echo expand('^3+%f + 3',$vars); echo '<br/>'; echo expand('%h',$vars); echo '<br/>'; echo expand('Your code is: %code',$vars); echo '<br/>'; echo expand('Some Info: %i',$vars); ?> 

Вышеприведенный код предполагает PHP 5.3, поскольку он использует закрытие.

Вывод:

 1 1 268 Today is Tuesday. Your code is: ***** Some Info: Zip: 01234 

Для PHP <5.3 может использоваться следующий адаптированный код:

 function expand2($str, $vars) { $regex = '/\%(\w+)/'; $eval = substr($str, 0, 1) == '^'; $res = preg_replace_callback($regex, array(new Helper($vars, $eval),'callback'), $str); if($eval) { ob_start(); $expr = substr($res, 1); if(eval('$res = ' . $expr . ';')===false) { ob_end_clean(); die('Not a correct PHP-Expression: '.$expr); } ob_end_clean(); } return $res; } class Helper { var $vars; var $eval; function Helper($vars,$eval) { $this->vars = $vars; $this->eval = $eval; } function callback($matches) { if(isset($this->vars[$matches[1]])) { $expanded = expand($this->vars[$matches[1]], $this->vars); if($this->eval) { // Special handling since $str is going to be evaluated .. if(!is_numeric($expanded) || (substr($expanded . '', 0, 1)==='0' && strpos($expanded . '', '.')===false)) { $expanded = "'$expanded'"; } } return $expanded; } else { // Variable does not exist in $vars array if($this->eval) { return 'null'; } return $matches[0]; } } } 

Теперь я написал оценщика для вашего кода, который также обращается к круговой справочной проблеме.

Использование:

 $expression = new Evaluator($vars); $vars['a'] = 'This is a string'; // ... $vars['circular'] = '%ralucric'; $vars['ralucric'] = '%circular'; echo $expression->evaluate('%circular'); 

Я использую $this->stack для обработки круговых ссылок. (Не знаю, что такое стек, я просто назвал его так ^^)

 class Evaluator { private $vars; private $stack = array(); private $inEval = false; public function __construct(&$vars) { $this->vars =& $vars; } public function evaluate($str) { // empty string if (!isset($str[0])) { return ''; } if ($str[0] == '^') { $this->inEval = true; ob_start(); eval('$str = ' . preg_replace_callback('#%(\w+)#', array($this, '_replace'), substr($str, 1)) . ';'); if ($error = ob_get_clean()) { throw new LogicException('Eval code failed: '.$error); } $this->inEval = false; } else { $str = preg_replace_callback('#%(\w+)#', array($this, '_replace'), $str); } return $str; } private function _replace(&$matches) { if (!isset($this->vars[$matches[1]])) { return $this->inEval ? 'null' : ''; } if (isset($this->stack[$matches[1]])) { throw new LogicException('Circular Reference detected!'); } $this->stack[$matches[1]] = true; $return = $this->evaluate($this->vars[$matches[1]]); unset($this->stack[$matches[1]]); return $this->inEval == false ? $return : '\'' . $return . '\''; } } с class Evaluator { private $vars; private $stack = array(); private $inEval = false; public function __construct(&$vars) { $this->vars =& $vars; } public function evaluate($str) { // empty string if (!isset($str[0])) { return ''; } if ($str[0] == '^') { $this->inEval = true; ob_start(); eval('$str = ' . preg_replace_callback('#%(\w+)#', array($this, '_replace'), substr($str, 1)) . ';'); if ($error = ob_get_clean()) { throw new LogicException('Eval code failed: '.$error); } $this->inEval = false; } else { $str = preg_replace_callback('#%(\w+)#', array($this, '_replace'), $str); } return $str; } private function _replace(&$matches) { if (!isset($this->vars[$matches[1]])) { return $this->inEval ? 'null' : ''; } if (isset($this->stack[$matches[1]])) { throw new LogicException('Circular Reference detected!'); } $this->stack[$matches[1]] = true; $return = $this->evaluate($this->vars[$matches[1]]); unset($this->stack[$matches[1]]); return $this->inEval == false ? $return : '\'' . $return . '\''; } } 

Изменить 1 : я проверил максимальную глубину рекурсии для этого скрипта, используя это:

 $alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEF'; // GHIJKLMNOPQRSTUVWXYZ $length = strlen($alphabet); $vars['a'] = 'Hallo World!'; for ($i = 1; $i < $length; ++$i) { $vars[$alphabet[$i]] = '%' . $alphabet[$i-1]; } var_dump($vars); $expression = new Evaluator($vars); echo $expression->evaluate('%' . $alphabet[$length - 1]); 

Если другой символ добавлен в $alphabet будет достигнута максимальная глубина рекурсии 100. (Но, возможно, вы можете изменить эту настройку где-нибудь?)

Я на самом деле просто сделал это, реализуя структуру MVC.

То, что я сделал, это создать функцию «find-tags», которая использует регулярное выражение для поиска всех вещей, которые должны быть заменены с помощью preg_match_all, а затем повторена в списке и вызвана функцией рекурсивно с помощью str_replaced кода.

ОЧЕНЬ Упрощенный код

 function findTags($body) { $tagPattern = '/{%(?P<tag>\w+) *(?P<inputs>.*?)%}/' preg_match_all($tagPattern,$body,$results,PREG_SET_ORDER); foreach($results as $command) { $toReturn[] = array(0=>$command[0],'tag'=>$command['tag'],'inputs'=>$command['inputs']); } if(!isset($toReturn)) $toReturn = array(); return $toReturn; } function renderToView($body) { $arr = findTags($body); if(count($arr) == 0) return $body; else { foreach($arr as $tag) { $body = str_replace($tag[0],$LOOKUPARRY[$tag['tag']],$body); } } return renderToView($body); }