Маршрут со специальными символами неправильно анализируется в Zend Framework 2

URI с немецкими специальными символами не работают (ошибка 404). У меня уже была эта проблема ( здесь), и она была решена с помощью модификатора Unicode и пользовательского помощника вида, который использует ее.

Теперь у меня та же проблема с Segment дочерний маршрут, но на этот раз подход с идентификатором Unicode и пользовательским помощником представления не работает.

Все запросы как sld.tld/sport/sportäöüÄÖÜß/cityäöüÄÖÜß или же sld.tld/sport/sportäöüÄÖÜß/cityäöüÄÖÜß/page/123 заканчиваются 404 ошибка.

/module/Catalog/config/module.config.php

<?php
return array(
    ...
    'router' => array(
        'routes' => array(
            'catalog' => array(
                ...
            ),
            'city' => array(
                ...
            ),
            // works correctly, if I remove the child route
            'sport' => array(
                'type'  => 'MyNamespace\Mvc\Router\Http\UnicodeRegex',
                'options' => array(
                    'regex' => '/catalog/(?<city>[\p{L}\p{Zs}]*)/(?<sport>[\p{L}\p{Zs}]*)',
                    'defaults' => array(
                        'controller' => 'Catalog\Controller\Catalog',
                        'action'     => 'list-courses',
                    ),
                    'spec'  => '/catalog/%city%/%sport%',
                ),
                'may_terminate' => true,
                'child_routes' => array(
                    'courses' => array(
                        'type'  => 'segment',
                        'options' => array(
                            'route' => '[/page/:page]',
                            'defaults' => array(
                                'controller' => 'Catalog\Controller\Catalog',
                                'action'     => 'list-courses',
                            ),
                        ),
                        'may_terminate' => true,
                    ),
                )
            ),
        ),
    ),
    ...
);

Я также попробовал это с UnicodeRegex детский маршрут:

        'sport' => array(
            'type'  => 'MyNamespace\Mvc\Router\Http\UnicodeRegex',
            'options' => array(
                'regex' => '/catalog/(?<city>[\p{L}\p{Zs}]*)/(?<sport>[\p{L}\p{Zs}]*)',
                'defaults' => array(
                    'controller' => 'Catalog\Controller\Catalog',
                    'action'     => 'list-courses',
                ),
                'spec'  => '/catalog/%city%/%sport%',
            ),
            'may_terminate' => true,
            'child_routes' => array(
                'courses' => array(
                    'type'  => 'MyNamespace\Mvc\Router\Http\UnicodeRegex',
                    'options' => array(
                        'regex' => '/page/(?<page>[\p{N}]*)',
                        'defaults' => array(
                            'controller' => 'Catalog\Controller\Catalog',
                            'action'     => 'list-courses',
                        ),
                        'spec'  => '/page/%page%',
                    ),
                    'may_terminate' => true,
                ),
            )
        ),

UnicodeRegex

смотрите здесь

UnicodeSegment

Расширяет Zend\Mvc\Router\Http\Segment и завершает ввод ВСЕХ preg_match(...) звонки с u:

  • '((\G(?P<literal>[^:{\[\]]*)(?P<token>[:{\[\]]|$)))u'
  • '(\G\{(?P<name>[^}]+)\}:?)u'
  • '((\G(?P<name>[^:/{\[\]]+)(?:{(?P<delimiters>[^}]+)})?:?))u'
  • '(\G(?P<literal>[^}]+)\})u'
  • '(\G' . $this->regex . ')u'
  • '(^' . $this->regex . '$)u'

Как заставить это работать?

1 ответ

Решение

Просто взглянул на это, нужно поменять UnicodeRegex метод match, чтобы он возвращал правильную длину для той части URL, с которой он совпал, вот попытка исправить это, что, кажется, работает (по крайней мере, для меня) с вашей настройкой

public function match(Request $request, $pathOffset = null)
{
    if (!method_exists($request, 'getUri')) {
        return null;
    }

    $uri  = $request->getUri();
    $path = rawurldecode($uri->getPath());

    if ($pathOffset !== null) {
        $result = preg_match('(\G' . $this->regex . ')u', $path, $matches, null, $pathOffset);
    } else {
        $result = preg_match('(^' . $this->regex . '$)u', $path, $matches);
    }

    if (!$result) {
        return null;
    }

    foreach ($matches as $key => $value) {
        if (is_numeric($key) || is_int($key) || $value === '') {
            unset($matches[$key]);
        } else {
            $matches[$key] = rawurldecode($value);
        }
    }

    // at this point there's a mismatch between the length of the rawurlencoded path
    // that all other route helpers use, so we need to match their expectations
    // to do that we build the matched part from the spec, using the matched params 
    $url = $this->spec;
    $mergedParams = array_merge($this->defaults, $matches);
    foreach ($mergedParams as $key => $value) {
        $spec = '%' . $key . '%';
        if (strpos($url, $spec) !== false) {
            $url = str_replace($spec, rawurlencode($value), $url);
        }
    }
    // make sure the url we built from spec exists in the original uri path
    if (false === strpos($uri->getPath(), $url)) {
        return null;
    }
    // now we can get the matchedLength
    $matchedLength = strlen($url);

    return new RouteMatch($mergedParams, $matchedLength);
} 
Другие вопросы по тегам