We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Ошибка в маршрутизации

Как можно избежать таких ситуаций, т.е. страница доступна по след. URL

https://invo.phalcon.io/////session///register https://invo.phalcon.io/session/register///////////

Заранее, спасибо!

Нашел сам решение этого неприятного момента

$router->setUriSource(Router::URI_SOURCE_SERVER_REQUEST_URI);


8.1k

Ошибки нет. Эти маршруты соответствуют маске маршрутов при поведении по умолчанию. См. https://docs.phalconphp.ru/ru/latest/reference/routing.html#id11 This default behavior corresponds to the default mask /:controller/:action/:params https://docs.phalcon.io/en/latest/reference/routing.html#default-behavior

Я задаю вот такой марушрут

$router->add('/home/hello', [...]);

И получает что страница доступна по всем возможным комбинациям /

https://localhost/home/hello https://localhost///home//hello https://localhost/home///////hello и т.д.



8.1k

Ну, тогда покажите ваш роутер.

Я уже решил свою проблему. Просто в _url удаляются лишние слеши поэтому и получается такая ситуация

Показывать особо нечего

$di['router'] = function() {

    $router = new Phalcon\Mvc\Router(false);
    // $router->setUriSource(Router::URI_SOURCE_SERVER_REQUEST_URI); // Специально законметил чтобы была понятна суть проблемы, т.к. это решило мою проблему
    $router->removeExtraSlashes(false);

    $router->add('/home/hello', [
        'controller' => 'index',
        'action'     => 'hello'
    ]);

    return $router;
};


8.1k

Да, наблюдается такое. Стало интересно, сделал тестирование ситуации:

<?php

header('Content-Type: text/html; charset=utf-8');

$router = new \Phalcon\Mvc\Router(false);
    //$router->setUriSource(\Phalcon\Mvc\Router::URI_SOURCE_SERVER_REQUEST_URI); // Специально законметил чтобы была понятна суть проблемы, т.к. это решило мою проблему
    $router->removeExtraSlashes(false);

    $router->add('/home/hello', [
        'controller' => 'index',
        'action'     => 'hello'
    ]);

$testRoutes = [
'/home/hello',
'/',
'/home//////hello',
'home/hello////////',
];

foreach ($testRoutes as $testRoute) {

    $router->handle($testRoute);

    echo 'Test route : ', $testRoute, '<br>', PHP_EOL;

    if ($router->wasMatched()) {
        echo 'Controller : ', $router->getControllerName(), '<br>', PHP_EOL;
        echo 'Action : ', $router->getActionName(), '<br>', PHP_EOL;
    } else {
        echo "The route was't matched...<br>", PHP_EOL;
    }
    echo '<br>', PHP_EOL;

}    

echo '<hr><pre>';
var_dump($_SERVER);
echo '<hr>';
var_dump($router->getRoutes());
echo '</pre>';

Result:

Test route : /home/hello
Controller : index
Action : hello

Test route : /
The route was't matched...

Test route : /home//////hello
The route was't matched...

Test route : home/hello////////
The route was't matched...

