diff --git a/AbstractUriElement.php b/AbstractUriElement.php
index d602d6f3..7aba8612 100644
--- a/AbstractUriElement.php
+++ b/AbstractUriElement.php
@@ -42,7 +42,7 @@ abstract class AbstractUriElement
*/
public function __construct(\DOMElement $node, $currentUri, $method = 'GET')
{
- if (!in_array(strtolower(substr($currentUri, 0, 4)), array('http', 'file'))) {
+ if (!\in_array(strtolower(substr($currentUri, 0, 4)), ['http', 'file'])) {
throw new \InvalidArgumentException(sprintf('Current URI must be an absolute URL ("%s").', $currentUri));
}
@@ -114,7 +114,7 @@ public function getUri()
}
// relative path
- $path = parse_url(substr($this->currentUri, strlen($baseUri)), PHP_URL_PATH);
+ $path = parse_url(substr($this->currentUri, \strlen($baseUri)), PHP_URL_PATH);
$path = $this->canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri);
return $baseUri.('' === $path || '/' !== $path[0] ? '/' : '').$path;
@@ -144,7 +144,7 @@ protected function canonicalizePath($path)
$path .= '/';
}
- $output = array();
+ $output = [];
foreach (explode('/', $path) as $segment) {
if ('..' === $segment) {
diff --git a/Crawler.php b/Crawler.php
index 316d0083..9a0a46e1 100644
--- a/Crawler.php
+++ b/Crawler.php
@@ -20,9 +20,6 @@
*/
class Crawler implements \Countable, \IteratorAggregate
{
- /**
- * @var string The current URI
- */
protected $uri;
/**
@@ -33,7 +30,7 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* @var array A map of manually registered namespaces
*/
- private $namespaces = array();
+ private $namespaces = [];
/**
* @var string The base href value
@@ -46,9 +43,9 @@ class Crawler implements \Countable, \IteratorAggregate
private $document;
/**
- * @var \DOMElement[]
+ * @var \DOMNode[]
*/
- private $nodes = array();
+ private $nodes = [];
/**
* Whether the Crawler contains HTML or XML content (used when converting CSS to XPath).
@@ -58,14 +55,12 @@ class Crawler implements \Countable, \IteratorAggregate
private $isHtml = true;
/**
- * @param mixed $node A Node to use as the base for the crawling
- * @param string $currentUri The current URI
- * @param string $baseHref The base href value
+ * @param \DOMNodeList|\DOMNode|\DOMNode[]|string|null $node A Node to use as the base for the crawling
*/
- public function __construct($node = null, $currentUri = null, $baseHref = null)
+ public function __construct($node = null, $uri = null, $baseHref = null)
{
- $this->uri = $currentUri;
- $this->baseHref = $baseHref ?: $currentUri;
+ $this->uri = $uri;
+ $this->baseHref = $baseHref ?: $uri;
$this->add($node);
}
@@ -95,7 +90,7 @@ public function getBaseHref()
*/
public function clear()
{
- $this->nodes = array();
+ $this->nodes = [];
$this->document = null;
}
@@ -105,9 +100,9 @@ public function clear()
* This method uses the appropriate specialized add*() method based
* on the type of the argument.
*
- * @param \DOMNodeList|\DOMNode|array|string|null $node A node
+ * @param \DOMNodeList|\DOMNode|\DOMNode[]|string|null $node A node
*
- * @throws \InvalidArgumentException When node is not the expected type.
+ * @throws \InvalidArgumentException when node is not the expected type
*/
public function add($node)
{
@@ -115,24 +110,24 @@ public function add($node)
$this->addNodeList($node);
} elseif ($node instanceof \DOMNode) {
$this->addNode($node);
- } elseif (is_array($node)) {
+ } elseif (\is_array($node)) {
$this->addNodes($node);
- } elseif (is_string($node)) {
+ } elseif (\is_string($node)) {
$this->addContent($node);
} elseif (null !== $node) {
- throw new \InvalidArgumentException(sprintf('Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "%s".', is_object($node) ? get_class($node) : gettype($node)));
+ throw new \InvalidArgumentException(sprintf('Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "%s".', \is_object($node) ? \get_class($node) : \gettype($node)));
}
}
/**
* Adds HTML/XML content.
*
- * If the charset is not set via the content type, it is assumed
- * to be ISO-8859-1, which is the default charset defined by the
+ * If the charset is not set via the content type, it is assumed to be UTF-8,
+ * or ISO-8859-1 as a fallback, which is the default charset defined by the
* HTTP 1.1 specification.
*
* @param string $content A string to parse as HTML/XML
- * @param null|string $type The content type of the string
+ * @param string|null $type The content type of the string
*/
public function addContent($content, $type = null)
{
@@ -161,7 +156,7 @@ public function addContent($content, $type = null)
}
if (null === $charset) {
- $charset = 'ISO-8859-1';
+ $charset = preg_match('//u', $content) ? 'UTF-8' : 'ISO-8859-1';
}
if ('x' === $xmlMatches[1]) {
@@ -187,34 +182,39 @@ public function addContent($content, $type = null)
public function addHtmlContent($content, $charset = 'UTF-8')
{
$internalErrors = libxml_use_internal_errors(true);
- $disableEntities = libxml_disable_entity_loader(true);
+ if (LIBXML_VERSION < 20900) {
+ $disableEntities = libxml_disable_entity_loader(true);
+ }
$dom = new \DOMDocument('1.0', $charset);
$dom->validateOnParse = true;
- set_error_handler(function () {throw new \Exception();});
+ set_error_handler(function () { throw new \Exception(); });
try {
// Convert charset to HTML-entities to work around bugs in DOMDocument::loadHTML()
$content = mb_convert_encoding($content, 'HTML-ENTITIES', $charset);
} catch (\Exception $e) {
+ } catch (\ValueError $e) {
+ } finally {
+ restore_error_handler();
}
- restore_error_handler();
-
if ('' !== trim($content)) {
@$dom->loadHTML($content);
}
libxml_use_internal_errors($internalErrors);
- libxml_disable_entity_loader($disableEntities);
+ if (LIBXML_VERSION < 20900) {
+ libxml_disable_entity_loader($disableEntities);
+ }
$this->addDocument($dom);
- $base = $this->filterRelativeXPath('descendant-or-self::base')->extract(array('href'));
+ $base = $this->filterRelativeXPath('descendant-or-self::base')->extract(['href']);
$baseHref = current($base);
- if (count($base) && !empty($baseHref)) {
+ if (\count($base) && !empty($baseHref)) {
if ($this->baseHref) {
$linkNode = $dom->createElement('a');
$linkNode->setAttribute('href', $baseHref);
@@ -250,7 +250,9 @@ public function addXmlContent($content, $charset = 'UTF-8', $options = LIBXML_NO
}
$internalErrors = libxml_use_internal_errors(true);
- $disableEntities = libxml_disable_entity_loader(true);
+ if (LIBXML_VERSION < 20900) {
+ $disableEntities = libxml_disable_entity_loader(true);
+ }
$dom = new \DOMDocument('1.0', $charset);
$dom->validateOnParse = true;
@@ -260,7 +262,9 @@ public function addXmlContent($content, $charset = 'UTF-8', $options = LIBXML_NO
}
libxml_use_internal_errors($internalErrors);
- libxml_disable_entity_loader($disableEntities);
+ if (LIBXML_VERSION < 20900) {
+ libxml_disable_entity_loader($disableEntities);
+ }
$this->addDocument($dom);
@@ -325,7 +329,7 @@ public function addNode(\DOMNode $node)
}
// Don't add duplicate nodes in the Crawler
- if (in_array($node, $this->nodes, true)) {
+ if (\in_array($node, $this->nodes, true)) {
return;
}
@@ -337,7 +341,7 @@ public function addNode(\DOMNode $node)
*
* @param int $position The position
*
- * @return Crawler A new instance of the Crawler with the selected node, or an empty Crawler if it does not exist
+ * @return static
*/
public function eq($position)
{
@@ -366,7 +370,7 @@ public function eq($position)
*/
public function each(\Closure $closure)
{
- $data = array();
+ $data = [];
foreach ($this->nodes as $i => $node) {
$data[] = $closure($this->createSubCrawler($node), $i);
}
@@ -380,11 +384,11 @@ public function each(\Closure $closure)
* @param int $offset
* @param int $length
*
- * @return Crawler A Crawler instance with the sliced nodes
+ * @return static
*/
public function slice($offset = 0, $length = null)
{
- return $this->createSubCrawler(array_slice($this->nodes, $offset, $length));
+ return $this->createSubCrawler(\array_slice($this->nodes, $offset, $length));
}
/**
@@ -394,11 +398,11 @@ public function slice($offset = 0, $length = null)
*
* @param \Closure $closure An anonymous function
*
- * @return Crawler A Crawler instance with the selected nodes
+ * @return static
*/
public function reduce(\Closure $closure)
{
- $nodes = array();
+ $nodes = [];
foreach ($this->nodes as $i => $node) {
if (false !== $closure($this->createSubCrawler($node), $i)) {
$nodes[] = $node;
@@ -411,7 +415,7 @@ public function reduce(\Closure $closure)
/**
* Returns the first node of the current selection.
*
- * @return Crawler A Crawler instance with the first selected node
+ * @return static
*/
public function first()
{
@@ -421,17 +425,17 @@ public function first()
/**
* Returns the last node of the current selection.
*
- * @return Crawler A Crawler instance with the last selected node
+ * @return static
*/
public function last()
{
- return $this->eq(count($this->nodes) - 1);
+ return $this->eq(\count($this->nodes) - 1);
}
/**
* Returns the siblings nodes of the current selection.
*
- * @return Crawler A Crawler instance with the sibling nodes
+ * @return static
*
* @throws \InvalidArgumentException When current node is empty
*/
@@ -447,7 +451,7 @@ public function siblings()
/**
* Returns the next siblings nodes of the current selection.
*
- * @return Crawler A Crawler instance with the next sibling nodes
+ * @return static
*
* @throws \InvalidArgumentException When current node is empty
*/
@@ -463,7 +467,7 @@ public function nextAll()
/**
* Returns the previous sibling nodes of the current selection.
*
- * @return Crawler A Crawler instance with the previous sibling nodes
+ * @return static
*
* @throws \InvalidArgumentException
*/
@@ -479,7 +483,7 @@ public function previousAll()
/**
* Returns the parents nodes of the current selection.
*
- * @return Crawler A Crawler instance with the parents nodes of the current selection
+ * @return static
*
* @throws \InvalidArgumentException When current node is empty
*/
@@ -490,7 +494,7 @@ public function parents()
}
$node = $this->getNode(0);
- $nodes = array();
+ $nodes = [];
while ($node = $node->parentNode) {
if (XML_ELEMENT_NODE === $node->nodeType) {
@@ -504,7 +508,7 @@ public function parents()
/**
* Returns the children nodes of the current selection.
*
- * @return Crawler A Crawler instance with the children nodes
+ * @return static
*
* @throws \InvalidArgumentException When current node is empty
*/
@@ -516,7 +520,7 @@ public function children()
$node = $this->getNode(0)->firstChild;
- return $this->createSubCrawler($node ? $this->sibling($node) : array());
+ return $this->createSubCrawler($node ? $this->sibling($node) : []);
}
/**
@@ -592,6 +596,36 @@ public function html()
return $html;
}
+ /**
+ * Evaluates an XPath expression.
+ *
+ * Since an XPath expression might evaluate to either a simple type or a \DOMNodeList,
+ * this method will return either an array of simple types or a new Crawler instance.
+ *
+ * @param string $xpath An XPath expression
+ *
+ * @return array|Crawler An array of evaluation results or a new Crawler instance
+ */
+ public function evaluate($xpath)
+ {
+ if (null === $this->document) {
+ throw new \LogicException('Cannot evaluate the expression on an uninitialized crawler.');
+ }
+
+ $data = [];
+ $domxpath = $this->createDOMXPath($this->document, $this->findNamespacePrefixes($xpath));
+
+ foreach ($this->nodes as $node) {
+ $data[] = $domxpath->evaluate($xpath, $node);
+ }
+
+ if (isset($data[0]) && $data[0] instanceof \DOMNodeList) {
+ return $this->createSubCrawler($data);
+ }
+
+ return $data;
+ }
+
/**
* Extracts information from the list of nodes.
*
@@ -599,7 +633,7 @@ public function html()
*
* Example:
*
- * $crawler->filter('h1 a')->extract(array('_text', 'href'));
+ * $crawler->filter('h1 a')->extract(['_text', 'href']);
*
* @param array $attributes An array of attributes
*
@@ -608,11 +642,11 @@ public function html()
public function extract($attributes)
{
$attributes = (array) $attributes;
- $count = count($attributes);
+ $count = \count($attributes);
- $data = array();
+ $data = [];
foreach ($this->nodes as $node) {
- $elements = array();
+ $elements = [];
foreach ($attributes as $attribute) {
if ('_text' === $attribute) {
$elements[] = $node->nodeValue;
@@ -621,7 +655,7 @@ public function extract($attributes)
}
}
- $data[] = $count > 1 ? $elements : $elements[0];
+ $data[] = 1 === $count ? $elements[0] : $elements;
}
return $data;
@@ -637,7 +671,7 @@ public function extract($attributes)
*
* @param string $xpath An XPath expression
*
- * @return Crawler A new instance of Crawler with the filtered list of nodes
+ * @return static
*/
public function filterXPath($xpath)
{
@@ -658,14 +692,14 @@ public function filterXPath($xpath)
*
* @param string $selector A CSS selector
*
- * @return Crawler A new instance of Crawler with the filtered list of nodes
+ * @return static
*
* @throws \RuntimeException if the CssSelector Component is not available
*/
public function filter($selector)
{
- if (!class_exists('Symfony\\Component\\CssSelector\\CssSelectorConverter')) {
- throw new \RuntimeException('Unable to filter with a CSS selector as the Symfony CssSelector 2.8+ is not installed (you can use filterXPath instead).');
+ if (!class_exists(CssSelectorConverter::class)) {
+ throw new \RuntimeException('To filter with a CSS selector, install the CssSelector component ("composer require symfony/css-selector"). Or use filterXpath instead.');
}
$converter = new CssSelectorConverter($this->isHtml);
@@ -679,7 +713,7 @@ public function filter($selector)
*
* @param string $value The link text
*
- * @return Crawler A new instance of Crawler with the filtered list of nodes
+ * @return static
*/
public function selectLink($value)
{
@@ -694,7 +728,7 @@ public function selectLink($value)
*
* @param string $value The image alt
*
- * @return Crawler A new instance of Crawler with the filtered list of nodes
+ * @return static A new instance of Crawler with the filtered list of nodes
*/
public function selectImage($value)
{
@@ -708,12 +742,12 @@ public function selectImage($value)
*
* @param string $value The button text
*
- * @return Crawler A new instance of Crawler with the filtered list of nodes
+ * @return static
*/
public function selectButton($value)
{
$translate = 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")';
- $xpath = sprintf('descendant-or-self::input[((contains(%s, "submit") or contains(%s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', $translate, $translate, static::xpathLiteral(' '.$value.' ')).
+ $xpath = sprintf('descendant-or-self::input[((contains(%s, "submit") or contains(%1$s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %s)) ', $translate, static::xpathLiteral(' '.$value.' ')).
sprintf('or (contains(%s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %s)) or @id=%s or @name=%s] ', $translate, static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value)).
sprintf('| descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %s) or @id=%s or @name=%s]', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value), static::xpathLiteral($value));
@@ -738,7 +772,7 @@ public function link($method = 'get')
$node = $this->getNode(0);
if (!$node instanceof \DOMElement) {
- throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_class($node)));
+ throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', \get_class($node)));
}
return new Link($node, $this->baseHref, $method);
@@ -753,10 +787,10 @@ public function link($method = 'get')
*/
public function links()
{
- $links = array();
+ $links = [];
foreach ($this->nodes as $node) {
if (!$node instanceof \DOMElement) {
- throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', get_class($node)));
+ throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', \get_class($node)));
}
$links[] = new Link($node, $this->baseHref, 'get');
@@ -774,14 +808,14 @@ public function links()
*/
public function image()
{
- if (!count($this)) {
+ if (!\count($this)) {
throw new \InvalidArgumentException('The current node list is empty.');
}
$node = $this->getNode(0);
if (!$node instanceof \DOMElement) {
- throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_class($node)));
+ throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', \get_class($node)));
}
return new Image($node, $this->baseHref);
@@ -794,10 +828,10 @@ public function image()
*/
public function images()
{
- $images = array();
+ $images = [];
foreach ($this as $node) {
if (!$node instanceof \DOMElement) {
- throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', get_class($node)));
+ throw new \InvalidArgumentException(sprintf('The current node list should contain only DOMElement instances, "%s" found.', \get_class($node)));
}
$images[] = new Image($node, $this->baseHref);
@@ -825,7 +859,7 @@ public function form(array $values = null, $method = null)
$node = $this->getNode(0);
if (!$node instanceof \DOMElement) {
- throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_class($node)));
+ throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', \get_class($node)));
}
$form = new Form($node, $this->uri, $method, $this->baseHref);
@@ -862,7 +896,7 @@ public function registerNamespace($prefix, $namespace)
* Escaped characters are: quotes (") and apostrophe (').
*
* Examples:
- *
+ *
* echo Crawler::xpathLiteral('foo " bar');
* //prints 'foo " bar'
*
@@ -871,7 +905,6 @@ public function registerNamespace($prefix, $namespace)
*
* echo Crawler::xpathLiteral('a\'b"c');
* //prints concat('a', "'", 'b"c')
- *
*
* @param string $s String to be escaped
*
@@ -888,7 +921,7 @@ public static function xpathLiteral($s)
}
$string = $s;
- $parts = array();
+ $parts = [];
while (true) {
if (false !== $pos = strpos($string, "'")) {
$parts[] = sprintf("'%s'", substr($string, 0, $pos));
@@ -900,7 +933,7 @@ public static function xpathLiteral($s)
}
}
- return sprintf('concat(%s)', implode($parts, ', '));
+ return sprintf('concat(%s)', implode(', ', $parts));
}
/**
@@ -910,7 +943,7 @@ public static function xpathLiteral($s)
*
* @param string $xpath
*
- * @return Crawler
+ * @return static
*/
private function filterRelativeXPath($xpath)
{
@@ -938,31 +971,56 @@ private function filterRelativeXPath($xpath)
*/
private function relativize($xpath)
{
- $expressions = array();
+ $expressions = [];
- $unionPattern = '/\|(?![^\[]*\])/';
// An expression which will never match to replace expressions which cannot match in the crawler
- // We cannot simply drop
+ // We cannot drop
$nonMatchingExpression = 'a[name() = "b"]';
- // Split any unions into individual expressions.
- foreach (preg_split($unionPattern, $xpath) as $expression) {
- $expression = trim($expression);
- $parenthesis = '';
+ $xpathLen = \strlen($xpath);
+ $openedBrackets = 0;
+ $startPosition = strspn($xpath, " \t\n\r\0\x0B");
+
+ for ($i = $startPosition; $i <= $xpathLen; ++$i) {
+ $i += strcspn($xpath, '"\'[]|', $i);
+
+ if ($i < $xpathLen) {
+ switch ($xpath[$i]) {
+ case '"':
+ case "'":
+ if (false === $i = strpos($xpath, $xpath[$i], $i + 1)) {
+ return $xpath; // The XPath expression is invalid
+ }
+ continue 2;
+ case '[':
+ ++$openedBrackets;
+ continue 2;
+ case ']':
+ --$openedBrackets;
+ continue 2;
+ }
+ }
+ if ($openedBrackets) {
+ continue;
+ }
- // If the union is inside some braces, we need to preserve the opening braces and apply
- // the change only inside it.
- if (preg_match('/^[\(\s*]+/', $expression, $matches)) {
- $parenthesis = $matches[0];
- $expression = substr($expression, strlen($parenthesis));
+ if ($startPosition < $xpathLen && '(' === $xpath[$startPosition]) {
+ // If the union is inside some braces, we need to preserve the opening braces and apply
+ // the change only inside it.
+ $j = 1 + strspn($xpath, "( \t\n\r\0\x0B", $startPosition + 1);
+ $parenthesis = substr($xpath, $startPosition, $j);
+ $startPosition += $j;
+ } else {
+ $parenthesis = '';
}
+ $expression = rtrim(substr($xpath, $startPosition, $i - $startPosition));
if (0 === strpos($expression, 'self::*/')) {
$expression = './'.substr($expression, 8);
}
// add prefix before absolute element selector
- if (empty($expression)) {
+ if ('' === $expression) {
$expression = $nonMatchingExpression;
} elseif (0 === strpos($expression, '//')) {
$expression = 'descendant-or-self::'.substr($expression, 2);
@@ -975,7 +1033,7 @@ private function relativize($xpath)
} elseif ('/' === $expression[0] || '.' === $expression[0] || 0 === strpos($expression, 'self::')) {
$expression = $nonMatchingExpression;
} elseif (0 === strpos($expression, 'descendant::')) {
- $expression = 'descendant-or-self::'.substr($expression, strlen('descendant::'));
+ $expression = 'descendant-or-self::'.substr($expression, 12);
} elseif (preg_match('/^(ancestor|ancestor-or-self|attribute|following|following-sibling|namespace|parent|preceding|preceding-sibling)::/', $expression)) {
// the fake root has no parent, preceding or following nodes and also no attributes (even no namespace attributes)
$expression = $nonMatchingExpression;
@@ -983,21 +1041,26 @@ private function relativize($xpath)
$expression = 'self::'.$expression;
}
$expressions[] = $parenthesis.$expression;
+
+ if ($i === $xpathLen) {
+ return implode(' | ', $expressions);
+ }
+
+ $i += strspn($xpath, " \t\n\r\0\x0B", $i + 1);
+ $startPosition = $i + 1;
}
- return implode(' | ', $expressions);
+ return $xpath; // The XPath expression is invalid
}
/**
* @param int $position
*
- * @return \DOMElement|null
+ * @return \DOMNode|null
*/
public function getNode($position)
{
- if (isset($this->nodes[$position])) {
- return $this->nodes[$position];
- }
+ return isset($this->nodes[$position]) ? $this->nodes[$position] : null;
}
/**
@@ -1005,11 +1068,11 @@ public function getNode($position)
*/
public function count()
{
- return count($this->nodes);
+ return \count($this->nodes);
}
/**
- * @return \ArrayIterator
+ * @return \ArrayIterator|\DOMNode[]
*/
public function getIterator()
{
@@ -1024,10 +1087,10 @@ public function getIterator()
*/
protected function sibling($node, $siblingDir = 'nextSibling')
{
- $nodes = array();
+ $nodes = [];
do {
- if ($node !== $this->getNode(0) && $node->nodeType === 1) {
+ if ($node !== $this->getNode(0) && 1 === $node->nodeType) {
$nodes[] = $node;
}
} while ($node = $node->$siblingDir);
@@ -1036,14 +1099,11 @@ protected function sibling($node, $siblingDir = 'nextSibling')
}
/**
- * @param \DOMDocument $document
- * @param array $prefixes
- *
* @return \DOMXPath
*
* @throws \InvalidArgumentException
*/
- private function createDOMXPath(\DOMDocument $document, array $prefixes = array())
+ private function createDOMXPath(\DOMDocument $document, array $prefixes = [])
{
$domxpath = new \DOMXPath($document);
@@ -1058,10 +1118,9 @@ private function createDOMXPath(\DOMDocument $document, array $prefixes = array(
}
/**
- * @param \DOMXPath $domxpath
- * @param string $prefix
+ * @param string $prefix
*
- * @return string
+ * @return string|null
*
* @throws \InvalidArgumentException
*/
@@ -1074,9 +1133,7 @@ private function discoverNamespace(\DOMXPath $domxpath, $prefix)
// ask for one namespace, otherwise we'd get a collection with an item for each node
$namespaces = $domxpath->query(sprintf('(//namespace::*[name()="%s"])[last()]', $this->defaultNamespacePrefix === $prefix ? '' : $prefix));
- if ($node = $namespaces->item(0)) {
- return $node->nodeValue;
- }
+ return ($node = $namespaces->item(0)) ? $node->nodeValue : null;
}
/**
@@ -1090,13 +1147,13 @@ private function findNamespacePrefixes($xpath)
return array_unique($matches['prefix']);
}
- return array();
+ return [];
}
/**
* Creates a crawler for some subnodes.
*
- * @param \DOMElement|\DOMElement[]|\DOMNodeList|null $nodes
+ * @param \DOMNodeList|\DOMNode|\DOMNode[]|string|null $nodes
*
* @return static
*/
diff --git a/Field/ChoiceFormField.php b/Field/ChoiceFormField.php
index c429417c..a7d2b847 100644
--- a/Field/ChoiceFormField.php
+++ b/Field/ChoiceFormField.php
@@ -45,7 +45,7 @@ class ChoiceFormField extends FormField
public function hasValue()
{
// don't send a value for unchecked checkboxes
- if (in_array($this->type, array('checkbox', 'radio')) && null === $this->value) {
+ if (\in_array($this->type, ['checkbox', 'radio']) && null === $this->value) {
return false;
}
@@ -75,7 +75,7 @@ public function isDisabled()
/**
* Sets the value of the field.
*
- * @param string $value The value of the field
+ * @param string|array $value The value of the field
*/
public function select($value)
{
@@ -97,14 +97,14 @@ public function tick()
}
/**
- * Ticks a checkbox.
+ * Unticks a checkbox.
*
* @throws \LogicException When the type provided is not correct
*/
public function untick()
{
if ('checkbox' !== $this->type) {
- throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
+ throw new \LogicException(sprintf('You cannot untick "%s" as it is not a checkbox (%s).', $this->name, $this->type));
}
$this->setValue(false);
@@ -113,7 +113,7 @@ public function untick()
/**
* Sets the value of the field.
*
- * @param string $value The value of the field
+ * @param string|array|bool $value The value of the field
*
* @throws \InvalidArgumentException When value type provided is not correct
*/
@@ -126,25 +126,25 @@ public function setValue($value)
// check
$this->value = $this->options[0]['value'];
} else {
- if (is_array($value)) {
+ if (\is_array($value)) {
if (!$this->multiple) {
throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name));
}
foreach ($value as $v) {
if (!$this->containsOption($v, $this->options)) {
- throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $v, implode(', ', $this->availableOptionValues())));
+ throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: "%s").', $this->name, $v, implode('", "', $this->availableOptionValues())));
}
}
} elseif (!$this->containsOption($value, $this->options)) {
- throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: %s).', $this->name, $value, implode(', ', $this->availableOptionValues())));
+ throw new \InvalidArgumentException(sprintf('Input "%s" cannot take "%s" as a value (possible values: "%s").', $this->name, $value, implode('", "', $this->availableOptionValues())));
}
if ($this->multiple) {
$value = (array) $value;
}
- if (is_array($value)) {
+ if (\is_array($value)) {
$this->value = $value;
} else {
parent::setValue($value);
@@ -155,11 +155,9 @@ public function setValue($value)
/**
* Adds a choice to the current ones.
*
- * This method should only be used internally.
- *
- * @param \DOMElement $node
- *
* @throws \LogicException When choice provided is not multiple nor radio
+ *
+ * @internal
*/
public function addChoice(\DOMElement $node)
{
@@ -207,11 +205,11 @@ protected function initialize()
}
if ('input' === $this->node->nodeName && 'checkbox' !== strtolower($this->node->getAttribute('type')) && 'radio' !== strtolower($this->node->getAttribute('type'))) {
- throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is %s).', $this->node->getAttribute('type')));
+ throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input tag with a type of checkbox or radio (given type is "%s").', $this->node->getAttribute('type')));
}
$this->value = null;
- $this->options = array();
+ $this->options = [];
$this->multiple = false;
if ('input' == $this->node->nodeName) {
@@ -226,7 +224,7 @@ protected function initialize()
$this->type = 'select';
if ($this->node->hasAttribute('multiple')) {
$this->multiple = true;
- $this->value = array();
+ $this->value = [];
$this->name = str_replace('[]', '', $this->name);
}
@@ -255,13 +253,11 @@ protected function initialize()
/**
* Returns option value with associated disabled flag.
*
- * @param \DOMElement $node
- *
* @return array
*/
private function buildOptionValue(\DOMElement $node)
{
- $option = array();
+ $option = [];
$defaultDefaultValue = 'select' === $this->node->nodeName ? '' : 'on';
$defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : $defaultDefaultValue;
@@ -301,7 +297,7 @@ public function containsOption($optionValue, $options)
*/
public function availableOptionValues()
{
- $values = array();
+ $values = [];
foreach ($this->options as $option) {
$values[] = $option['value'];
diff --git a/Field/FileFormField.php b/Field/FileFormField.php
index 0e0f9434..61bc7c68 100644
--- a/Field/FileFormField.php
+++ b/Field/FileFormField.php
@@ -27,12 +27,12 @@ class FileFormField extends FormField
*/
public function setErrorCode($error)
{
- $codes = array(UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION);
- if (!in_array($error, $codes)) {
- throw new \InvalidArgumentException(sprintf('The error code %s is not valid.', $error));
+ $codes = [UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, UPLOAD_ERR_EXTENSION];
+ if (!\in_array($error, $codes)) {
+ throw new \InvalidArgumentException(sprintf('The error code "%s" is not valid.', $error));
}
- $this->value = array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0);
+ $this->value = ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0];
}
/**
@@ -48,7 +48,7 @@ public function upload($value)
/**
* Sets the value of the field.
*
- * @param string $value The value of the field
+ * @param string|null $value The value of the field
*/
public function setValue($value)
{
@@ -59,8 +59,8 @@ public function setValue($value)
$name = $info['basename'];
// copy to a tmp location
- $tmp = sys_get_temp_dir().'/'.sha1(uniqid(mt_rand(), true));
- if (array_key_exists('extension', $info)) {
+ $tmp = sys_get_temp_dir().'/'.strtr(substr(base64_encode(hash('sha256', uniqid(mt_rand(), true), true)), 0, 7), '/', '_');
+ if (\array_key_exists('extension', $info)) {
$tmp .= '.'.$info['extension'];
}
if (is_file($tmp)) {
@@ -75,7 +75,7 @@ public function setValue($value)
$value = '';
}
- $this->value = array('name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size);
+ $this->value = ['name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size];
}
/**
@@ -100,7 +100,7 @@ protected function initialize()
}
if ('file' !== strtolower($this->node->getAttribute('type'))) {
- throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is %s).', $this->node->getAttribute('type')));
+ throw new \LogicException(sprintf('A FileFormField can only be created from an input tag with a type of file (given type is "%s").', $this->node->getAttribute('type')));
}
$this->setValue(null);
diff --git a/Field/FormField.php b/Field/FormField.php
index a6b33ded..0bc4f544 100644
--- a/Field/FormField.php
+++ b/Field/FormField.php
@@ -44,8 +44,6 @@ abstract class FormField
protected $disabled;
/**
- * Constructor.
- *
* @param \DOMElement $node The node associated with this field
*/
public function __construct(\DOMElement $node)
@@ -57,6 +55,27 @@ public function __construct(\DOMElement $node)
$this->initialize();
}
+ /**
+ * Returns the label tag associated to the field or null if none.
+ *
+ * @return \DOMElement|null
+ */
+ public function getLabel()
+ {
+ $xpath = new \DOMXPath($this->node->ownerDocument);
+
+ if ($this->node->hasAttribute('id')) {
+ $labels = $xpath->query(sprintf('descendant::label[@for="%s"]', $this->node->getAttribute('id')));
+ if ($labels->length > 0) {
+ return $labels->item(0);
+ }
+ }
+
+ $labels = $xpath->query('ancestor::label[1]', $this->node);
+
+ return $labels->length > 0 ? $labels->item(0) : null;
+ }
+
/**
* Returns the name of the field.
*
@@ -80,7 +99,7 @@ public function getValue()
/**
* Sets the value of the field.
*
- * @param string $value The value of the field
+ * @param string|array|bool|null $value The value of the field
*/
public function setValue($value)
{
diff --git a/Field/InputFormField.php b/Field/InputFormField.php
index 090913ef..1c3c84d7 100644
--- a/Field/InputFormField.php
+++ b/Field/InputFormField.php
@@ -32,11 +32,12 @@ protected function initialize()
throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName));
}
- if ('checkbox' === strtolower($this->node->getAttribute('type'))) {
+ $type = strtolower($this->node->getAttribute('type'));
+ if ('checkbox' === $type) {
throw new \LogicException('Checkboxes should be instances of ChoiceFormField.');
}
- if ('file' === strtolower($this->node->getAttribute('type'))) {
+ if ('file' === $type) {
throw new \LogicException('File inputs should be instances of FileFormField.');
}
diff --git a/Form.php b/Form.php
index 0390e3fc..8a6e28ca 100644
--- a/Form.php
+++ b/Form.php
@@ -37,8 +37,6 @@ class Form extends Link implements \ArrayAccess
private $baseHref;
/**
- * Constructor.
- *
* @param \DOMElement $node A \DOMElement 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)
@@ -69,7 +67,7 @@ public function getFormNode()
*
* @param array $values An array of field values
*
- * @return Form
+ * @return $this
*/
public function setValues(array $values)
{
@@ -89,7 +87,7 @@ public function setValues(array $values)
*/
public function getValues()
{
- $values = array();
+ $values = [];
foreach ($this->fields->all() as $name => $field) {
if ($field->isDisabled()) {
continue;
@@ -110,11 +108,11 @@ public function getValues()
*/
public function getFiles()
{
- if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) {
- return array();
+ if (!\in_array($this->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
+ return [];
}
- $files = array();
+ $files = [];
foreach ($this->fields->all() as $name => $field) {
if ($field->isDisabled()) {
@@ -139,13 +137,13 @@ public function getFiles()
*/
public function getPhpValues()
{
- $values = array();
+ $values = [];
foreach ($this->getValues() as $name => $value) {
- $qs = http_build_query(array($name => $value), '', '&');
+ $qs = http_build_query([$name => $value], '', '&');
if (!empty($qs)) {
parse_str($qs, $expandedValue);
- $varName = substr($name, 0, strlen(key($expandedValue)));
- $values = array_replace_recursive($values, array($varName => current($expandedValue)));
+ $varName = substr($name, 0, \strlen(key($expandedValue)));
+ $values = array_replace_recursive($values, [$varName => current($expandedValue)]);
}
}
@@ -166,13 +164,25 @@ public function getPhpValues()
*/
public function getPhpFiles()
{
- $values = array();
+ $values = [];
foreach ($this->getFiles() as $name => $value) {
- $qs = http_build_query(array($name => $value), '', '&');
+ $qs = http_build_query([$name => $value], '', '&');
if (!empty($qs)) {
parse_str($qs, $expandedValue);
- $varName = substr($name, 0, strlen(key($expandedValue)));
- $values = array_replace_recursive($values, array($varName => current($expandedValue)));
+ $varName = substr($name, 0, \strlen(key($expandedValue)));
+
+ array_walk_recursive(
+ $expandedValue,
+ function (&$value, $key) {
+ if (ctype_digit($value) && ('size' === $key || 'error' === $key)) {
+ $value = (int) $value;
+ }
+ }
+ );
+
+ reset($expandedValue);
+
+ $values = array_replace_recursive($values, [$varName => current($expandedValue)]);
}
}
@@ -192,14 +202,14 @@ public function getUri()
{
$uri = parent::getUri();
- if (!in_array($this->getMethod(), array('POST', 'PUT', 'DELETE', 'PATCH'))) {
+ if (!\in_array($this->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
$query = parse_url($uri, PHP_URL_QUERY);
- $currentParameters = array();
+ $currentParameters = [];
if ($query) {
parse_str($query, $currentParameters);
}
- $queryString = http_build_query(array_merge($currentParameters, $this->getValues()), null, '&');
+ $queryString = http_build_query(array_merge($currentParameters, $this->getValues()), '', '&');
$pos = strpos($uri, '?');
$base = false === $pos ? $uri : substr($uri, 0, $pos);
@@ -211,6 +221,11 @@ public function getUri()
protected function getRawUri()
{
+ // If the form was created from a button rather than the form node, check for HTML5 action overrides
+ if ($this->button !== $this->node && $this->button->getAttribute('formaction')) {
+ return $this->button->getAttribute('formaction');
+ }
+
return $this->node->getAttribute('action');
}
@@ -227,6 +242,11 @@ public function getMethod()
return $this->method;
}
+ // If the form was created from a button rather than the form node, check for HTML5 method override
+ if ($this->button !== $this->node && $this->button->getAttribute('formmethod')) {
+ return strtoupper($this->button->getAttribute('formmethod'));
+ }
+
return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET';
}
@@ -257,7 +277,7 @@ public function remove($name)
*
* @param string $name The field name
*
- * @return FormField The field instance
+ * @return FormField|FormField[]|FormField[][] The value of the field
*
* @throws \InvalidArgumentException When field is not present in this form
*/
@@ -268,8 +288,6 @@ public function get($name)
/**
* Sets a named field.
- *
- * @param FormField $field The field
*/
public function set(FormField $field)
{
@@ -279,7 +297,7 @@ public function set(FormField $field)
/**
* Gets all fields.
*
- * @return FormField[] An array of fields
+ * @return FormField[]
*/
public function all()
{
@@ -303,7 +321,7 @@ public function offsetExists($name)
*
* @param string $name The field name
*
- * @return FormField The associated Field instance
+ * @return FormField|FormField[]|FormField[][] The value of the field
*
* @throws \InvalidArgumentException if the field does not exist
*/
@@ -356,14 +374,12 @@ public function disableValidation()
*
* Expects a 'submit' button \DOMElement and finds the corresponding form element, or the form element itself.
*
- * @param \DOMElement $node A \DOMElement instance
- *
* @throws \LogicException If given node is not a button or input or does not have a form ancestor
*/
protected function setNode(\DOMElement $node)
{
$this->button = $node;
- if ('button' === $node->nodeName || ('input' === $node->nodeName && in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image')))) {
+ if ('button' === $node->nodeName || ('input' === $node->nodeName && \in_array(strtolower($node->getAttribute('type')), ['submit', 'button', 'image']))) {
if ($node->hasAttribute('form')) {
// if the node has the HTML5-compliant 'form' attribute, use it
$formId = $node->getAttribute('form');
@@ -427,14 +443,14 @@ private function initialize()
// corresponding elements are either descendants or have a matching HTML5 form attribute
$formId = Crawler::xpathLiteral($this->node->getAttribute('id'));
- $fieldNodes = $xpath->query(sprintf('descendant::input[@form=%s] | descendant::button[@form=%s] | descendant::textarea[@form=%s] | descendant::select[@form=%s] | //form[@id=%s]//input[not(@form)] | //form[@id=%s]//button[not(@form)] | //form[@id=%s]//textarea[not(@form)] | //form[@id=%s]//select[not(@form)]', $formId, $formId, $formId, $formId, $formId, $formId, $formId, $formId));
+ $fieldNodes = $xpath->query(sprintf('( descendant::input[@form=%s] | descendant::button[@form=%1$s] | descendant::textarea[@form=%1$s] | descendant::select[@form=%1$s] | //form[@id=%1$s]//input[not(@form)] | //form[@id=%1$s]//button[not(@form)] | //form[@id=%1$s]//textarea[not(@form)] | //form[@id=%1$s]//select[not(@form)] )[not(ancestor::template)]', $formId));
foreach ($fieldNodes as $node) {
$this->addField($node);
}
} else {
// do the xpath query with $this->node as the context node, to only find descendant elements
// however, descendant elements with form attribute are not part of this form
- $fieldNodes = $xpath->query('descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)]', $this->node);
+ $fieldNodes = $xpath->query('( descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)] )[not(ancestor::template)]', $this->node);
foreach ($fieldNodes as $node) {
$this->addField($node);
}
@@ -464,7 +480,7 @@ private function addField(\DOMElement $node)
}
} elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) {
$this->set(new Field\FileFormField($node));
- } elseif ('input' == $nodeName && !in_array(strtolower($node->getAttribute('type')), array('submit', 'button', 'image'))) {
+ } elseif ('input' == $nodeName && !\in_array(strtolower($node->getAttribute('type')), ['submit', 'button', 'image'])) {
$this->set(new Field\InputFormField($node));
} elseif ('textarea' == $nodeName) {
$this->set(new Field\TextareaFormField($node));
diff --git a/FormFieldRegistry.php b/FormFieldRegistry.php
index dbd08ff7..af8bcfba 100644
--- a/FormFieldRegistry.php
+++ b/FormFieldRegistry.php
@@ -15,17 +15,17 @@
/**
* This is an internal class that must not be used directly.
+ *
+ * @internal
*/
class FormFieldRegistry
{
- private $fields = array();
+ private $fields = [];
private $base;
/**
* Adds a field to the registry.
- *
- * @param FormField $field The field
*/
public function add(FormField $field)
{
@@ -33,8 +33,8 @@ public function add(FormField $field)
$target = &$this->fields;
while ($segments) {
- if (!is_array($target)) {
- $target = array();
+ if (!\is_array($target)) {
+ $target = [];
}
$path = array_shift($segments);
if ('' === $path) {
@@ -55,9 +55,9 @@ public function remove($name)
{
$segments = $this->getSegments($name);
$target = &$this->fields;
- while (count($segments) > 1) {
+ while (\count($segments) > 1) {
$path = array_shift($segments);
- if (!array_key_exists($path, $target)) {
+ if (!\is_array($target) || !\array_key_exists($path, $target)) {
return;
}
$target = &$target[$path];
@@ -70,7 +70,7 @@ public function remove($name)
*
* @param string $name The fully qualified name of the field
*
- * @return mixed The value of the field
+ * @return FormField|FormField[]|FormField[][] The value of the field
*
* @throws \InvalidArgumentException if the field does not exist
*/
@@ -80,8 +80,8 @@ public function &get($name)
$target = &$this->fields;
while ($segments) {
$path = array_shift($segments);
- if (!array_key_exists($path, $target)) {
- throw new \InvalidArgumentException(sprintf('Unreachable field "%s"', $path));
+ if (!\is_array($target) || !\array_key_exists($path, $target)) {
+ throw new \InvalidArgumentException(sprintf('Unreachable field "%s".', $path));
}
$target = &$target[$path];
}
@@ -118,11 +118,13 @@ public function has($name)
public function set($name, $value)
{
$target = &$this->get($name);
- if ((!is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) {
+ if ((!\is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) {
$target->setValue($value);
- } elseif (is_array($value)) {
- $fields = self::create($name, $value);
- foreach ($fields->all() as $k => $v) {
+ } elseif (\is_array($value)) {
+ $registry = new static();
+ $registry->base = $name;
+ $registry->fields = $value;
+ foreach ($registry->all() as $k => $v) {
$this->set($k, $v);
}
} else {
@@ -133,33 +135,13 @@ public function set($name, $value)
/**
* Returns the list of field with their value.
*
- * @return FormField[] The list of fields as array((string) Fully qualified name => (mixed) value)
+ * @return FormField[] The list of fields as [string] Fully qualified name => (mixed) value)
*/
public function all()
{
return $this->walk($this->fields, $this->base);
}
- /**
- * Creates an instance of the class.
- *
- * This function is made private because it allows overriding the $base and
- * the $values properties without any type checking.
- *
- * @param string $base The fully qualified name of the base field
- * @param array $values The values of the fields
- *
- * @return FormFieldRegistry
- */
- private static function create($base, array $values)
- {
- $registry = new static();
- $registry->base = $base;
- $registry->fields = $values;
-
- return $registry;
- }
-
/**
* Transforms a PHP array in a list of fully qualified name / value.
*
@@ -167,13 +149,13 @@ private static function create($base, array $values)
* @param string $base The name of the base field
* @param array $output The initial values
*
- * @return array The list of fields as array((string) Fully qualified name => (mixed) value)
+ * @return array The list of fields as [string] Fully qualified name => (mixed) value)
*/
- private function walk(array $array, $base = '', array &$output = array())
+ private function walk(array $array, $base = '', array &$output = [])
{
foreach ($array as $k => $v) {
$path = empty($base) ? $k : sprintf('%s[%s]', $base, $k);
- if (is_array($v)) {
+ if (\is_array($v)) {
$this->walk($v, $path, $output);
} else {
$output[$path] = $v;
@@ -186,9 +168,7 @@ private function walk(array $array, $base = '', array &$output = array())
/**
* Splits a field name into segments as a web browser would do.
*
- *
- * getSegments('base[foo][3][]') = array('base', 'foo, '3', '');
- *
+ * getSegments('base[foo][3][]') = ['base', 'foo, '3', ''];
*
* @param string $name The name of the field
*
@@ -197,7 +177,7 @@ private function walk(array $array, $base = '', array &$output = array())
private function getSegments($name)
{
if (preg_match('/^(?P[^[]+)(?P(\[.*)|$)/', $name, $m)) {
- $segments = array($m['base']);
+ $segments = [$m['base']];
while (!empty($m['extra'])) {
$extra = $m['extra'];
if (preg_match('/^\[(?P.*?)\](?P.*)$/', $extra, $m)) {
@@ -210,6 +190,6 @@ private function getSegments($name)
return $segments;
}
- return array($name);
+ return [$name];
}
}
diff --git a/LICENSE b/LICENSE
index 12a74531..9e936ec0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2016 Fabien Potencier
+Copyright (c) 2004-2020 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Tests/CrawlerTest.php b/Tests/CrawlerTest.php
old mode 100755
new mode 100644
index b54563b7..26164dc5
--- a/Tests/CrawlerTest.php
+++ b/Tests/CrawlerTest.php
@@ -11,9 +11,10 @@
namespace Symfony\Component\DomCrawler\Tests;
+use PHPUnit\Framework\TestCase;
use Symfony\Component\DomCrawler\Crawler;
-class CrawlerTest extends \PHPUnit_Framework_TestCase
+class CrawlerTest extends TestCase
{
public function testConstructor()
{
@@ -30,14 +31,14 @@ public function testConstructor()
public function testGetUri()
{
$uri = 'http://symfony.com';
- $crawler = new Crawler(null, $uri);
+ $crawler = new Crawler(null, $uri);
$this->assertEquals($uri, $crawler->getUri());
}
public function testGetBaseHref()
{
$baseHref = 'http://symfony.com';
- $crawler = new Crawler(null, null, $baseHref);
+ $crawler = new Crawler(null, null, $baseHref);
$this->assertEquals($baseHref, $crawler->getBaseHref());
}
@@ -51,7 +52,7 @@ public function testAdd()
$crawler->add($this->createNodeList());
$this->assertEquals('foo', $crawler->filterXPath('//div')->attr('class'), '->add() adds nodes from a \DOMNodeList');
- $list = array();
+ $list = [];
foreach ($this->createNodeList() as $node) {
$list[] = $node;
}
@@ -68,21 +69,17 @@ public function testAdd()
$this->assertEquals('Foo', $crawler->filterXPath('//body')->text(), '->add() adds nodes from a string');
}
- /**
- * @expectedException \InvalidArgumentException
- */
public function testAddInvalidType()
{
+ $this->expectException('InvalidArgumentException');
$crawler = new Crawler();
$crawler->add(1);
}
- /**
- * @expectedException \InvalidArgumentException
- * @expectedExceptionMessage Attaching DOM nodes from multiple documents in the same crawler is forbidden.
- */
public function testAddMultipleDocumentNode()
{
+ $this->expectException('InvalidArgumentException');
+ $this->expectExceptionMessage('Attaching DOM nodes from multiple documents in the same crawler is forbidden.');
$crawler = $this->createTestCrawler();
$crawler->addHtmlContent('
', 'UTF-8');
}
@@ -203,7 +200,7 @@ public function testAddXmlContentWithErrors()
EOF
, 'UTF-8');
- $this->assertTrue(count(libxml_get_errors()) > 1);
+ $this->assertGreaterThan(1, libxml_get_errors());
libxml_clear_errors();
libxml_use_internal_errors($internalErrors);
@@ -238,7 +235,13 @@ public function testAddContent()
$crawler = new Crawler();
$crawler->addContent('中文');
$this->assertEquals('中文', $crawler->filterXPath('//span')->text(), '->addContent() guess wrong charset');
+ }
+ /**
+ * @requires extension iconv
+ */
+ public function testAddContentNonUtf8()
+ {
$crawler = new Crawler();
$crawler->addContent(iconv('UTF-8', 'SJIS', '
日本語'));
$this->assertEquals('日本語', $crawler->filterXPath('//body')->text(), '->addContent() can recognize "Shift_JIS" in html5 meta charset tag');
@@ -262,7 +265,7 @@ public function testAddNodeList()
public function testAddNodes()
{
- $list = array();
+ $list = [];
foreach ($this->createNodeList() as $node) {
$list[] = $node;
}
@@ -307,7 +310,7 @@ public function testEach()
return $i.'-'.$node->text();
});
- $this->assertEquals(array('0-One', '1-Two', '2-Three'), $data, '->each() executes an anonymous function on each node of the list');
+ $this->assertEquals(['0-One', '1-Two', '2-Three'], $data, '->each() executes an anonymous function on each node of the list');
}
public function testIteration()
@@ -332,7 +335,7 @@ public function testReduce()
{
$crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
$nodes = $crawler->reduce(function ($node, $i) {
- return $i !== 1;
+ return 1 !== $i;
});
$this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler');
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler');
@@ -390,7 +393,7 @@ public function testText()
public function testHtml()
{
$this->assertEquals('', $this->createTestCrawler()->filterXPath('//a[5]')->html());
- $this->assertEquals('', trim($this->createTestCrawler()->filterXPath('//form[@id="FooFormId"]')->html()));
+ $this->assertEquals('', trim(preg_replace('~>\s+<~', '><', $this->createTestCrawler()->filterXPath('//form[@id="FooFormId"]')->html())));
try {
$this->createTestCrawler()->filterXPath('//ol')->html();
@@ -404,10 +407,11 @@ public function testExtract()
{
$crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li');
- $this->assertEquals(array('One', 'Two', 'Three'), $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list');
- $this->assertEquals(array(array('One', 'first'), array('Two', ''), array('Three', '')), $crawler->extract(array('_text', 'class')), '->extract() returns an array of extracted data from the node list');
+ $this->assertEquals(['One', 'Two', 'Three'], $crawler->extract('_text'), '->extract() returns an array of extracted data from the node list');
+ $this->assertEquals([['One', 'first'], ['Two', ''], ['Three', '']], $crawler->extract(['_text', 'class']), '->extract() returns an array of extracted data from the node list');
+ $this->assertEquals([[], [], []], $crawler->extract([]), '->extract() returns empty arrays if the attribute list is empty');
- $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->extract('_text'), '->extract() returns an empty array if the node list is empty');
+ $this->assertEquals([], $this->createTestCrawler()->filterXPath('//ol')->extract('_text'), '->extract() returns an empty array if the node list is empty');
}
public function testFilterXpathComplexQueries()
@@ -430,6 +434,7 @@ public function testFilterXpathComplexQueries()
$this->assertCount(5, $crawler->filterXPath('(//a | //div)//img'));
$this->assertCount(7, $crawler->filterXPath('((//a | //div)//img | //ul)'));
$this->assertCount(7, $crawler->filterXPath('( ( //a | //div )//img | //ul )'));
+ $this->assertCount(1, $crawler->filterXPath("//a[./@href][((./@id = 'Klausi|Claudiu' or normalize-space(string(.)) = 'Klausi|Claudiu' or ./@title = 'Klausi|Claudiu' or ./@rel = 'Klausi|Claudiu') or .//img[./@alt = 'Klausi|Claudiu'])]"));
}
public function testFilterXPath()
@@ -596,7 +601,7 @@ public function testFilterXPathWithSelfAxes()
$this->assertCount(0, $crawler->filterXPath('self::a'), 'The fake root node has no "real" element name');
$this->assertCount(0, $crawler->filterXPath('self::a/img'), 'The fake root node has no "real" element name');
- $this->assertCount(9, $crawler->filterXPath('self::*/a'));
+ $this->assertCount(10, $crawler->filterXPath('self::*/a'));
}
public function testFilter()
@@ -760,22 +765,18 @@ public function testLink()
}
}
- /**
- * @expectedException \InvalidArgumentException
- * @expectedExceptionMessage The selected node should be instance of DOMElement
- */
public function testInvalidLink()
{
+ $this->expectException('InvalidArgumentException');
+ $this->expectExceptionMessage('The selected node should be instance of DOMElement');
$crawler = $this->createTestCrawler('http://example.com/bar/');
$crawler->filterXPath('//li/text()')->link();
}
- /**
- * @expectedException \InvalidArgumentException
- * @expectedExceptionMessage The selected node should be instance of DOMElement
- */
public function testInvalidLinks()
{
+ $this->expectException('InvalidArgumentException');
+ $this->expectExceptionMessage('The selected node should be instance of DOMElement');
$crawler = $this->createTestCrawler('http://example.com/bar/');
$crawler->filterXPath('//li/text()')->link();
}
@@ -834,25 +835,25 @@ public function testChaining()
public function testLinks()
{
$crawler = $this->createTestCrawler('http://example.com/bar/')->selectLink('Foo');
- $this->assertInternalType('array', $crawler->links(), '->links() returns an array');
+ $this->assertIsArray($crawler->links(), '->links() returns an array');
$this->assertCount(4, $crawler->links(), '->links() returns an array');
$links = $crawler->links();
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Link', $links[0], '->links() returns an array of Link instances');
- $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->links(), '->links() returns an empty array if the node selection is empty');
+ $this->assertEquals([], $this->createTestCrawler()->filterXPath('//ol')->links(), '->links() returns an empty array if the node selection is empty');
}
public function testImages()
{
$crawler = $this->createTestCrawler('http://example.com/bar/')->selectImage('Bar');
- $this->assertInternalType('array', $crawler->images(), '->images() returns an array');
+ $this->assertIsArray($crawler->images(), '->images() returns an array');
$this->assertCount(4, $crawler->images(), '->images() returns an array');
$images = $crawler->images();
$this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Image', $images[0], '->images() returns an array of Image instances');
- $this->assertEquals(array(), $this->createTestCrawler()->filterXPath('//ol')->links(), '->links() returns an empty array if the node selection is empty');
+ $this->assertEquals([], $this->createTestCrawler()->filterXPath('//ol')->links(), '->links() returns an empty array if the node selection is empty');
}
public function testForm()
@@ -865,9 +866,9 @@ public function testForm()
$this->assertEquals($crawler->form()->getFormNode()->getAttribute('id'), $crawler2->form()->getFormNode()->getAttribute('id'), '->form() works on elements with form attribute');
- $this->assertEquals(array('FooName' => 'FooBar', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form(array('FooName' => 'FooBar'))->getValues(), '->form() takes an array of values to submit as its first argument');
- $this->assertEquals(array('FooName' => 'FooValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler->form()->getValues(), '->getValues() returns correct form values');
- $this->assertEquals(array('FooBarName' => 'FooBarValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'), $crawler2->form()->getValues(), '->getValues() returns correct form values');
+ $this->assertEquals(['FooName' => 'FooBar', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'], $crawler->form(['FooName' => 'FooBar'])->getValues(), '->form() takes an array of values to submit as its first argument');
+ $this->assertEquals(['FooName' => 'FooValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'], $crawler->form()->getValues(), '->getValues() returns correct form values');
+ $this->assertEquals(['FooBarName' => 'FooBarValue', 'TextName' => 'TextValue', 'FooTextName' => 'FooTextValue'], $crawler2->form()->getValues(), '->getValues() returns correct form values');
try {
$this->createTestCrawler()->filterXPath('//ol')->form();
@@ -877,12 +878,10 @@ public function testForm()
}
}
- /**
- * @expectedException \InvalidArgumentException
- * @expectedExceptionMessage The selected node should be instance of DOMElement
- */
public function testInvalidForm()
{
+ $this->expectException('InvalidArgumentException');
+ $this->expectExceptionMessage('The selected node should be instance of DOMElement');
$crawler = $this->createTestCrawler('http://example.com/bar/');
$crawler->filterXPath('//li/text()')->form();
}
@@ -988,7 +987,9 @@ public function testChildren()
$crawler = new Crawler('');
$crawler->filter('p')->children();
$this->assertTrue(true, '->children() does not trigger a notice if the node has no children');
- } catch (\PHPUnit_Framework_Error_Notice $e) {
+ } catch (\PHPUnit\Framework\Error\Notice $e) {
+ $this->fail('->children() does not trigger a notice if the node has no children');
+ } catch (\PHPUnit\Framework\Error\Notice $e) {
$this->fail('->children() does not trigger a notice if the node has no children');
}
}
@@ -1016,7 +1017,7 @@ public function testParents()
/**
* @dataProvider getBaseTagData
*/
- public function testBaseTag($baseValue, $linkValue, $expectedUri, $currentUri = null, $description = null)
+ public function testBaseTag($baseValue, $linkValue, $expectedUri, $currentUri = null, $description = '')
{
$crawler = new Crawler('', $currentUri);
$this->assertEquals($expectedUri, $crawler->filterXPath('//a')->link()->getUri(), $description);
@@ -1024,13 +1025,13 @@ public function testBaseTag($baseValue, $linkValue, $expectedUri, $currentUri =
public function getBaseTagData()
{
- return array(
- array('http://base.com', 'link', 'http://base.com/link'),
- array('//base.com', 'link', 'https://base.com/link', 'https://domain.com', ' tag can use a schema-less URL'),
- array('path/', 'link', 'https://domain.com/path/link', 'https://domain.com', ' tag can set a path'),
- array('http://base.com', '#', 'http://base.com#', 'http://domain.com/path/link', ' tag does work with links to an anchor'),
- array('http://base.com', '', 'http://base.com', 'http://domain.com/path/link', ' tag does work with empty links'),
- );
+ return [
+ ['http://base.com', 'link', 'http://base.com/link'],
+ ['//base.com', 'link', 'https://base.com/link', 'https://domain.com', ' tag can use a schema-less URL'],
+ ['path/', 'link', 'https://domain.com/path/link', 'https://domain.com', ' tag can set a path'],
+ ['http://base.com', '#', 'http://base.com#', 'http://domain.com/path/link', ' tag does work with links to an anchor'],
+ ['http://base.com', '', 'http://base.com', 'http://domain.com/path/link', ' tag does work with empty links'],
+ ];
}
/**
@@ -1044,14 +1045,14 @@ public function testBaseTagWithForm($baseValue, $actionValue, $expectedUri, $cur
public function getBaseTagWithFormData()
{
- return array(
- array('https://base.com/', 'link/', 'https://base.com/link/', 'https://base.com/link/', ' tag does work with a path and relative form action'),
- array('/basepath', '/registration', 'http://domain.com/registration', 'http://domain.com/registration', ' tag does work with a path and form action'),
- array('/basepath', '', 'http://domain.com/registration', 'http://domain.com/registration', ' tag does work with a path and empty form action'),
- array('http://base.com/', '/registration', 'http://base.com/registration', 'http://domain.com/registration', ' tag does work with a URL and form action'),
- array('http://base.com', '', 'http://domain.com/path/form', 'http://domain.com/path/form', ' tag does work with a URL and an empty form action'),
- array('http://base.com/path', '/registration', 'http://base.com/registration', 'http://domain.com/path/form', ' tag does work with a URL and form action'),
- );
+ return [
+ ['https://base.com/', 'link/', 'https://base.com/link/', 'https://base.com/link/', ' tag does work with a path and relative form action'],
+ ['/basepath', '/registration', 'http://domain.com/registration', 'http://domain.com/registration', ' tag does work with a path and form action'],
+ ['/basepath', '', 'http://domain.com/registration', 'http://domain.com/registration', ' tag does work with a path and empty form action'],
+ ['http://base.com/', '/registration', 'http://base.com/registration', 'http://domain.com/registration', ' tag does work with a URL and form action'],
+ ['http://base.com', '', 'http://domain.com/path/form', 'http://domain.com/path/form', ' tag does work with a URL and an empty form action'],
+ ['http://base.com/path', '/registration', 'http://base.com/registration', 'http://domain.com/path/form', ' tag does work with a URL and form action'],
+ ];
}
public function testCountOfNestedElements()
@@ -1061,6 +1062,49 @@ public function testCountOfNestedElements()
$this->assertCount(1, $crawler->filter('li:contains("List item 1")'));
}
+ public function testEvaluateReturnsTypedResultOfXPathExpressionOnADocumentSubset()
+ {
+ $crawler = $this->createTestCrawler();
+
+ $result = $crawler->filterXPath('//form/input')->evaluate('substring-before(@name, "Name")');
+
+ $this->assertSame(['Text', 'Foo', 'Bar'], $result);
+ }
+
+ public function testEvaluateReturnsTypedResultOfNamespacedXPathExpressionOnADocumentSubset()
+ {
+ $crawler = $this->createTestXmlCrawler();
+
+ $result = $crawler->filterXPath('//yt:accessControl/@action')->evaluate('string(.)');
+
+ $this->assertSame(['comment', 'videoRespond'], $result);
+ }
+
+ public function testEvaluateReturnsTypedResultOfNamespacedXPathExpression()
+ {
+ $crawler = $this->createTestXmlCrawler();
+ $crawler->registerNamespace('youtube', 'http://gdata.youtube.com/schemas/2007');
+
+ $result = $crawler->evaluate('string(//youtube:accessControl/@action)');
+
+ $this->assertSame(['comment'], $result);
+ }
+
+ public function testEvaluateReturnsACrawlerIfXPathExpressionEvaluatesToANode()
+ {
+ $crawler = $this->createTestCrawler()->evaluate('//form/input[1]');
+
+ $this->assertInstanceOf(Crawler::class, $crawler);
+ $this->assertCount(1, $crawler);
+ $this->assertSame('input', $crawler->first()->nodeName());
+ }
+
+ public function testEvaluateThrowsAnExceptionIfDocumentIsEmpty()
+ {
+ $this->expectException('LogicException');
+ (new Crawler())->evaluate('//form/input[1]');
+ }
+
public function createTestCrawler($uri = null)
{
$dom = new \DOMDocument();
@@ -1079,6 +1123,8 @@ public function createTestCrawler($uri = null)
GetLink
+ Klausi|Claudiu
+
');
+
+ $field = new InputFormField($dom->getElementById('foo'));
+ $this->assertNull($field->getLabel(), '->getLabel() returns null if no label is defined');
+ }
+
+ public function testLabelIsAssignedByForAttribute()
+ {
+ $dom = new \DOMDocument();
+ $dom->loadHTML('');
+
+ $field = new InputFormField($dom->getElementById('foo'));
+ $this->assertEquals('Foo label', $field->getLabel()->textContent, '->getLabel() returns the associated label');
+ }
+
+ public function testLabelIsAssignedByParentingRelation()
+ {
+ $dom = new \DOMDocument();
+ $dom->loadHTML('');
+
+ $field = new InputFormField($dom->getElementById('foo'));
+ $this->assertEquals('Foo label', $field->getLabel()->textContent, '->getLabel() returns the parent label');
+ }
}
diff --git a/Tests/Field/FormFieldTestCase.php b/Tests/Field/FormFieldTestCase.php
index 26b1b0e2..5ca19d95 100644
--- a/Tests/Field/FormFieldTestCase.php
+++ b/Tests/Field/FormFieldTestCase.php
@@ -11,9 +11,11 @@
namespace Symfony\Component\DomCrawler\Tests\Field;
-class FormFieldTestCase extends \PHPUnit_Framework_TestCase
+use PHPUnit\Framework\TestCase;
+
+class FormFieldTestCase extends TestCase
{
- protected function createNode($tag, $value, $attributes = array())
+ protected function createNode($tag, $value, $attributes = [])
{
$document = new \DOMDocument();
$node = $document->createElement($tag, $value);
diff --git a/Tests/Field/InputFormFieldTest.php b/Tests/Field/InputFormFieldTest.php
index 193d301d..a1f327bb 100644
--- a/Tests/Field/InputFormFieldTest.php
+++ b/Tests/Field/InputFormFieldTest.php
@@ -17,30 +17,30 @@ class InputFormFieldTest extends FormFieldTestCase
{
public function testInitialize()
{
- $node = $this->createNode('input', '', array('type' => 'text', 'name' => 'name', 'value' => 'value'));
+ $node = $this->createNode('input', '', ['type' => 'text', 'name' => 'name', 'value' => 'value']);
$field = new InputFormField($node);
$this->assertEquals('value', $field->getValue(), '->initialize() sets the value of the field to the value attribute value');
$node = $this->createNode('textarea', '');
try {
- $field = new InputFormField($node);
+ new InputFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is not an input');
} catch (\LogicException $e) {
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input');
}
- $node = $this->createNode('input', '', array('type' => 'checkbox'));
+ $node = $this->createNode('input', '', ['type' => 'checkbox']);
try {
- $field = new InputFormField($node);
+ new InputFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is a checkbox');
} catch (\LogicException $e) {
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is a checkbox');
}
- $node = $this->createNode('input', '', array('type' => 'file'));
+ $node = $this->createNode('input', '', ['type' => 'file']);
try {
- $field = new InputFormField($node);
+ new InputFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is a file');
} catch (\LogicException $e) {
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is a file');
diff --git a/Tests/Field/TextareaFormFieldTest.php b/Tests/Field/TextareaFormFieldTest.php
index 5d4d0038..192984ce 100644
--- a/Tests/Field/TextareaFormFieldTest.php
+++ b/Tests/Field/TextareaFormFieldTest.php
@@ -24,7 +24,7 @@ public function testInitialize()
$node = $this->createNode('input', '');
try {
- $field = new TextareaFormField($node);
+ new TextareaFormField($node);
$this->fail('->initialize() throws a \LogicException if the node is not a textarea');
} catch (\LogicException $e) {
$this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a textarea');
diff --git a/Tests/FormTest.php b/Tests/FormTest.php
index 3035e8d3..19f704e5 100644
--- a/Tests/FormTest.php
+++ b/Tests/FormTest.php
@@ -11,11 +11,12 @@
namespace Symfony\Component\DomCrawler\Tests;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\DomCrawler\Field\TextareaFormField;
use Symfony\Component\DomCrawler\Form;
use Symfony\Component\DomCrawler\FormFieldRegistry;
-use Symfony\Component\DomCrawler\Field;
-class FormTest extends \PHPUnit_Framework_TestCase
+class FormTest extends TestCase
{
public static function setUpBeforeClass()
{
@@ -39,14 +40,14 @@ public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor()
$nodes = $dom->getElementsByTagName('input');
try {
- $form = new Form($nodes->item(0), 'http://example.com');
+ new Form($nodes->item(0), 'http://example.com');
$this->fail('__construct() throws a \\LogicException if the node has no form ancestor');
} catch (\LogicException $e) {
$this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor');
}
try {
- $form = new Form($nodes->item(1), 'http://example.com');
+ new Form($nodes->item(1), 'http://example.com');
$this->fail('__construct() throws a \\LogicException if the input type is not submit, button, or image');
} catch (\LogicException $e) {
$this->assertTrue(true, '__construct() throws a \\LogicException if the input type is not submit, button, or image');
@@ -55,7 +56,7 @@ public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor()
$nodes = $dom->getElementsByTagName('button');
try {
- $form = new Form($nodes->item(0), 'http://example.com');
+ new Form($nodes->item(0), 'http://example.com');
$this->fail('__construct() throws a \\LogicException if the node has no form ancestor');
} catch (\LogicException $e) {
$this->assertTrue(true, '__construct() throws a \\LogicException if the node has no form ancestor');
@@ -63,11 +64,18 @@ public function testConstructorThrowsExceptionIfTheNodeHasNoFormAncestor()
}
/**
- * __construct() should throw \\LogicException if the form attribute is invalid.
+ * @dataProvider constructorThrowsExceptionIfNoRelatedFormProvider
*
- * @expectedException \LogicException
+ * __construct() should throw a \LogicException if the form attribute is invalid.
*/
- public function testConstructorThrowsExceptionIfNoRelatedForm()
+ public function testConstructorThrowsExceptionIfNoRelatedForm(\DOMElement $node)
+ {
+ $this->expectException('LogicException');
+
+ new Form($node, 'http://example.com');
+ }
+
+ public function constructorThrowsExceptionIfNoRelatedFormProvider()
{
$dom = new \DOMDocument();
$dom->loadHTML('
@@ -82,8 +90,10 @@ public function testConstructorThrowsExceptionIfNoRelatedForm()
$nodes = $dom->getElementsByTagName('input');
- $form = new Form($nodes->item(0), 'http://example.com');
- $form = new Form($nodes->item(1), 'http://example.com');
+ return [
+ [$nodes->item(0)],
+ [$nodes->item(1)],
+ ];
}
public function testConstructorLoadsOnlyFieldsOfTheRightForm()
@@ -129,18 +139,18 @@ public function testConstructorHandlesFormValues()
$form2 = new Form($buttonElements->item(0), 'http://example.com');
// Tests if form values are correctly assigned to forms
- $values1 = array(
- 'apples' => array('1', '2'),
+ $values1 = [
+ 'apples' => ['1', '2'],
'form_name' => 'form-1',
'button_1' => 'Capture fields',
'outer_field' => 'success',
- );
- $values2 = array(
- 'oranges' => array('1', '2', '3'),
+ ];
+ $values2 = [
+ 'oranges' => ['1', '2', '3'],
'form_name' => 'form_2',
'button_2' => '',
- 'app_frontend_form_type_contact_form_type' => array('contactType' => '', 'firstName' => 'John'),
- );
+ 'app_frontend_form_type_contact_form_type' => ['contactType' => '', 'firstName' => 'John'],
+ ];
$this->assertEquals($values1, $form1->getPhpValues(), 'HTML5-compliant form attribute handled incorrectly');
$this->assertEquals($values2, $form2->getPhpValues(), 'HTML5-compliant form attribute handled incorrectly');
@@ -160,25 +170,28 @@ public function testMultiValuedFields()
');
$this->assertEquals(
- array_keys($form->all()),
- array('foo[2]', 'foo[3]', 'bar[foo][0]', 'bar[foo][foobar]')
+ ['foo[2]', 'foo[3]', 'bar[foo][0]', 'bar[foo][foobar]'],
+ array_keys($form->all())
);
- $this->assertEquals($form->get('foo[2]')->getValue(), 'foo');
- $this->assertEquals($form->get('foo[3]')->getValue(), 'foo');
- $this->assertEquals($form->get('bar[foo][0]')->getValue(), 'foo');
- $this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foo');
+ $this->assertEquals('foo', $form->get('foo[2]')->getValue());
+ $this->assertEquals('foo', $form->get('foo[3]')->getValue());
+ $this->assertEquals('foo', $form->get('bar[foo][0]')->getValue());
+ $this->assertEquals('foo', $form->get('bar[foo][foobar]')->getValue());
$form['foo[2]'] = 'bar';
$form['foo[3]'] = 'bar';
- $this->assertEquals($form->get('foo[2]')->getValue(), 'bar');
- $this->assertEquals($form->get('foo[3]')->getValue(), 'bar');
+ $this->assertEquals('bar', $form->get('foo[2]')->getValue());
+ $this->assertEquals('bar', $form->get('foo[3]')->getValue());
- $form['bar'] = array('foo' => array('0' => 'bar', 'foobar' => 'foobar'));
+ $form['bar'] = ['foo' => ['0' => 'bar', 'foobar' => 'foobar']];
- $this->assertEquals($form->get('bar[foo][0]')->getValue(), 'bar');
- $this->assertEquals($form->get('bar[foo][foobar]')->getValue(), 'foobar');
+ $this->assertEquals('bar', $form->get('bar[foo][0]')->getValue());
+ $this->assertEquals(
+ 'foobar',
+ $form->get('bar[foo][foobar]')->getValue()
+ );
}
/**
@@ -189,10 +202,11 @@ public function testConstructor($message, $form, $values)
$form = $this->createForm('');
$this->assertEquals(
$values,
- array_map(function ($field) {
- $class = get_class($field);
+ array_map(
+ function ($field) {
+ $class = \get_class($field);
- return array(substr($class, strrpos($class, '\\') + 1), $field->getValue());
+ return [substr($class, strrpos($class, '\\') + 1), $field->getValue()];
},
$form->all()
),
@@ -202,84 +216,84 @@ public function testConstructor($message, $form, $values)
public function provideInitializeValues()
{
- return array(
- array(
+ return [
+ [
'does not take into account input fields without a name attribute',
'
',
- array(),
- ),
- array(
+ [],
+ ],
+ [
'does not take into account input fields with an empty name attribute value',
'
',
- array(),
- ),
- array(
+ [],
+ ],
+ [
'takes into account disabled input fields',
'
',
- array('foo' => array('InputFormField', 'foo')),
- ),
- array(
+ ['foo' => ['InputFormField', 'foo']],
+ ],
+ [
'appends the submitted button value',
'',
- array('bar' => array('InputFormField', 'bar')),
- ),
- array(
+ ['bar' => ['InputFormField', 'bar']],
+ ],
+ [
'appends the submitted button value for Button element',
'',
- array('bar' => array('InputFormField', 'bar')),
- ),
- array(
+ ['bar' => ['InputFormField', 'bar']],
+ ],
+ [
'appends the submitted button value but not other submit buttons',
'
',
- array('foobar' => array('InputFormField', 'foobar')),
- ),
- array(
+ ['foobar' => ['InputFormField', 'foobar']],
+ ],
+ [
'turns an image input into x and y fields',
'',
- array('bar.x' => array('InputFormField', '0'), 'bar.y' => array('InputFormField', '0')),
- ),
- array(
+ ['bar.x' => ['InputFormField', '0'], 'bar.y' => ['InputFormField', '0']],
+ ],
+ [
'returns textareas',
'
',
- array('foo' => array('TextareaFormField', 'foo')),
- ),
- array(
+ ['foo' => ['TextareaFormField', 'foo']],
+ ],
+ [
'returns inputs',
'
',
- array('foo' => array('InputFormField', 'foo')),
- ),
- array(
+ ['foo' => ['InputFormField', 'foo']],
+ ],
+ [
'returns checkboxes',
'
',
- array('foo' => array('ChoiceFormField', 'foo')),
- ),
- array(
+ ['foo' => ['ChoiceFormField', 'foo']],
+ ],
+ [
'returns not-checked checkboxes',
'
',
- array('foo' => array('ChoiceFormField', false)),
- ),
- array(
+ ['foo' => ['ChoiceFormField', false]],
+ ],
+ [
'returns radio buttons',
'
',
- array('foo' => array('ChoiceFormField', 'bar')),
- ),
- array(
+ ['foo' => ['ChoiceFormField', 'bar']],
+ ],
+ [
'returns file inputs',
'
',
- array('foo' => array('FileFormField', array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))),
- ),
- );
+ ['foo' => ['FileFormField', ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]]],
+ ],
+ ];
}
public function testGetFormNode()
@@ -320,6 +334,12 @@ public function testGetMethod()
$this->assertEquals('PATCH', $form->getMethod(), '->getMethod() returns the method defined in the constructor if provided');
}
+ public function testGetMethodWithOverride()
+ {
+ $form = $this->createForm('');
+ $this->assertEquals('POST', $form->getMethod(), '->getMethod() returns the method attribute value of the form');
+ }
+
public function testGetSetValue()
{
$form = $this->createForm('');
@@ -369,92 +389,109 @@ public function testOffsetUnset()
{
$form = $this->createForm('');
unset($form['foo']);
- $this->assertFalse(isset($form['foo']), '->offsetUnset() removes a field');
+ $this->assertArrayNotHasKey('foo', $form, '->offsetUnset() removes a field');
}
public function testOffsetExists()
{
$form = $this->createForm('');
- $this->assertTrue(isset($form['foo']), '->offsetExists() return true if the field exists');
- $this->assertFalse(isset($form['bar']), '->offsetExists() return false if the field does not exist');
+ $this->assertArrayHasKey('foo', $form, '->offsetExists() return true if the field exists');
+ $this->assertArrayNotHasKey('bar', $form, '->offsetExists() return false if the field does not exist');
}
public function testGetValues()
{
$form = $this->createForm('');
- $this->assertEquals(array('foo[bar]' => 'foo', 'bar' => 'bar', 'baz' => array()), $form->getValues(), '->getValues() returns all form field values');
+ $this->assertEquals(['foo[bar]' => 'foo', 'bar' => 'bar', 'baz' => []], $form->getValues(), '->getValues() returns all form field values');
$form = $this->createForm('');
- $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include not-checked checkboxes');
+ $this->assertEquals(['bar' => 'bar'], $form->getValues(), '->getValues() does not include not-checked checkboxes');
$form = $this->createForm('');
- $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include file input fields');
+ $this->assertEquals(['bar' => 'bar'], $form->getValues(), '->getValues() does not include file input fields');
$form = $this->createForm('');
- $this->assertEquals(array('bar' => 'bar'), $form->getValues(), '->getValues() does not include disabled fields');
+ $this->assertEquals(['bar' => 'bar'], $form->getValues(), '->getValues() does not include disabled fields');
+
+ $form = $this->createForm('');
+ $this->assertEquals(['bar' => 'bar'], $form->getValues(), '->getValues() does not include template fields');
+ $this->assertFalse($form->has('foo'));
}
public function testSetValues()
{
$form = $this->createForm('');
- $form->setValues(array('foo' => false, 'bar' => 'foo'));
- $this->assertEquals(array('bar' => 'foo'), $form->getValues(), '->setValues() sets the values of fields');
+ $form->setValues(['foo' => false, 'bar' => 'foo']);
+ $this->assertEquals(['bar' => 'foo'], $form->getValues(), '->setValues() sets the values of fields');
}
public function testMultiselectSetValues()
{
$form = $this->createForm('');
- $form->setValues(array('multi' => array('foo', 'bar')));
- $this->assertEquals(array('multi' => array('foo', 'bar')), $form->getValues(), '->setValue() sets the values of select');
+ $form->setValues(['multi' => ['foo', 'bar']]);
+ $this->assertEquals(['multi' => ['foo', 'bar']], $form->getValues(), '->setValue() sets the values of select');
}
public function testGetPhpValues()
{
$form = $this->createForm('');
- $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), '->getPhpValues() converts keys with [] to arrays');
+ $this->assertEquals(['foo' => ['bar' => 'foo'], 'bar' => 'bar'], $form->getPhpValues(), '->getPhpValues() converts keys with [] to arrays');
$form = $this->createForm('');
- $this->assertEquals(array('fo.o' => array('ba.r' => 'foo'), 'ba r' => 'bar'), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names');
+ $this->assertEquals(['fo.o' => ['ba.r' => 'foo'], 'ba r' => 'bar'], $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names');
$form = $this->createForm('');
- $this->assertEquals(array('fo.o' => array('ba.r' => array('foo', 'ba.z' => 'bar'))), $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names recursively');
+ $this->assertEquals(['fo.o' => ['ba.r' => ['foo', 'ba.z' => 'bar']]], $form->getPhpValues(), '->getPhpValues() preserves periods and spaces in names recursively');
$form = $this->createForm('');
- $this->assertEquals(array('foo' => array('bar' => 'foo'), 'bar' => 'bar'), $form->getPhpValues(), "->getPhpValues() doesn't return empty values");
+ $this->assertEquals(['foo' => ['bar' => 'foo'], 'bar' => 'bar'], $form->getPhpValues(), "->getPhpValues() doesn't return empty values");
}
public function testGetFiles()
{
$form = $this->createForm('');
- $this->assertEquals(array(), $form->getFiles(), '->getFiles() returns an empty array if method is get');
+ $this->assertEquals([], $form->getFiles(), '->getFiles() returns an empty array if method is get');
$form = $this->createForm('');
- $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for POST');
+ $this->assertEquals(['foo[bar]' => ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]], $form->getFiles(), '->getFiles() only returns file fields for POST');
$form = $this->createForm('', 'put');
- $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PUT');
+ $this->assertEquals(['foo[bar]' => ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]], $form->getFiles(), '->getFiles() only returns file fields for PUT');
$form = $this->createForm('', 'delete');
- $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for DELETE');
+ $this->assertEquals(['foo[bar]' => ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]], $form->getFiles(), '->getFiles() only returns file fields for DELETE');
$form = $this->createForm('', 'patch');
- $this->assertEquals(array('foo[bar]' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)), $form->getFiles(), '->getFiles() only returns file fields for PATCH');
+ $this->assertEquals(['foo[bar]' => ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]], $form->getFiles(), '->getFiles() only returns file fields for PATCH');
$form = $this->createForm('');
- $this->assertEquals(array(), $form->getFiles(), '->getFiles() does not include disabled file fields');
+ $this->assertEquals([], $form->getFiles(), '->getFiles() does not include disabled file fields');
+
+ $form = $this->createForm('');
+ $this->assertEquals([], $form->getFiles(), '->getFiles() does not include template file fields');
+ $this->assertFalse($form->has('foo'));
}
public function testGetPhpFiles()
{
$form = $this->createForm('');
- $this->assertEquals(array('foo' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() converts keys with [] to arrays');
+ $this->assertEquals(['foo' => ['bar' => ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]]], $form->getPhpFiles(), '->getPhpFiles() converts keys with [] to arrays');
$form = $this->createForm('');
- $this->assertEquals(array('f.o o' => array('bar' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names');
+ $this->assertEquals(['f.o o' => ['bar' => ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]]], $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names');
$form = $this->createForm('');
- $this->assertEquals(array('f.o o' => array('bar' => array('ba.z' => array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0), array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0)))), $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names recursively');
+ $this->assertEquals(['f.o o' => ['bar' => ['ba.z' => ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0], ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]]]], $form->getPhpFiles(), '->getPhpFiles() preserves periods and spaces in names recursively');
+
+ $form = $this->createForm('');
+ $files = $form->getPhpFiles();
+
+ $this->assertSame(0, $files['foo']['bar']['size'], '->getPhpFiles() converts size to int');
+ $this->assertSame(4, $files['foo']['bar']['error'], '->getPhpFiles() converts error to int');
+
+ $form = $this->createForm('');
+ $this->assertEquals(['size' => ['error' => ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]]], $form->getPhpFiles(), '->getPhpFiles() int conversion does not collide with file names');
}
/**
@@ -527,85 +564,91 @@ public function testGetUriWithoutAction()
$this->assertEquals('http://localhost/foo/bar', $form->getUri(), '->getUri() returns path if no action defined');
}
+ public function testGetUriWithActionOverride()
+ {
+ $form = $this->createForm('', null, 'http://localhost/foo/');
+ $this->assertEquals('http://localhost/bar', $form->getUri(), '->getUri() returns absolute URIs');
+ }
+
public function provideGetUriValues()
{
- return array(
- array(
+ return [
+ [
'returns the URI of the form',
'',
- array(),
+ [],
'/foo',
- ),
- array(
+ ],
+ [
'appends the form values if the method is get',
'',
- array(),
+ [],
'/foo?foo=foo',
- ),
- array(
+ ],
+ [
'appends the form values and merges the submitted values',
'',
- array('foo' => 'bar'),
+ ['foo' => 'bar'],
'/foo?foo=bar',
- ),
- array(
+ ],
+ [
'does not append values if the method is post',
'',
- array(),
+ [],
'/foo',
- ),
- array(
+ ],
+ [
'does not append values if the method is patch',
'',
- array(),
+ [],
'/foo',
'PUT',
- ),
- array(
+ ],
+ [
'does not append values if the method is delete',
'',
- array(),
+ [],
'/foo',
'DELETE',
- ),
- array(
+ ],
+ [
'does not append values if the method is put',
'',
- array(),
+ [],
'/foo',
'PATCH',
- ),
- array(
+ ],
+ [
'appends the form values to an existing query string',
'',
- array(),
+ [],
'/foo?bar=bar&foo=foo',
- ),
- array(
+ ],
+ [
'replaces query values with the form values',
'',
- array(),
+ [],
'/foo?bar=foo',
- ),
- array(
+ ],
+ [
'returns an empty URI if the action is empty',
'',
- array(),
+ [],
'/',
- ),
- array(
+ ],
+ [
'appends the form values even if the action is empty',
'',
- array(),
+ [],
'/?foo=foo',
- ),
- array(
+ ],
+ [
'chooses the path if the action attribute value is a sharp (#)',
'',
- array(),
+ [],
'/#',
- ),
- );
+ ],
+ ];
}
public function testHas()
@@ -684,20 +727,16 @@ public function testFormFieldRegistryAcceptAnyNames()
$registry->remove('[t:dbt%3adate;]data_daterange_enddate_value');
}
- /**
- * @expectedException \InvalidArgumentException
- */
public function testFormFieldRegistryGetThrowAnExceptionWhenTheFieldDoesNotExist()
{
+ $this->expectException('InvalidArgumentException');
$registry = new FormFieldRegistry();
$registry->get('foo');
}
- /**
- * @expectedException \InvalidArgumentException
- */
public function testFormFieldRegistrySetThrowAnExceptionWhenTheFieldDoesNotExist()
{
+ $this->expectException('InvalidArgumentException');
$registry = new FormFieldRegistry();
$registry->set('foo', null);
}
@@ -731,7 +770,7 @@ public function testFormRegistrySupportsMultivaluedFields()
$registry->add($this->getFormFieldMock('bar[baz]'));
$this->assertEquals(
- array('foo[0]', 'foo[1]', 'bar[5]', 'bar[6]', 'bar[baz]'),
+ ['foo[0]', 'foo[1]', 'bar[5]', 'bar[6]', 'bar[baz]'],
array_keys($registry->all())
);
}
@@ -765,37 +804,33 @@ public function testFormRegistrySetValues()
$registry->set('foo[3]', 3);
$registry->set('foo[bar][baz]', 'fbb');
- $registry->set('foo', array(
+ $registry->set('foo', [
2 => 2,
3 => 3,
- 'bar' => array(
+ 'bar' => [
'baz' => 'fbb',
- ),
- ));
+ ],
+ ]);
}
- /**
- * @expectedException \InvalidArgumentException
- * @expectedExceptionMessage Cannot set value on a compound field "foo[bar]".
- */
public function testFormRegistrySetValueOnCompoundField()
{
+ $this->expectException('InvalidArgumentException');
+ $this->expectExceptionMessage('Cannot set value on a compound field "foo[bar]".');
$registry = new FormFieldRegistry();
$registry->add($this->getFormFieldMock('foo[bar][baz]'));
$registry->set('foo[bar]', 'fbb');
}
- /**
- * @expectedException \InvalidArgumentException
- * @expectedExceptionMessage Unreachable field "0"
- */
public function testFormRegistrySetArrayOnNotCompoundField()
{
+ $this->expectException('InvalidArgumentException');
+ $this->expectExceptionMessage('Unreachable field "0"');
$registry = new FormFieldRegistry();
$registry->add($this->getFormFieldMock('bar'));
- $registry->set('bar', array('baz'));
+ $registry->set('bar', ['baz']);
}
public function testDifferentFieldTypesWithSameName()
@@ -824,7 +859,7 @@ protected function getFormFieldMock($name, $value = null)
{
$field = $this
->getMockBuilder('Symfony\\Component\\DomCrawler\\Field\\FormField')
- ->setMethods(array('getName', 'getValue', 'setValue', 'initialize'))
+ ->setMethods(['getName', 'getValue', 'setValue', 'initialize'])
->disableOriginalConstructor()
->getMock()
;
@@ -832,13 +867,13 @@ protected function getFormFieldMock($name, $value = null)
$field
->expects($this->any())
->method('getName')
- ->will($this->returnValue($name))
+ ->willReturn($name)
;
$field
->expects($this->any())
->method('getValue')
- ->will($this->returnValue($value))
+ ->willReturn($value)
;
return $field;
@@ -847,7 +882,7 @@ protected function getFormFieldMock($name, $value = null)
protected function createForm($form, $method = null, $currentUri = null)
{
$dom = new \DOMDocument();
- $dom->loadHTML(''.$form.'');
+ @$dom->loadHTML(''.$form.'');
$xPath = new \DOMXPath($dom);
$nodes = $xPath->query('//input | //button');
@@ -922,19 +957,49 @@ protected function createTestMultipleForm()
return $dom;
}
- public function testgetPhpValuesWithEmptyTextarea()
+ public function testGetPhpValuesWithEmptyTextarea()
{
$dom = new \DOMDocument();
$dom->loadHTML('
-
-
-
- ');
+
+
+ '
+ );
$nodes = $dom->getElementsByTagName('form');
$form = new Form($nodes->item(0), 'http://example.com');
- $this->assertEquals($form->getPhpValues(), array('example' => ''));
+ $this->assertEquals(['example' => ''], $form->getPhpValues());
+ }
+
+ public function testGetReturnTypes()
+ {
+ $dom = new \DOMDocument();
+ $dom->loadHTML('
+
+
+ '
+ );
+
+ $nodes = $dom->getElementsByTagName('form');
+ $form = new Form($nodes->item(0), 'http://example.com');
+
+ // FormField
+ $this->assertInstanceOf(TextareaFormField::class, $textareaFormField = $form->get('foo[collection][0][bar]'));
+
+ // Array of FormField
+ $this->assertSame([
+ 'bar' => $textareaFormField,
+ ], $form->get('foo[collection][0]'));
+
+ // Array of array of FormField
+ $this->assertSame([
+ [
+ 'bar' => $textareaFormField,
+ ],
+ ], $form->get('foo[collection]'));
}
}
diff --git a/Tests/ImageTest.php b/Tests/ImageTest.php
index 71a74c31..0b258a4b 100644
--- a/Tests/ImageTest.php
+++ b/Tests/ImageTest.php
@@ -11,15 +11,14 @@
namespace Symfony\Component\DomCrawler\Tests;
+use PHPUnit\Framework\TestCase;
use Symfony\Component\DomCrawler\Image;
-class ImageTest extends \PHPUnit_Framework_TestCase
+class ImageTest extends TestCase
{
- /**
- * @expectedException \LogicException
- */
public function testConstructorWithANonImgTag()
{
+ $this->expectException('LogicException');
$dom = new \DOMDocument();
$dom->loadHTML('