如何在preg_replace或preg_replace_callback期间将正则表达式反向引用保存到数组

时间:2013-08-30 21:11:41

标签: php regex arrays preg-replace preg-replace-callback

问题在于:我有一个数据库,里面装满了用XHTML标记的文章。我们的应用程序使用Prince XML生成PDF。其中的一个神器是脚注以内联方式标记,使用以下模式:

<p>Some paragraph text<span class="fnt">This is the text of a footnote</span>.</p>

Prince使用数字脚注标记替换每个span.fnt,并将所附文本作为脚注呈现在页面底部。

我们希望以电子书格式呈现相同的内容,而XHTML是一个很好的起点,但内联脚注非常糟糕。我想要做的是将脚注转换为我的电子书构建脚本中的尾注。

这就是我的想法:

  1. 创建一个名为$endnotes的空数组来存储尾注文本。
  2. 将变量$endnote_no设置为零。该变量将保存当前的尾注编号,以内联方式显示为尾注标记,并用于将尾注标记与特定尾注链接。
  3. 使用preg_replacepreg_replace_callback查找<span class="fnt">(.*?)</span>的每个实例。
  4. 为每个实例增加$endnote_no,并将内联span替换为'<sup><a href="#endnote_'。 $ endnote_no。 '“&GT;' 。$ endnote_no。''`
  5. 将脚注文本推送到$endnotes数组,以便我可以在文档末尾使用它。
  6. 用数字尾注引号替换所有脚注后,遍历$endnotes数组,将尾注作为XHTML中的有序列表吐出。
  7. 这个过程有点超出了我的PHP理解范围,当我尝试将其转换为代码时,我迷失了方向。这是我到目前为止所做的,我主要根据我在PHP文档中找到的代码示例拼凑在一起:

    $endnotes = array();
    $endnote_no = 0;
    class Endnoter {
    
      public function replace($subject) {
        $this->endnote_no = 0;
        return preg_replace_callback('`<span class="fnt">(.*?)</span>`', array($this, '_callback'), $subject);
      }
    
      public function _callback($matches) {
        array_push($endnotes, $1);
        return '<sup><a href="#endnote_' . $this->endnote_no++ . '">' . $this->endnote_no . '</a></sup>';
      }
    }
    

    ...

    $replacer = new Endnoter();
    $replacer->replace($body);
    echo '<pre>';
    print_r($endnotes); // Just checking to see if the $endnotes are there.
    echo '</pre>';
    

    任何指导都会有所帮助,特别是如果有更简单的方法可以实现这一目标。

2 个答案:

答案 0 :(得分:2)

首先,你最好使用正则表达式进行HTML操作而不是;看这里: How do you parse and process HTML/XML in PHP?

但是,如果您真的想要走这条路,那么您的代码就会出现一些问题:

  1. return '<sup><a href="#endnote_' . $this->endnote_no++ . '">' . $this->endnote_no . '</a></sup>';
    

    如果endnote_no为1,例如这将产生

    '<sup><a href="#endnote_1">2</a></sup>';
    

    如果这两个值都应该相同,那么首先要增加endnote_no:

    return '<sup><a href="#endnote_' . ++$this->endnote_no . '">' . $this->endnote_no . '</a></sup>';
    

    请注意调用前面的++而不是之后的。

  2. array_push($endnotes, $1);
    

    $1不是定义的值。您正在寻找传入回调的数组,因此您需要$matches[1]

  3. print_r($endnotes);
    

    $endnotes未在类外定义,因此您要么需要getter函数来检索$endnotes(通常 ),要么在类中公开变量。有了吸气剂:

    class Endnotes {
        private $endnotes = array();
        //replace any references to $endnotes in your class with $this->endnotes and add a function:
    
        public function getEndnotes() {
            return $this->endnotes;
        }
    }
    //and then outside
    print_r($replacer->getEndnotes());
    
  4. preg_replace_callback未通过引用传递,因此您实际上并未修改原始字符串。 $replacer->replace($body);应为$body = $replacer->replace($body);,除非您希望通过引用将正文传递到replace()函数并在其中更新其值。

答案 1 :(得分:2)

不知道更简单的方法,但你已经到了一半。这似乎有效。

我刚刚清理了一下,移动了类中的变量并添加了一个输出方法来获取脚注列表。

class Endnoter
{
    private $number_of_notes = 0;
    private $footnote_texts = array();

    public function replace($input) {

        return preg_replace_callback('#<span class="fnt">(.*)</span>#i', array($this, 'replace_callback'), $input);

    }

    protected function replace_callback($matches) {

        // the text sits in the matches array
        // see http://php.net/manual/en/function.preg-replace-callback.php
        $this->footnote_texts[] = $matches[1];

        return '<sup><a href="#endnote_'.(++$this->number_of_notes).'">'.$this->number_of_notes.'</a></sup>';

    }

    public function getEndnotes() {
        $out = array();
        $out[] = '<ol>';

        foreach($this->footnote_texts as $text) {
            $out[] = '<li>'.$text.'</li>';
        }

        $out[] = '</ol>';

        return implode("\n", $out);
    }

 }