array(21) {
  ["DOCUMENT_ROOT"]=>
  string(40) "/projects/phalcon"
  ["REMOTE_ADDR"]=>
  string(3) "::1"
  ["REMOTE_PORT"]=>
  string(5) "45666"
  ["SERVER_SOFTWARE"]=>
  string(28) "PHP 5.5.6 Development Server"
  ["SERVER_PROTOCOL"]=>
  string(8) "HTTP/1.1"
  ["SERVER_NAME"]=>
  string(9) "localhost"
  ["SERVER_PORT"]=>
  string(4) "9000"
  ["REQUEST_URI"]=>
  string(11) "/client.php"
  ["REQUEST_METHOD"]=>
  string(3) "GET"
  ["SCRIPT_NAME"]=>
  string(11) "/client.php"
  ["SCRIPT_FILENAME"]=>
  string(51) "/projects/phalcon/client.php"
  ["PHP_SELF"]=>
  string(11) "/client.php"
  ["HTTP_HOST"]=>
  string(14) "localhost:9000"
  ["HTTP_USER_AGENT"]=>
  string(66) "Mozilla/5.0 (X11; Linux i686; rv:25.0) Gecko/20100101 Firefox/25.0"
  ["HTTP_ACCEPT"]=>
  string(63) "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
  ["HTTP_ACCEPT_LANGUAGE"]=>
  string(23) "en-us,en;q=0.7,ru;q=0.3"
  ["HTTP_ACCEPT_ENCODING"]=>
  string(13) "gzip, deflate"
  ["HTTP_DNT"]=>
  string(1) "0"
  ["HTTP_CONNECTION"]=>
  string(10) "keep-alive"
  ["REQUEST_TIME_FLOAT"]=>
  float(1384622395.9004)
  ["REQUEST_TIME"]=>
  int(1384622395)
}
array(1) {
  [0]=>
  object(Phalcon\Mvc\Router\Route)#2 (9) {
    ["_pattern":protected]=>
    string(11) "/home/hello"
    ["_compiledPattern":protected]=>
    string(11) "/home/hello"
    ["_paths":protected]=>
    array(2) {
      ["controller"]=>
      string(5) "index"
      ["action"]=>
      string(5) "hello"
    }
    ["_methods":protected]=>
    NULL
    ["_hostname":protected]=>
    NULL
    ["_converters":protected]=>
    NULL
    ["_id":protected]=>
    int(0)
    ["_name":protected]=>
    NULL
    ["_beforeMatch":protected]=>
    NULL
  }
}

У вас есть прекрасная возможность оформить ошибку на GitHub.



8.1k

P.S. Я продублировал эту ситуацию на английском. Посмотрим, может Phalcon обратит на нее внимание.

Хаха, в Ларавел 4 проверил, там такой баги нет



8.1k

Там ротинг Symfony. В laravel практически своего ничего уже нет.



8.1k

Если испольуются аннотации, эта ситуация не проявляется.

