Skip to content

Commit

Permalink
[DomCrawler] refactored URLs management in Link and Form
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Apr 23, 2011
1 parent 5591f34 commit 8b74c6e
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 204 deletions.
36 changes: 7 additions & 29 deletions src/Symfony/Component/DomCrawler/Crawler.php
Expand Up @@ -23,31 +23,20 @@
class Crawler extends \SplObjectStorage
{
private $uri;
private $host;
private $path;
private $base;

/**
* Constructor.
*
* @param mixed $node A Node to use as the base for the crawling
* @param string $uri The base URI to use for absolute links or form actions
* @param string $base An optional base href for generating the uris for Form and Link.
* This will be autodetected if $node has a <base> tag.
* @param string $uri The current URI or the base href value
*
* @api
*/
public function __construct($node = null, $uri = null, $base = null)
public function __construct($node = null, $uri = null)
{
$this->uri = $uri;

list($this->host, $this->path) = $this->parseUri($this->uri);

$this->add($node);

if ($base) {
$this->base = $base;
}
}

/**
Expand Down Expand Up @@ -131,7 +120,7 @@ public function addHtmlContent($content, $charset = 'UTF-8')
$base = $this->filter('base')->extract(array('href'));

if (count($base)) {
$this->base = current($base);
$this->uri = current($base);
}
}

Expand Down Expand Up @@ -498,7 +487,7 @@ public function filterXPath($xpath)

$domxpath = new \DOMXPath($document);

return new static($domxpath->query($xpath), $this->uri, $this->base);
return new static($domxpath->query($xpath), $this->uri);
}

/**
Expand Down Expand Up @@ -579,7 +568,7 @@ public function link($method = 'get')

$node = $this->getNode(0);

return new Link($node, $method, $this->host, $this->path, $this->base);
return new Link($node, $this->uri, $method);
}

/**
Expand All @@ -593,7 +582,7 @@ public function links()
{
$links = array();
foreach ($this as $node) {
$links[] = new Link($node, 'get', $this->host, $this->path);
$links[] = new Link($node, $this->uri, 'get');
}

return $links;
Expand All @@ -617,7 +606,7 @@ public function form(array $values = null, $method = null)
throw new \InvalidArgumentException('The current node list is empty.');
}

$form = new Form($this->getNode(0), $method, $this->host, $this->path, $this->base);
$form = new Form($this->getNode(0), $this->uri, $method);

if (null !== $values) {
$form->setValues($values);
Expand Down Expand Up @@ -665,17 +654,6 @@ private function getNode($position)
// @codeCoverageIgnoreEnd
}

private function parseUri($uri)
{
if ('http' !== substr($uri, 0, 4)) {
return array(null, '/');
}

$path = parse_url($uri, PHP_URL_PATH);

return array(preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $uri), $path);
}

private function sibling($node, $siblingDir = 'nextSibling')
{
$nodes = array();
Expand Down
66 changes: 20 additions & 46 deletions src/Symfony/Component/DomCrawler/Form.php
Expand Up @@ -20,31 +20,31 @@
*
* @api
*/
class Form implements \ArrayAccess
class Form extends Link implements \ArrayAccess
{
private $document;
private $button;
private $node;
private $fields;
private $method;
private $host;
private $path;
private $base;

/**
* Constructor.
*
* @param \DOMNode $node A \DOMNode instance
* @param string $method The method to use for the link (if null, it defaults to the method defined by the form)
* @param string $host The base URI to use for absolute links (like http://localhost)
* @param string $path The base path for relative links (/ by default)
* @param string $base An optional base href for generating the submit uri
* @param \DOMNode $node A \DOMNode instance
* @param string $currentUri The URI of the page where the form is embedded
* @param string $method The method to use for the link (if null, it defaults to the method defined by the form)
*
* @throws \LogicException if the node is not a button inside a form tag
*
* @api
*/
public function __construct(\DOMNode $node, $method = null, $host = null, $path = '/', $base = null)
public function __construct(\DOMNode $node, $currentUri, $method = null)
{
parent::__construct($node, $currentUri, $method);

$this->initialize();
}

protected function setNode(\DOMNode $node)
{
$this->button = $node;
if ('button' == $node->nodeName || ('input' == $node->nodeName && in_array($node->getAttribute('type'), array('submit', 'button', 'image')))) {
Expand All @@ -57,13 +57,8 @@ public function __construct(\DOMNode $node, $method = null, $host = null, $path
} else {
throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
}
$this->node = $node;
$this->method = $method;
$this->host = $host;
$this->path = empty($path) ? '/' : $path;
$this->base = $base;

$this->initialize();
$this->node = $node;
}

/**
Expand Down Expand Up @@ -179,48 +174,27 @@ public function getPhpFiles()
* This method merges the value if the method is GET to mimics
* browser behavior.
*
* @param Boolean $absolute Whether to return an absolute URI or not (this only works if a base URI has been provided)
*
* @return string The URI
*
* @api
*/
public function getUri($absolute = true)
public function getUri()
{
$uri = $this->node->getAttribute('action');
$urlHaveScheme = 'http' === substr($uri, 0, 4);

if (!$uri) {
$uri = $this->path;
}

if ('#' === $uri[0]) {
$uri = $this->path.$uri;
}
$uri = parent::getUri();

if (!in_array($this->getMethod(), array('post', 'put', 'delete')) && $queryString = http_build_query($this->getValues(), null, '&')) {
$sep = false === strpos($uri, '?') ? '?' : '&';
$uri .= $sep.$queryString;
}

$path = $this->path;
if ('?' !== substr($uri, 0, 1) && '/' !== substr($path, -1)) {
$path = substr($path, 0, strrpos($path, '/') + 1);
}

if (!$this->base && $uri && '/' !== $uri[0] && !$urlHaveScheme) {
$uri = $path.$uri;
} elseif ($this->base) {
$uri = $this->base.$uri;
}

if (!$this->base && $absolute && null !== $this->host && !$urlHaveScheme) {
return $this->host.$uri;
}

return $uri;
}

protected function getRawUri()
{
return $this->node->getAttribute('action');
}

/**
* Gets the form method.
*
Expand Down
74 changes: 38 additions & 36 deletions src/Symfony/Component/DomCrawler/Link.php
Expand Up @@ -20,36 +20,39 @@
*/
class Link
{
private $node;
private $method;
private $host;
private $path;
private $base;
protected $node;
protected $method;
protected $currentUri;

/**
* Constructor.
*
* @param \DOMNode $node A \DOMNode instance
* @param string $method The method to use for the link (get by default)
* @param string $host The base URI to use for absolute links (like http://localhost)
* @param string $path The base path for relative links (/ by default)
* @param string $base An optional base href for generating the uri
* @param \DOMNode $node A \DOMNode instance
* @param string $currentUri The URI of the page where the link is embedded (or the base href)
* @param string $method The method to use for the link (get by default)
*
* @throws \LogicException if the node is not a link
*
* @api
*/
public function __construct(\DOMNode $node, $method = 'get', $host = null, $path = '/', $base = null)
public function __construct(\DOMNode $node, $currentUri, $method = 'get')
{
if (!in_array(substr($currentUri, 0, 4), array('http', 'file'))) {
throw new \InvalidArgumentException(sprintf('Current URI must be an asbolute URL ("%s").', $currentUri));
}

$this->setNode($node);
$this->method = $method;
$this->currentUri = $currentUri;
}

protected function setNode(\DOMNode $node)
{
if ('a' != $node->nodeName) {
throw new \LogicException(sprintf('Unable to click on a "%s" tag.', $node->nodeName));
}

$this->node = $node;
$this->method = $method;
$this->host = $host;
$this->path = empty($path) ? '/' : $path;
$this->base = $base;
}

/**
Expand All @@ -65,42 +68,41 @@ public function getNode()
/**
* Gets the URI associated with this link.
*
* @param Boolean $absolute Whether to return an absolute URI or not (this only works if a base URI has been provided)
*
* @return string The URI
*
* @api
*/
public function getUri($absolute = true)
public function getUri()
{
$uri = $this->node->getAttribute('href');
$urlHaveScheme = 'http' === substr($uri, 0, 4);
$uri = $this->getRawUri();

if (!$uri) {
$uri = $this->path;
// absolute URL?
if ('http' === substr($uri, 0, 4)) {
return $uri;
}

if ('#' === $uri[0]) {
$uri = $this->path.$uri;
// empty URI
if (!$uri) {
return $this->currentUri;
}

$path = $this->path;
if ('?' !== substr($uri, 0, 1) && '/' !== substr($path, -1)) {
$path = substr($path, 0, strrpos($path, '/') + 1);
// only an anchor or a query string
if (in_array($uri[0], array('?', '#'))) {
return $this->currentUri.$uri;
}

if (!$this->base && $uri && '/' !== $uri[0] && !$urlHaveScheme) {
$uri = $path.$uri;
} elseif ($this->base) {
$uri = $this->base.$uri;
// absolute path
if ('/' === $uri[0]) {
return preg_replace('#^(.*?//[^/]+)(?:\/.*)?$#', '$1', $this->currentUri).$uri;
}

if (!$this->base && $absolute && null !== $this->host && !$urlHaveScheme) {

return $this->host.$uri;
}
// relative path
return substr($this->currentUri, 0, strrpos($this->currentUri, '/') + 1).$uri;
}

return $uri;
protected function getRawUri()
{
return $this->node->getAttribute('href');
}

/**
Expand Down
20 changes: 3 additions & 17 deletions tests/Symfony/Tests/Component/DomCrawler/CrawlerTest.php
Expand Up @@ -287,18 +287,11 @@ public function testSelectButton()

public function testLink()
{
$crawler = $this->createTestCrawler()->selectLink('Foo');
$crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo');
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $crawler->link(), '->link() returns a Link instance');

$this->assertEquals('/foo', $crawler->link()->getUri(), '->link() returns a Link instance');
$this->assertEquals('post', $crawler->link('post')->getMethod(), '->link() takes a method as its argument');

$crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo');
$this->assertEquals('http://example.com/bar/foo', $crawler->link()->getUri(), '->link() returns a Link instance');

$crawler = $this->createTestCrawler('http://example.com/bar')->selectLink('Foo');
$this->assertEquals('http://example.com/foo', $crawler->link()->getUri(), '->link() returns a Link instance');

$crawler = $this->createTestCrawler('http://example.com/bar')->selectLink('GetLink');
$this->assertEquals('http://example.com/bar?get=param', $crawler->link()->getUri(), '->link() returns a Link instance');

Expand All @@ -312,7 +305,7 @@ public function testLink()

public function testLinks()
{
$crawler = $this->createTestCrawler()->selectLink('Foo');
$crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo');
$this->assertInternalType('array', $crawler->links(), '->links() returns an array');

$this->assertEquals(4, count($crawler->links()), '->links() returns an array');
Expand All @@ -324,18 +317,11 @@ public function testLinks()

public function testForm()
{
$crawler = $this->createTestCrawler()->selectButton('FooValue');
$crawler = $this->createTestCrawler('http://example.com/bar/')->selectButton('FooValue');
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Form', $crawler->form(), '->form() returns a Form instance');

$this->assertEquals('/foo?FooName=FooValue', $crawler->form()->getUri(), '->form() returns a Form instance');
$this->assertEquals(array('FooName' => 'FooBar'), $crawler->form(array('FooName' => 'FooBar'))->getValues(), '->form() takes an array of values to submit as its first argument');

$crawler = $this->createTestCrawler('http://example.com/bar/')->selectButton('FooValue');
$this->assertEquals('http://example.com/bar/foo?FooName=FooValue', $crawler->form()->getUri(), '->form() returns a Form instance');

$crawler = $this->createTestCrawler('http://example.com/bar')->selectButton('FooValue');
$this->assertEquals('http://example.com/foo?FooName=FooValue', $crawler->form()->getUri(), '->form() returns a Form instance');

try {
$this->createTestCrawler()->filter('ol')->form();
$this->fail('->form() throws an \InvalidArgumentException if the node list is empty');
Expand Down

0 comments on commit 8b74c6e

Please sign in to comment.