Присоединить URL-адреса в symfony/goutte

У меня есть Goutte/Client (goutte использует symfony для запросов), и я хотел бы соединить пути и получить конечный URL:

$client = new Goutte\Client();
$crawler = $client->request('GET', 'http://DOMAIN/some/path/')
// $crawler is instance of Symfony\Component\DomCrawler\Crawler

$new_path = '../new_page';
$final path = $crawler->someMagicFunction($new_path);
// final path == http://DOMAIN/some/new_page

Я ищу простой способ присоединиться к переменной $new_path с текущей страницей из запроса и получить новый URL-адрес.

Обратите внимание, что $new_page может быть любым из:

new_page    ==> http://DOMAIN/some/path/new_page
../new_page ==> http://DOMAIN/some/new_page
/new_page   ==> http://DOMAIN/new_page

Предоставляет ли symfony/goutte/guzzle простой способ сделать это?

Я нашел getUriForPath из Symfony\Component\HttpFoundation\Request, но не не вижу простого способа преобразовать Symfony\Component\BrowserKit\Request в HttpFoundation\Request


person Dekel    schedule 27.11.2016    source источник
comment
вам действительно нужно канонизировать путь URL-адреса? guzzle должен без проблем обрабатывать запрос к http://DOMAIN/some/path/../new_page   -  person Federkun    schedule 27.11.2016
comment
Да, мне это нужно для каких-то других проверок (а не для конкретного запроса). Кроме того, если $new_page равно /new_page, у меня могут возникнуть проблемы с конечным URL.   -  person Dekel    schedule 27.11.2016


Ответы (2)


Вы можете использовать parse_url, чтобы получить путь к URL-адресу:

$components = parse_url('http://DOMAIN/some/path/');
$path = $components['path'];

тогда вам нужен способ канонизировать его. Этот ответ может помочь вам:

function normalizePath($path, $separator = '\\/')
{
    // Remove any kind of funky unicode whitespace
    $normalized = preg_replace('#\p{C}+|^\./#u', '', $path);

    // Path remove self referring paths ("/./").
    $normalized = preg_replace('#/\.(?=/)|^\./|\./$#', '', $normalized);

    // Regex for resolving relative paths
    $regex = '#\/*[^/\.]+/\.\.#Uu';

    while (preg_match($regex, $normalized)) {
        $normalized = preg_replace($regex, '', $normalized);
    }

    if (preg_match('#/\.{2}|\.{2}/#', $normalized)) {
        throw new LogicException('Path is outside of the defined root, path: [' . $path . '], resolved: [' . $normalized . ']');
    }

    return trim($normalized, $separator);
}

Все, что осталось сделать, это перестроить URL-адрес, вы можете увидеть этот комментарий< /а>:

function unparse_url($parsed_url) { 
    $scheme   = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; 
    $host     = isset($parsed_url['host']) ? $parsed_url['host'] : ''; 
    $port     = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; 
    $user     = isset($parsed_url['user']) ? $parsed_url['user'] : ''; 
    $pass     = isset($parsed_url['pass']) ? ':' . $parsed_url['pass']  : ''; 
    $pass     = ($user || $pass) ? "$pass@" : ''; 
    $path     = isset($parsed_url['path']) ? $parsed_url['path'] : ''; 
    $query    = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : ''; 
    $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : ''; 
    return "$scheme$user$pass$host$port/$path$query$fragment"; 
}

Конечный путь:

$new_path = '../new_page';

if (strpos($new_path, '/') === 0) { // absolute path, replace it entirely
    $path = $new_path;
} else { // relative path, append it
    $path = $path . $new_path;
}

Поставить все это вместе:

// http://DOMAIN/some/new_page
echo unparse_url(array_replace($components, array('path' => normalizePath($path))));
person Federkun    schedule 27.11.2016
comment
Спасибо за ответ, я надеялся, что Symfony даст более простое решение для этого. Надеюсь, вы не возражаете - я подожду еще немного, прежде чем отметить это как правильный ответ, возможно, у кого-то будет лучшее решение. - person Dekel; 27.11.2016
comment
Я не уверен, как вы обрабатываете http://example.org/page/, объединенный с /new_page (где конечный URL должен быть http://example.org/new_page). Вы можете объяснить? - person Dekel; 28.11.2016
comment
Последний пример (echo resolveUrl('http://example.org/page/', '/new_page'), "\n";) дает http://example.org/page вместо http://example.org/new_page. - person Dekel; 28.11.2016

Используйте Uri::resolve() из пакета guzzlehttp/prs7. Этот метод позволяет создать нормализованный URL-адрес из базовой и относительной частей.

Пример (с использованием отличной оболочки psysh):

Psy Shell v0.7.2 (PHP 7.0.12 — cli) by Justin Hileman
>>> $base = new GuzzleHttp\Psr7\Uri('http://example.com/some/dir')
=> GuzzleHttp\Psr7\Uri {#208}
>>> (string) GuzzleHttp\Psr7\Uri::resolve($base, '/new_base/next/next/../../back_2')
=> "http://example.com/new_base/back_2"

Также взгляните на класс UriNormalizer. Существует пример (тестовый набор), который связано с вашей проблемой.

Из тестового примера:

$uri = new Uri('http://example.org/../a/b/../c/./d.html');
$normalizedUri = UriNormalizer::normalize($uri, UriNormalizer::REMOVE_DOT_SEGMENTS);

$this->assertSame('http://example.org/a/c/d.html', (string) $normalizedUri);
person Alexey Shokov    schedule 28.11.2016
comment
Я не уверен, как вы обрабатываете http://example.org/page/, объединенный с /new_page (где конечный URL должен быть http://example.org/new_page). Вы можете объяснить? - person Dekel; 28.11.2016
comment
Ты прав. Только что обновил ответ на правильное решение с помощью guzzlehttp/prs7. - person Alexey Shokov; 28.11.2016
comment
Спасибо. Похоже, что версия goutte, которую я использую, немного устарела и не имеет последней версии guzzle (в которой есть prs7 и UriResolve). Но вы получили мой голос здесь :) Еще раз спасибо за вашу помощь! - person Dekel; 28.11.2016
comment
Пожалуйста. Просто установите пакет psr7 отдельно, он не зависит от нового Guzzle :) Так что вы можете использовать свой текущий Goutte и пакет psr7. - person Alexey Shokov; 28.11.2016