В Джумле 1.5, в MODX Evo/Revo такая же картина ((((

Вот такое поведение должно быть нормальным https://www.artlebedev.ru/////////////////////////everything/ Т.е. редирект на нормальный URL



8.1k

Да нет, это, я считаю, тоже ненормально. Посмотрел, я regexp в \Palcon\Mvc\Router, мне кажется именно там проблема. По крайней мере регулярка пропускает много слешей. Жаль, я С настолько не знаю...

Нормально отдать 404?



8.1k

Думаю, нормально. Symfony так поступает. Есть опасность зациклиться с редиректом. К тому же, откуда мы знаем, откуда лишний слеш, и что подразумевалось в таком адресе - контроллер/данные, модуль/контроллер/действие/данные, контроллер/действие. Т.е. где ошибка? Что именно пропущено во вводе? Поэтому мое мнение - отдать 404.

Лучше эту проблему решать на уровне веб-сервера, а не приложения. Phalcon нормализует путь, поэтому он работает в одном случае и обрабатывает ненормализированный путь в случае с Router::URI_SOURCE_SERVER_REQUEST_URI. Просто нужно добавить правило в URL rewrite настройки сервера.



8.1k

Вы уж извините, но с роутами лучше разбираться в одном месте. А то получится - здесь читаем, здесь смотрим, это пропускаем, здесь рыбу заворачивали.... Да и с перенаправлением можно хороший цикл поймать.

А про RFC спасибо, внимательно почитаю, раз уж появился повод.



8.1k

Как говорится - с ходу. RFC 3986 Part 3.3 Path. ... If a URI does not contain an authority component, then the path cannot begin with two slash characters ("//"). Т.е. двойной слеш должен быть только в начале authority. Более того, ...When authority is not present, the path cannot begin with two slash characters ("//"). Т.е. во всех остальных случаях сдвоенные слеши не допускаются и трактуются как ошибочные. https://tools.ietf.org/html/rfc3986#appendix-A Именно поэтому, наверное, в Symfony такие path и не пропускают.

Это неточная трактовка RFC, двойные слеши не могут быть на начале пути, но могут быть например в середине, RFC ничего не говорит по этому поводу. Теоретически второй слеш может отделять директорию с пустым именем и это будет идентификатор другого ресурса, e.g., /some/path != /some//path. Но это все полемика, на деле все обстоит по-другому.

Фишечки типа "а под ссылочкой /babe-with-big-boobs мы будем отдавать из базы фото тетки с сиськами о-о-о-ооотакого размера!" придумали SEOшники. Изначальные правила трактовки путей в ссылках работают по принципам навигации в файловой системе. Где dublicate slashes, dot segments, trailing slashes и прочее относится к path normalization.

Path normalization в свою очередь может проводиться на нескольких уровнях: браузер - прокси - веб-сервер (см. первый пример) - CGI - скрипт (роутер). Первое и возможно второе вне нашего контроля, поэтому давай рассмотрим нормализацию пути в скрипте или как ты логично указал - в Раутере. Во-первых, раутер нифига не знает о конечном ресурсе ибо его задача всего-навсего определить кто именно может обработать путь и вернуть эту информацию в dispatcher. Уже немного нелогично получается и совсем некрасиво если мы задумаемся "а чё делать дальше то?". Давай с тобой почешем репки:

  • нормализировать путь и обрабатывать конечный разультат если он соответствует некой route. /some/path == /some//path. Работать будет, но для SEO это плохо и по RFC это уже не уникальный идентификатор
  • отдать 404 Not Found, или как вариант ...
  • отдать 400 Bad Request, но с другой стороны мы очень заинтересованы чтобы человек нашел то что искал, если проблема решаемая, поэтому лучше ...
  • отдать 301 Permanent Redirect ... хотя, если это ошибчно засвеченная ссылка, то мы хотим чтобы ею не пользовались, поэтому лучше ...
  • отдать 302 Found и указать что она Expiry: +1 week например, если мы уверены что неправильная ссылка больше работать не будет

Какой вариант выбрать, вот вопрос?! Я думаю, тот который удобнее по ситуации и тут сразу назревает второй вопрос, а откуда будет Раутер знать что он Анна Каренина и под какой поезд из пяти выше перечисленных бросаться? Нет, можно конечно, учитывая что круглое не впихнуть в квадратное, сделать их оба треугольными и использовать какое-то generic решение, 404 например, но рано или поздно появится второй Иван и скажет что его это не устраивает...

Теперь задумаемся о таком: а правда ли нам надо нагружать приложение бессмысленной логикой с перенеправлениями, блекджеком и шлюхами, если это может делать сервер?



8.1k

Наверное, это вечная тема. В RFC я ничего не нашел о разрешении двойных слэшей, хотя и явного запрета там нет. Но, мне кажется, здесь не стооит применять принцип разрешения всего, что не запрщено явно. С другой стоорны, я не встречал в своей практике ресурсов с двойным слешем. Смотрел, как Symfony относится к тому - не пропускает. Yii - пропускает. Для себя решил - не пропускать. В двойных слешах все-таки вижу неоднозначность. Если мы проверяем URI на соответствие /:module/:controller/:action/:params , то, в случае двойного слеша справедливо предположить, что пропущена какая-то часть адреса ресурса. Т.е. лучше отдать 404. Хотя такая адресация и возможна в файловой системе (Linux), Но, опять же, смысла в этом не вижу. Пустых (в смысле с пустым именем) директорий не встречал. Т.е. test/bar/foo == test///////bar///foo И в том, и вдругом случае, откроется маршрут test/bar/foo. Какой смысл в дублированных слешах?



8.1k

Тем временем проблема разяснилась. https://github.com/phalcon/cphalcon/issues/1561

Да, я тоже проверил и на апаче и на nginx еще до инициализации фалькона

<?php
// index.php
print_r($_GET);die;

Переменная _url нормализуется веб-сервером, а REQUEST_URI нет. Поэтому, да, фалькон здесь ни при чем. Буду использовать REQUEST_URI в качестве источника и отдавать 404 ошибку