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('Bar', $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 +
diff --git a/Tests/Field/ChoiceFormFieldTest.php b/Tests/Field/ChoiceFormFieldTest.php index 95922863..176ea592 100644 --- a/Tests/Field/ChoiceFormFieldTest.php +++ b/Tests/Field/ChoiceFormFieldTest.php @@ -19,15 +19,15 @@ public function testInitialize() { $node = $this->createNode('textarea', ''); try { - $field = new ChoiceFormField($node); + new ChoiceFormField($node); $this->fail('->initialize() throws a \LogicException if the node is not an input or a select'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input or a select'); } - $node = $this->createNode('input', '', array('type' => 'text')); + $node = $this->createNode('input', '', ['type' => 'text']); try { - $field = new ChoiceFormField($node); + new ChoiceFormField($node); $this->fail('->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is an input with a type different from checkbox or radio'); @@ -36,12 +36,12 @@ public function testInitialize() public function testGetType() { - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'foo']); $field = new ChoiceFormField($node); $this->assertEquals('radio', $field->getType(), '->getType() returns radio for radio buttons'); - $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo')); + $node = $this->createNode('input', '', ['type' => 'checkbox', 'name' => 'name', 'value' => 'foo']); $field = new ChoiceFormField($node); $this->assertEquals('checkbox', $field->getType(), '->getType() returns radio for a checkbox'); @@ -54,12 +54,12 @@ public function testGetType() public function testIsMultiple() { - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'foo']); $field = new ChoiceFormField($node); $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for radio buttons'); - $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo')); + $node = $this->createNode('input', '', ['type' => 'checkbox', 'name' => 'name', 'value' => 'foo']); $field = new ChoiceFormField($node); $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for checkboxes'); @@ -69,12 +69,12 @@ public function testIsMultiple() $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for selects without the multiple attribute'); - $node = $this->createNode('select', '', array('multiple' => 'multiple')); + $node = $this->createNode('select', '', ['multiple' => 'multiple']); $field = new ChoiceFormField($node); $this->assertTrue($field->isMultiple(), '->isMultiple() returns true for selects with the multiple attribute'); - $node = $this->createNode('select', '', array('multiple' => '')); + $node = $this->createNode('select', '', ['multiple' => '']); $field = new ChoiceFormField($node); $this->assertTrue($field->isMultiple(), '->isMultiple() returns true for selects with an empty multiple attribute'); @@ -82,14 +82,14 @@ public function testIsMultiple() public function testSelects() { - $node = $this->createSelectNode(array('foo' => false, 'bar' => false)); + $node = $this->createSelectNode(['foo' => false, 'bar' => false]); $field = new ChoiceFormField($node); $this->assertTrue($field->hasValue(), '->hasValue() returns true for selects'); $this->assertEquals('foo', $field->getValue(), '->getValue() returns the first option if none are selected'); $this->assertFalse($field->isMultiple(), '->isMultiple() returns false when no multiple attribute is defined'); - $node = $this->createSelectNode(array('foo' => false, 'bar' => true)); + $node = $this->createSelectNode(['foo' => false, 'bar' => true]); $field = new ChoiceFormField($node); $this->assertEquals('bar', $field->getValue(), '->getValue() returns the selected option'); @@ -105,7 +105,7 @@ public function testSelects() } try { - $field->setValue(array('foobar')); + $field->setValue(['foobar']); $this->fail('->setValue() throws an \InvalidArgumentException if the value is an array'); } catch (\InvalidArgumentException $e) { $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is an array'); @@ -114,7 +114,7 @@ public function testSelects() public function testSelectWithEmptyBooleanAttribute() { - $node = $this->createSelectNode(array('foo' => false, 'bar' => true), array(), ''); + $node = $this->createSelectNode(['foo' => false, 'bar' => true], [], ''); $field = new ChoiceFormField($node); $this->assertEquals('bar', $field->getValue()); @@ -122,7 +122,7 @@ public function testSelectWithEmptyBooleanAttribute() public function testSelectIsDisabled() { - $node = $this->createSelectNode(array('foo' => false, 'bar' => true), array('disabled' => 'disabled')); + $node = $this->createSelectNode(['foo' => false, 'bar' => true], ['disabled' => 'disabled']); $field = new ChoiceFormField($node); $this->assertTrue($field->isDisabled(), '->isDisabled() returns true for selects with a disabled attribute'); @@ -130,27 +130,27 @@ public function testSelectIsDisabled() public function testMultipleSelects() { - $node = $this->createSelectNode(array('foo' => false, 'bar' => false), array('multiple' => 'multiple')); + $node = $this->createSelectNode(['foo' => false, 'bar' => false], ['multiple' => 'multiple']); $field = new ChoiceFormField($node); - $this->assertEquals(array(), $field->getValue(), '->setValue() returns an empty array if multiple is true and no option is selected'); + $this->assertEquals([], $field->getValue(), '->setValue() returns an empty array if multiple is true and no option is selected'); $field->setValue('foo'); - $this->assertEquals(array('foo'), $field->getValue(), '->setValue() returns an array of options if multiple is true'); + $this->assertEquals(['foo'], $field->getValue(), '->setValue() returns an array of options if multiple is true'); $field->setValue('bar'); - $this->assertEquals(array('bar'), $field->getValue(), '->setValue() returns an array of options if multiple is true'); + $this->assertEquals(['bar'], $field->getValue(), '->setValue() returns an array of options if multiple is true'); - $field->setValue(array('foo', 'bar')); - $this->assertEquals(array('foo', 'bar'), $field->getValue(), '->setValue() returns an array of options if multiple is true'); + $field->setValue(['foo', 'bar']); + $this->assertEquals(['foo', 'bar'], $field->getValue(), '->setValue() returns an array of options if multiple is true'); - $node = $this->createSelectNode(array('foo' => true, 'bar' => true), array('multiple' => 'multiple')); + $node = $this->createSelectNode(['foo' => true, 'bar' => true], ['multiple' => 'multiple']); $field = new ChoiceFormField($node); - $this->assertEquals(array('foo', 'bar'), $field->getValue(), '->getValue() returns the selected options'); + $this->assertEquals(['foo', 'bar'], $field->getValue(), '->getValue() returns the selected options'); try { - $field->setValue(array('foobar')); + $field->setValue(['foobar']); $this->fail('->setValue() throws an \InvalidArgumentException if the value is not one of the options'); } catch (\InvalidArgumentException $e) { $this->assertTrue(true, '->setValue() throws an \InvalidArgumentException if the value is not one of the options'); @@ -159,18 +159,18 @@ public function testMultipleSelects() public function testRadioButtons() { - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'foo']); $field = new ChoiceFormField($node); - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'bar']); $field->addChoice($node); $this->assertFalse($field->hasValue(), '->hasValue() returns false when no radio button is selected'); $this->assertNull($field->getValue(), '->getValue() returns null if no radio button is selected'); $this->assertFalse($field->isMultiple(), '->isMultiple() returns false for radio buttons'); - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'foo']); $field = new ChoiceFormField($node); - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar', 'checked' => 'checked')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'bar', 'checked' => 'checked']); $field->addChoice($node); $this->assertTrue($field->hasValue(), '->hasValue() returns true when a radio button is selected'); @@ -189,9 +189,9 @@ public function testRadioButtons() public function testRadioButtonsWithEmptyBooleanAttribute() { - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'foo']); $field = new ChoiceFormField($node); - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar', 'checked' => '')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'bar', 'checked' => '']); $field->addChoice($node); $this->assertTrue($field->hasValue(), '->hasValue() returns true when a radio button is selected'); @@ -200,11 +200,11 @@ public function testRadioButtonsWithEmptyBooleanAttribute() public function testRadioButtonIsDisabled() { - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'foo', 'disabled' => 'disabled')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'foo', 'disabled' => 'disabled']); $field = new ChoiceFormField($node); - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'bar')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'bar']); $field->addChoice($node); - $node = $this->createNode('input', '', array('type' => 'radio', 'name' => 'name', 'value' => 'baz', 'disabled' => '')); + $node = $this->createNode('input', '', ['type' => 'radio', 'name' => 'name', 'value' => 'baz', 'disabled' => '']); $field->addChoice($node); $field->select('foo'); @@ -222,7 +222,7 @@ public function testRadioButtonIsDisabled() public function testCheckboxes() { - $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name')); + $node = $this->createNode('input', '', ['type' => 'checkbox', 'name' => 'name']); $field = new ChoiceFormField($node); $this->assertFalse($field->hasValue(), '->hasValue() returns false when the checkbox is not checked'); @@ -235,18 +235,18 @@ public function testCheckboxes() $this->assertTrue(true, '->initialize() throws a \LogicException for checkboxes'); } - $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked')); + $node = $this->createNode('input', '', ['type' => 'checkbox', 'name' => 'name', 'checked' => 'checked']); $field = new ChoiceFormField($node); $this->assertTrue($field->hasValue(), '->hasValue() returns true when the checkbox is checked'); $this->assertEquals('on', $field->getValue(), '->getValue() returns 1 if the checkbox is checked and has no value attribute'); - $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo')); + $node = $this->createNode('input', '', ['type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo']); $field = new ChoiceFormField($node); $this->assertEquals('foo', $field->getValue(), '->getValue() returns the value attribute if the checkbox is checked'); - $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo')); + $node = $this->createNode('input', '', ['type' => 'checkbox', 'name' => 'name', 'checked' => 'checked', 'value' => 'foo']); $field = new ChoiceFormField($node); $field->setValue(false); @@ -265,7 +265,7 @@ public function testCheckboxes() public function testCheckboxWithEmptyBooleanAttribute() { - $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'value' => 'foo', 'checked' => '')); + $node = $this->createNode('input', '', ['type' => 'checkbox', 'name' => 'name', 'value' => 'foo', 'checked' => '']); $field = new ChoiceFormField($node); $this->assertTrue($field->hasValue(), '->hasValue() returns true when the checkbox is checked'); @@ -274,7 +274,7 @@ public function testCheckboxWithEmptyBooleanAttribute() public function testTick() { - $node = $this->createSelectNode(array('foo' => false, 'bar' => false)); + $node = $this->createSelectNode(['foo' => false, 'bar' => false]); $field = new ChoiceFormField($node); try { @@ -284,7 +284,7 @@ public function testTick() $this->assertTrue(true, '->tick() throws a \LogicException for select boxes'); } - $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name')); + $node = $this->createNode('input', '', ['type' => 'checkbox', 'name' => 'name']); $field = new ChoiceFormField($node); $field->tick(); $this->assertEquals('on', $field->getValue(), '->tick() ticks checkboxes'); @@ -292,7 +292,7 @@ public function testTick() public function testUntick() { - $node = $this->createSelectNode(array('foo' => false, 'bar' => false)); + $node = $this->createSelectNode(['foo' => false, 'bar' => false]); $field = new ChoiceFormField($node); try { @@ -302,7 +302,7 @@ public function testUntick() $this->assertTrue(true, '->untick() throws a \LogicException for select boxes'); } - $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked')); + $node = $this->createNode('input', '', ['type' => 'checkbox', 'name' => 'name', 'checked' => 'checked']); $field = new ChoiceFormField($node); $field->untick(); $this->assertNull($field->getValue(), '->untick() unticks checkboxes'); @@ -310,14 +310,14 @@ public function testUntick() public function testSelect() { - $node = $this->createNode('input', '', array('type' => 'checkbox', 'name' => 'name', 'checked' => 'checked')); + $node = $this->createNode('input', '', ['type' => 'checkbox', 'name' => 'name', 'checked' => 'checked']); $field = new ChoiceFormField($node); $field->select(true); $this->assertEquals('on', $field->getValue(), '->select() changes the value of the field'); $field->select(false); $this->assertNull($field->getValue(), '->select() changes the value of the field'); - $node = $this->createSelectNode(array('foo' => false, 'bar' => false)); + $node = $this->createSelectNode(['foo' => false, 'bar' => false]); $field = new ChoiceFormField($node); $field->select('foo'); $this->assertEquals('foo', $field->getValue(), '->select() changes the selected option'); @@ -325,11 +325,11 @@ public function testSelect() public function testOptionWithNoValue() { - $node = $this->createSelectNodeWithEmptyOption(array('foo' => false, 'bar' => false)); + $node = $this->createSelectNodeWithEmptyOption(['foo' => false, 'bar' => false]); $field = new ChoiceFormField($node); $this->assertEquals('foo', $field->getValue()); - $node = $this->createSelectNodeWithEmptyOption(array('foo' => false, 'bar' => true)); + $node = $this->createSelectNodeWithEmptyOption(['foo' => false, 'bar' => true]); $field = new ChoiceFormField($node); $this->assertEquals('bar', $field->getValue()); $field->select('foo'); @@ -338,28 +338,28 @@ public function testOptionWithNoValue() public function testDisableValidation() { - $node = $this->createSelectNode(array('foo' => false, 'bar' => false)); + $node = $this->createSelectNode(['foo' => false, 'bar' => false]); $field = new ChoiceFormField($node); $field->disableValidation(); $field->setValue('foobar'); $this->assertEquals('foobar', $field->getValue(), '->disableValidation() allows to set a value which is not in the selected options.'); - $node = $this->createSelectNode(array('foo' => false, 'bar' => false), array('multiple' => 'multiple')); + $node = $this->createSelectNode(['foo' => false, 'bar' => false], ['multiple' => 'multiple']); $field = new ChoiceFormField($node); $field->disableValidation(); - $field->setValue(array('foobar')); - $this->assertEquals(array('foobar'), $field->getValue(), '->disableValidation() allows to set a value which is not in the selected options.'); + $field->setValue(['foobar']); + $this->assertEquals(['foobar'], $field->getValue(), '->disableValidation() allows to set a value which is not in the selected options.'); } public function testSelectWithEmptyValue() { - $node = $this->createSelectNodeWithEmptyOption(array('' => true, 'Female' => false, 'Male' => false)); + $node = $this->createSelectNodeWithEmptyOption(['' => true, 'Female' => false, 'Male' => false]); $field = new ChoiceFormField($node); $this->assertSame('', $field->getValue()); } - protected function createSelectNode($options, $attributes = array(), $selectedAttrText = 'selected') + protected function createSelectNode($options, $attributes = [], $selectedAttrText = 'selected') { $document = new \DOMDocument(); $node = $document->createElement('select'); @@ -381,7 +381,7 @@ protected function createSelectNode($options, $attributes = array(), $selectedAt return $node; } - protected function createSelectNodeWithEmptyOption($options, $attributes = array()) + protected function createSelectNodeWithEmptyOption($options, $attributes = []) { $document = new \DOMDocument(); $node = $document->createElement('select'); diff --git a/Tests/Field/FileFormFieldTest.php b/Tests/Field/FileFormFieldTest.php index 3ce49a46..b14bcc85 100644 --- a/Tests/Field/FileFormFieldTest.php +++ b/Tests/Field/FileFormFieldTest.php @@ -17,22 +17,22 @@ class FileFormFieldTest extends FormFieldTestCase { public function testInitialize() { - $node = $this->createNode('input', '', array('type' => 'file')); + $node = $this->createNode('input', '', ['type' => 'file']); $field = new FileFormField($node); - $this->assertEquals(array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0), $field->getValue(), '->initialize() sets the value of the field to no file uploaded'); + $this->assertEquals(['name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0], $field->getValue(), '->initialize() sets the value of the field to no file uploaded'); $node = $this->createNode('textarea', ''); try { - $field = new FileFormField($node); + new FileFormField($node); $this->fail('->initialize() throws a \LogicException if the node is not an input field'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not an input field'); } - $node = $this->createNode('input', '', array('type' => 'text')); + $node = $this->createNode('input', '', ['type' => 'text']); try { - $field = new FileFormField($node); + new FileFormField($node); $this->fail('->initialize() throws a \LogicException if the node is not a file input field'); } catch (\LogicException $e) { $this->assertTrue(true, '->initialize() throws a \LogicException if the node is not a file input field'); @@ -44,18 +44,18 @@ public function testInitialize() */ public function testSetValue($method) { - $node = $this->createNode('input', '', array('type' => 'file')); + $node = $this->createNode('input', '', ['type' => 'file']); $field = new FileFormField($node); $field->$method(null); - $this->assertEquals(array('name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0), $field->getValue(), "->$method() clears the uploaded file if the value is null"); + $this->assertEquals(['name' => '', 'type' => '', 'tmp_name' => '', 'error' => UPLOAD_ERR_NO_FILE, 'size' => 0], $field->getValue(), "->$method() clears the uploaded file if the value is null"); $field->$method(__FILE__); $value = $field->getValue(); $this->assertEquals(basename(__FILE__), $value['name'], "->$method() sets the name of the file field"); $this->assertEquals('', $value['type'], "->$method() sets the type of the file field"); - $this->assertInternalType('string', $value['tmp_name'], "->$method() sets the tmp_name of the file field"); + $this->assertIsString($value['tmp_name'], "->$method() sets the tmp_name of the file field"); $this->assertFileExists($value['tmp_name'], "->$method() creates a copy of the file at the tmp_name path"); $this->assertEquals(0, $value['error'], "->$method() sets the error of the file field"); $this->assertEquals(filesize(__FILE__), $value['size'], "->$method() sets the size of the file field"); @@ -80,15 +80,15 @@ public function testSetValue($method) public function getSetValueMethods() { - return array( - array('setValue'), - array('upload'), - ); + return [ + ['setValue'], + ['upload'], + ]; } public function testSetErrorCode() { - $node = $this->createNode('input', '', array('type' => 'file')); + $node = $this->createNode('input', '', ['type' => 'file']); $field = new FileFormField($node); $field->setErrorCode(UPLOAD_ERR_FORM_SIZE); @@ -105,7 +105,7 @@ public function testSetErrorCode() public function testSetRawFilePath() { - $node = $this->createNode('input', '', array('type' => 'file')); + $node = $this->createNode('input', '', ['type' => 'file']); $field = new FileFormField($node); $field->setFilePath(__FILE__); diff --git a/Tests/Field/FormFieldTest.php b/Tests/Field/FormFieldTest.php index 510f7628..e2daa039 100644 --- a/Tests/Field/FormFieldTest.php +++ b/Tests/Field/FormFieldTest.php @@ -17,7 +17,7 @@ class FormFieldTest extends FormFieldTestCase { public function testGetName() { - $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('name', $field->getName(), '->getName() returns the name of the field'); @@ -25,7 +25,7 @@ public function testGetName() public function testGetSetHasValue() { - $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(), '->getValue() returns the value of the field'); @@ -35,4 +35,38 @@ public function testGetSetHasValue() $this->assertTrue($field->hasValue(), '->hasValue() always returns true'); } + + public function testLabelReturnsNullIfNoneIsDefined() + { + $dom = new \DOMDocument(); + $dom->loadHTML('
'); + + $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('
'.$form.'
'); $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('