PHP复杂替换文本中括号内的变量

PHP复杂替换文本中括号内的变量,php,regex,parsing,Php,Regex,Parsing,我有一个我创建的电子邮件模板,我希望能够使用像[姓名],[电子邮件],[电话]这样的东西,并在发送时替换它们。这些都很简单,但我还有其他更复杂的标记,如[PRODUCT:12]、[ARTICLE:23]等,我希望在第一部分中读取“type”,然后将第二部分作为值处理 我猜我需要一些preg_匹配,然后preg_替换,但我真的很茫然,因为我不擅长这些功能 有什么建议吗 ---编辑--- 我只是想说清楚。问题在于两部分括号内的var:value对。我需要能够看到它的产品,并做一个id=12返回的产品

我有一个我创建的电子邮件模板,我希望能够使用像[姓名],[电子邮件],[电话]这样的东西,并在发送时替换它们。这些都很简单,但我还有其他更复杂的标记,如[PRODUCT:12]、[ARTICLE:23]等,我希望在第一部分中读取“type”,然后将第二部分作为值处理

我猜我需要一些preg_匹配,然后preg_替换,但我真的很茫然,因为我不擅长这些功能

有什么建议吗

---编辑---

我只是想说清楚。问题在于两部分括号内的var:value对。我需要能够看到它的产品,并做一个id=12返回的产品,所以我可以做一个链接查找

---编辑2---

公共函数解析消息标签($matches){
$keys=分解(“:”,$MATCHS[1]);
$key=$key[0];
$id=(数组\键\存在(1,$keys))?$keys[1]:-1;
交换机($key){
案例“名称”:
返回$this->session->member->name();
打破
案例“名字”:
返回$this->session->member->first\u name;
打破
“电子邮件”案例:
返回$this->session->member->email;
打破
案例“产品”:
$product=product::通过_id($id)查找_;
返回“”;
打破
案例“文章”:
$product=文章::按_id($id)查找_;
返回“”;
打破
}
}
公共函数parse_消息($input)
{
$pattern='/\[(名称|名字|电子邮件|电话|产品:\d+|文章:\d+\]/';
返回preg_replace_回调($pattern,create_函数('$matches','return self::parse_message_tags;'),$input);
}
结果:

致命错误:当没有类作用域处于活动状态时,无法访问self::

致命错误:不在对象上下文中使用$this


我猜create_函数()会将您带出范围/上下文。显然我在这里使用的是类方法,所以简单地调用“parse\u message\u tags”是不可取的。

这个正则表达式与Id匹配,应该在文本中为您提供Id:
\[([A-Z]*):(\d*)\]

新正则表达式
\[([A-Z]*:?\d*)\]
两者都匹配。

此正则表达式与Id都匹配,并应在文本中为您提供Id:
\[([A-Z]*):(\d*)\]

新的正则表达式
\[([A-Z]*:?\d*)\]
两者都匹配。

您可以使用如下函数:

function parse_tag($tag) {
  preg_match('#\[(.*?)\]#', $tag, $matches);
  return explode(':', $matches[1]);
}
要将其投入使用,您可以执行以下操作:

$tests = array(
  '[NAME]' => array('NAME'),
  '[PRODUCT:SHIRT]' => array('PRODUCT', 'SHIRT'), 
  '[ARTICLE:23]' => array('ARTICLE', 23)
);

foreach($tests as $search => $expected) {
  $result = parse_tag($search);
  assert($expected == $result);
}

参见一个工作示例

您可以使用如下函数:

function parse_tag($tag) {
  preg_match('#\[(.*?)\]#', $tag, $matches);
  return explode(':', $matches[1]);
}
要将其投入使用,您可以执行以下操作:

$tests = array(
  '[NAME]' => array('NAME'),
  '[PRODUCT:SHIRT]' => array('PRODUCT', 'SHIRT'), 
  '[ARTICLE:23]' => array('ARTICLE', 23)
);

foreach($tests as $search => $expected) {
  $result = parse_tag($search);
  assert($expected == $result);
}

请参见一个工作示例,在这种情况下使用的函数是preg_replace_callback(),因为您需要在索引上查找第二种类型的标记。在这里,将代码包装到类中很有用。我们需要以某种方式将数据传递给回调函数。使用全局变量会很难看

class EmailTemplate {

    protected $text;
    protected $data;

