【问题标题】:preg_replace_callback not returning the correct datapreg_replace_callback 没有返回正确的数据
【发布时间】:2013-09-08 10:11:34
【问题描述】:

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

那个小类过去可以工作,但现在我正试图将它作为扩展移至新的 Bolt CMS。

目的是从 YML 文件中获取数据。数据如下:

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

扩展程序循环遍历此数据并将其与当前请求 URI 进行比较,该请求 URI 是从适用的 Symfony 组件获得的。如果匹配,用户将被相应地重定向。因此,在这种情况下,如果用户尝试访问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);
    }
}

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

【问题讨论】:

    标签: php regex arrays routing uri


    【解决方案1】:

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

    【讨论】:

    • 就是这样,:any 规则不允许使用正斜杠。当我切换分隔符时,我没有得到正确的替换。
    • 哦,对不起,我明白了 - 忘记它匹配整个字符串。尽管如此,它仍然有问题。
    【解决方案2】:

    好吧,它现在似乎可以工作了。出于某种原因,当我改变时,一切都到位了:

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

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

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

    【讨论】:

      猜你喜欢
      • 2016-11-12
      • 1970-01-01
      • 2013-10-14
      • 2015-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多