preg_replace_callback没有返回正确的数据

时间:2013-09-08 10:11:35

标签: php regex arrays routing uri

这与之前我提出的问题有关:Replacing named 'parameters' within a string in PHP

那个曾经工作过的小课程,但似乎行为不端,现在我正试图把它作为扩展转移到新的Bolt CMS。

目的是从YML文件中获取数据。数据看起来像这样:

redirects:
    contentToPage:
        from: "content/(slug:any)"
        to: "page/{slug}"

扩展循环遍历此数据,并将其与从适用的Symfony组件获取的当前Request URI进行比较。如果匹配,则将相应地重定向用户。因此,在这种情况下,如果用户尝试访问content/test,则会将其重定向到page/test

但是有些东西似乎出错,转换的替换不正确,或者我被抛出错误。首先,这是有问题的块:

$convertedReplacements = preg_replace_callback("/^{$convertedPlaceholders}$/", function ($captures) {
    $result = $this->destination;
    for ($c = 1, $n = count($captures); $c < $n; ++$c) {
        $value = array_shift($this->computedReplacements);
        $result = str_replace("\{$value\}", $captures[$c], $result);
    }
    return $result;
}, $requestUri);

$convertedPlaceholders包含from值中的已替换参数。因此,(slug:any)将替换为([a-z0-9\.\-\_\%\=]+)。现在,这可行,但该函数抛出此异常:preg_replace_callback(): Unknown modifier '('

但是,如果我将正则表达式分隔符从/更改为~#,我就不会收到错误。相反,我在YML文件中获取to属性的值。在这种情况下,我得到page/{slug}而不是page/test

我必须做一些愚蠢的事情,我不知道它是什么。就我所知,我只剩下一些我看不到的东西。

以下是整个扩展程序:

<?php
// Redirector Extension for Bolt
// Minimum version: 1.2

namespace Redirector;

use Silex\Application as Application;
use Symfony\Component\HttpFoundation\Request as Request;
use Symfony\Component\Yaml\Parser as Parser;
use Bolt\BaseExtension as BoltExtension;

class Extension extends BoltExtension
{
    protected $placeholders = array(
        ':all' => '.*',
        ':alpha' => '[a-z]+',
        ':alphanum' => '[a-z0-9]+',
        ':any' => '[a-z0-9\.\-\_\%\=]+',
        ':num' => '[0-9]+',
        ':segment' => '[a-z0-9\-\_]+',
        ':segments' => '[a-z0-9\-\_\/]+'
    );

    protected $computedReplacements;

    protected $destination;

    /**
     * Basic information about the extension. Shown in the Bolt Admin Environment.
     *
     * @return Array
     */

    function info() {
        $data = array(
            'name' => 'Redirector',
            'version' => '0.1',
            'author' => 'Foundry Code - Mike Anthony',
            'description' => 'An extension that allows you to perform any pre-app <code>301 Moved Permanently</code> redirects.',
            'type' => 'Pre-app Hook',
            'link' => 'http://code.foundrybusiness.co.za/extensions/bolt-redirector',
            'first_releasedate' => '2013-08-28',
            'latest_releasedate' => '2013-08-28',
            'required_bolt_version' => '1.2',
            'highest_bolt_version' => '1.2'
        );
        return $data;
    }

    /**
     * Initialise the extension's functions
     *
     * @return void
     */

    function initialize() {
        $this->options = $this->config['options'];
        $this->redirects = $this->config['redirects'];
        $this->handleRedirects();
    }

    /**
     * Check for a redirect. If it exists, then redirect to it's computed replacement.
     *
     * @return ? Response
     */
    function handleRedirects()
    {
        $redirector = $this;
        $this->app->before(function (Request $request) use ($redirector) {
            if (empty($redirector->redirects)) {
                return;
            }
            $requestUri = trim($request->getRequestUri(), '/');

            $availablePlaceholders = '';
            foreach ($this->placeholders as $placeholder => $expression) {
                $availablePlaceholders .= ltrim("$placeholder|", ':');
            }
            $availablePlaceholders = rtrim($availablePlaceholders, '|');
            //die($availablePlaceholders);

            $pattern = '/\{(\w+):('.$availablePlaceholders.')\}/';
            //die($pattern);

            foreach ($this->redirects as $redirectName => $redirectData) {
                $this->computedReplacements = array();
                $this->destination = $redirectData['to'];
                $from = rtrim($redirectData['from'], '/');
                $to = rtrim($redirectData['to'], '/');

                $convertedPlaceholders = preg_replace_callback($pattern, function ($captures) {
                    $this->computedReplacements[] = $captures[1];
                    return '(' . $this->placeholders[":{$captures[2]}"] . ')';
                }, $from);
                //die($convertedPlaceholders);

                $convertedReplacements = preg_replace_callback("/^{$convertedPlaceholders}$/", function ($captures) {
                    $result = $this->destination;
                    for ($c = 1, $n = count($captures); $c < $n; ++$c) {
                        $value = array_shift($this->computedReplacements);
                        $result = str_replace("\{$value\}", $captures[$c], $result);
                    }
                    return $result;
                }, $requestUri);
                die($convertedReplacements);

                if (preg_match("~^{$convertedPlaceholders}$~i", $requestUri)) {
                    return $this->app->redirect("/$convertedReplacements", 301);
                }
            }
        }, Application::EARLY_EVENT);
    }
}

关于我在这里可以做什么的任何想法?

2 个答案:

答案 0 :(得分:0)

如果你想使用/作为正则表达式分隔符,你应该转义正则表达式中的所有/个字符,或者更好的是,使用另一个分隔符。

答案 1 :(得分:0)

是的,它似乎现在正在运作。出于某种原因,当我改变时,一切都跳了出来:

str_replace("\{$value\}", $captures[$c], $result);

str_replace('{' . $value . '}', $captures[$c], $result);

我认为它与逃避有关,这很奇怪。在第一个中,我使用双引号,并转义大括号,以便它呈现为{slug}。但是,出于某种原因,这种情况并没有发生。关于为什么的任何想法?