    public function __construct($text, $data) {
        $this->text = $text;
        $this->data = $data;
    }

    public function callback($matches) {
        $replacement = null;
        $name = $matches[1];
        $index = (int) $matches[2];
        if(isset($this->data[$name])) {
            $value = $this->data[$name];
            if(is_scalar($value)) {
                $replacement = $value;
            } else {
                if(isset($value[$index])) {
                    $replacement = $value[$index];
                }
            }
        }
        return $replacement;
    }

    public function __toString() {
        return preg_replace_callback('/\[(\w+)(?:\:(\d+))?\]/', array($this, 'callback'), $this->text);
    }
}

$data = array(
    "NAME" => "Bob",
    "PRODUCT" => array("dog", "cat", "eggplant")
);
$email = new EmailTemplate("Dear [NAME], please buy my [PRODUCT:2]", $data);

echo $email;

这种模式可能看起来有些令人困惑。这里的关键是(?:)?部分第二个问号表示,\:(\d+)中的模式是可选的。第一个问号和冒号将字符分组而不捕获它们。这里使用它是因为我们只需要数组查找的数字。

在这种情况下使用的函数是preg\u replace\u callback(),因为您需要对第二类标记的索引执行数组查找。在这里,将代码包装到类中很有用。我们需要以某种方式将数据传递给回调函数。使用全局变量会很难看

class EmailTemplate {

    protected $text;
    protected $data;

    public function __construct($text, $data) {
        $this->text = $text;
        $this->data = $data;
    }

    public function callback($matches) {
        $replacement = null;
        $name = $matches[1];
        $index = (int) $matches[2];
        if(isset($this->data[$name])) {
            $value = $this->data[$name];
            if(is_scalar($value)) {
                $replacement = $value;
            } else {
                if(isset($value[$index])) {
                    $replacement = $value[$index];
                }
            }
        }
        return $replacement;
    }

    public function __toString() {
        return preg_replace_callback('/\[(\w+)(?:\:(\d+))?\]/', array($this, 'callback'), $this->text);
    }
}

$data = array(
    "NAME" => "Bob",
    "PRODUCT" => array("dog", "cat", "eggplant")
);
$email = new EmailTemplate("Dear [NAME], please buy my [PRODUCT:2]", $data);

echo $email;

这种模式可能看起来有些令人困惑。这里的关键是(?:)?部分第二个问号表示,\:(\d+)中的模式是可选的。第一个问号和冒号将字符分组而不捕获它们。这里使用它是因为我们只需要数组查找的数字。

这可以通过以下方法相对干净地完成:

返回:

你好############姓名####您看过我们的最新产品吗###产品id:32##。我们相信你会喜欢的!如果您不这样做,我们会打电话给您,并将您链接到文章id:12,这将把您的电子邮件(###email###)放在我们每小时的电子邮件时事通讯上


这可以通过以下方式相对干净地完成:

返回:

你好############姓名####您看过我们的最新产品吗###产品id:32##。我们相信你会喜欢的!如果您不这样做,我们会打电话给您,并将您链接到文章id:12,这将把您的电子邮件(###email###)放在我们每小时的电子邮件时事通讯上


对于案例1(
[ID]
),这会遇到麻烦吗?这与前3项(如[ID])不匹配,因为它们的处理方式不同。我需要一个函数,就像解析电子邮件正文时一样,谁知道它是[VAR]还是[VAR:VAL]。函数必须确定并从那里开始。编辑并添加了与两者匹配的功能。对于案例1(
[ID]
)这会不会遇到问题?这与前3项(如[ID])不匹配,因为它们的处理方式不同。我需要一个函数,就像解析电子邮件正文时一样,谁知道它是[VAR]还是[VAR:VAL]. 函数必须确定并从那里开始。编辑并添加功能以匹配两者。这是最好的答案,但不需要所有额外的函数。但我遇到了一个问题,因为我需要在对象上下文和preg_replace_callback($pattern,'callbackHandler',$string)中保存它;但这并不能实现。我真的需要调用$this->callbackHandler,但这不起作用。有什么想法吗?一旦这些额外的功能被删除,我会把它标记为正确答案。就目前而言,人们无法真正看到整个答案,添加函数也不是答案的一部分。同意,我不知道我在想什么这是最好的答案,但不需要所有额外的函数。在案件中处理这一切。不过我遇到了一个问题,因为我需要