From eda9fff7c99788d2fd1def2907750fb1a5058e1f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 23 May 2023 17:24:39 +0200 Subject: [PATCH 01/18] [7.0] Bump to PHP 8.2 minimum --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index d703acb..c474827 100644 --- a/composer.json +++ b/composer.json @@ -16,13 +16,13 @@ } ], "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0", "masterminds/html5": "^2.6" }, "require-dev": { - "symfony/css-selector": "^5.4|^6.0|^7.0" + "symfony/css-selector": "^6.4|^7.0" }, "autoload": { "psr-4": { "Symfony\\Component\\DomCrawler\\": "" }, From 16858e6962c73ffbdce6f3da3728dcf3b7848a10 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sun, 2 Jul 2023 23:52:21 +0200 Subject: [PATCH 02/18] [Components] Convert to native return types --- AbstractUriElement.php | 4 +--- Crawler.php | 48 ++++++++++--------------------------- Field/ChoiceFormField.php | 20 ++++------------ Field/FileFormField.php | 20 ++++------------ Field/FormField.php | 8 ++----- Field/InputFormField.php | 4 +--- Field/TextareaFormField.php | 4 +--- Form.php | 12 +++------- Image.php | 5 +--- Link.php | 5 +--- 10 files changed, 32 insertions(+), 98 deletions(-) diff --git a/AbstractUriElement.php b/AbstractUriElement.php index f610b01..c0b76ca 100644 --- a/AbstractUriElement.php +++ b/AbstractUriElement.php @@ -115,9 +115,7 @@ protected function canonicalizePath(string $path): string * * @param \DOMElement $node A \DOMElement instance * - * @return void - * * @throws \LogicException If given node is not an anchor */ - abstract protected function setNode(\DOMElement $node); + abstract protected function setNode(\DOMElement $node): void; } diff --git a/Crawler.php b/Crawler.php index 274aeee..e2fcad0 100644 --- a/Crawler.php +++ b/Crawler.php @@ -58,7 +58,6 @@ class Crawler implements \Countable, \IteratorAggregate */ private bool $isHtml = true; - private ?HTML5 $html5Parser = null; /** @@ -92,10 +91,8 @@ public function getBaseHref(): ?string /** * Removes all the nodes. - * - * @return void */ - public function clear() + public function clear(): void { $this->nodes = []; $this->document = null; @@ -110,11 +107,9 @@ public function clear() * * @param \DOMNodeList|\DOMNode|\DOMNode[]|string|null $node A node * - * @return void - * * @throws \InvalidArgumentException when node is not the expected type */ - public function add(\DOMNodeList|\DOMNode|array|string|null $node) + public function add(\DOMNodeList|\DOMNode|array|string|null $node): void { if ($node instanceof \DOMNodeList) { $this->addNodeList($node); @@ -135,10 +130,8 @@ public function add(\DOMNodeList|\DOMNode|array|string|null $node) * 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. - * - * @return void */ - public function addContent(string $content, string $type = null) + public function addContent(string $content, string $type = null): void { if (empty($type)) { $type = str_starts_with($content, 'parseHtmlString($content, $charset); $this->addDocument($dom); @@ -213,10 +204,8 @@ public function addHtmlContent(string $content, string $charset = 'UTF-8') * @param int $options Bitwise OR of the libxml option constants * LIBXML_PARSEHUGE is dangerous, see * http://symfony.com/blog/security-release-symfony-2-0-17-released - * - * @return void */ - public function addXmlContent(string $content, string $charset = 'UTF-8', int $options = \LIBXML_NONET) + public function addXmlContent(string $content, string $charset = 'UTF-8', int $options = \LIBXML_NONET): void { // remove the default namespace if it's the only namespace to make XPath expressions simpler if (!str_contains($content, 'xmlns:')) { @@ -243,10 +232,8 @@ public function addXmlContent(string $content, string $charset = 'UTF-8', int $o * Adds a \DOMDocument to the list of nodes. * * @param \DOMDocument $dom A \DOMDocument instance - * - * @return void */ - public function addDocument(\DOMDocument $dom) + public function addDocument(\DOMDocument $dom): void { if ($dom->documentElement) { $this->addNode($dom->documentElement); @@ -257,10 +244,8 @@ public function addDocument(\DOMDocument $dom) * Adds a \DOMNodeList to the list of nodes. * * @param \DOMNodeList $nodes A \DOMNodeList instance - * - * @return void */ - public function addNodeList(\DOMNodeList $nodes) + public function addNodeList(\DOMNodeList $nodes): void { foreach ($nodes as $node) { if ($node instanceof \DOMNode) { @@ -273,10 +258,8 @@ public function addNodeList(\DOMNodeList $nodes) * Adds an array of \DOMNode instances to the list of nodes. * * @param \DOMNode[] $nodes An array of \DOMNode instances - * - * @return void */ - public function addNodes(array $nodes) + public function addNodes(array $nodes): void { foreach ($nodes as $node) { $this->add($node); @@ -287,10 +270,8 @@ public function addNodes(array $nodes) * Adds a \DOMNode instance to the list of nodes. * * @param \DOMNode $node A \DOMNode instance - * - * @return void */ - public function addNode(\DOMNode $node) + public function addNode(\DOMNode $node): void { if ($node instanceof \DOMDocument) { $node = $node->documentElement; @@ -656,7 +637,7 @@ public function outerHtml(): string * 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. */ - public function evaluate(string $xpath): array|Crawler + public function evaluate(string $xpath): array|self { if (null === $this->document) { throw new \LogicException('Cannot evaluate the expression on an uninitialized crawler.'); @@ -881,18 +862,13 @@ public function form(array $values = null, string $method = null): Form /** * Overloads a default namespace prefix to be used with XPath and CSS expressions. - * - * @return void */ - public function setDefaultNamespacePrefix(string $prefix) + public function setDefaultNamespacePrefix(string $prefix): void { $this->defaultNamespacePrefix = $prefix; } - /** - * @return void - */ - public function registerNamespace(string $prefix, string $namespace) + public function registerNamespace(string $prefix, string $namespace): void { $this->namespaces[$prefix] = $namespace; } diff --git a/Field/ChoiceFormField.php b/Field/ChoiceFormField.php index dcae549..ac7ee8d 100644 --- a/Field/ChoiceFormField.php +++ b/Field/ChoiceFormField.php @@ -60,10 +60,8 @@ public function isDisabled(): bool /** * Sets the value of the field. - * - * @return void */ - public function select(string|array|bool $value) + public function select(string|array|bool $value): void { $this->setValue($value); } @@ -71,11 +69,9 @@ public function select(string|array|bool $value) /** * Ticks a checkbox. * - * @return void - * * @throws \LogicException When the type provided is not correct */ - public function tick() + public function tick(): void { if ('checkbox' !== $this->type) { throw new \LogicException(sprintf('You cannot tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); @@ -87,11 +83,9 @@ public function tick() /** * Unticks a checkbox. * - * @return void - * * @throws \LogicException When the type provided is not correct */ - public function untick() + public function untick(): void { if ('checkbox' !== $this->type) { throw new \LogicException(sprintf('You cannot untick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); @@ -103,11 +97,9 @@ public function untick() /** * Sets the value of the field. * - * @return void - * * @throws \InvalidArgumentException When value type provided is not correct */ - public function setValue(string|array|bool|null $value) + public function setValue(string|array|bool|null $value): void { if ('checkbox' === $this->type && false === $value) { // uncheck @@ -182,11 +174,9 @@ public function isMultiple(): bool /** * Initializes the form field. * - * @return void - * * @throws \LogicException When node type is incorrect */ - protected function initialize() + protected function initialize(): void { if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) { throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName)); diff --git a/Field/FileFormField.php b/Field/FileFormField.php index 4ebe766..a52ffcb 100644 --- a/Field/FileFormField.php +++ b/Field/FileFormField.php @@ -23,11 +23,9 @@ class FileFormField extends FormField * * @param int $error The error code (one of UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, or UPLOAD_ERR_EXTENSION) * - * @return void - * * @throws \InvalidArgumentException When error code doesn't exist */ - public function setErrorCode(int $error) + public function setErrorCode(int $error): void { $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)) { @@ -39,20 +37,16 @@ public function setErrorCode(int $error) /** * Sets the value of the field. - * - * @return void */ - public function upload(?string $value) + public function upload(?string $value): void { $this->setValue($value); } /** * Sets the value of the field. - * - * @return void */ - public function setValue(?string $value) + public function setValue(?string $value): void { if (null !== $value && is_readable($value)) { $error = \UPLOAD_ERR_OK; @@ -82,10 +76,8 @@ public function setValue(?string $value) /** * Sets path to the file as string for simulating HTTP request. - * - * @return void */ - public function setFilePath(string $path) + public function setFilePath(string $path): void { parent::setValue($path); } @@ -93,11 +85,9 @@ public function setFilePath(string $path) /** * Initializes the form field. * - * @return void - * * @throws \LogicException When node type is incorrect */ - protected function initialize() + protected function initialize(): void { if ('input' !== $this->node->nodeName) { throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName)); diff --git a/Field/FormField.php b/Field/FormField.php index b97d54d..54fe3bb 100644 --- a/Field/FormField.php +++ b/Field/FormField.php @@ -92,10 +92,8 @@ public function getValue(): string|array|null /** * Sets the value of the field. - * - * @return void */ - public function setValue(?string $value) + public function setValue(?string $value): void { $this->value = $value ?? ''; } @@ -118,8 +116,6 @@ public function isDisabled(): bool /** * Initializes the form field. - * - * @return void */ - abstract protected function initialize(); + abstract protected function initialize(): void; } diff --git a/Field/InputFormField.php b/Field/InputFormField.php index 19d7735..2fd43d4 100644 --- a/Field/InputFormField.php +++ b/Field/InputFormField.php @@ -24,11 +24,9 @@ class InputFormField extends FormField /** * Initializes the form field. * - * @return void - * * @throws \LogicException When node type is incorrect */ - protected function initialize() + protected function initialize(): void { if ('input' !== $this->node->nodeName && 'button' !== $this->node->nodeName) { throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName)); diff --git a/Field/TextareaFormField.php b/Field/TextareaFormField.php index 5168c52..46b151f 100644 --- a/Field/TextareaFormField.php +++ b/Field/TextareaFormField.php @@ -21,11 +21,9 @@ class TextareaFormField extends FormField /** * Initializes the form field. * - * @return void - * * @throws \LogicException When node type is incorrect */ - protected function initialize() + protected function initialize(): void { if ('textarea' !== $this->node->nodeName) { throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName)); diff --git a/Form.php b/Form.php index 9e53bbb..1747bf9 100644 --- a/Form.php +++ b/Form.php @@ -245,10 +245,8 @@ public function has(string $name): bool /** * Removes a field from the form. - * - * @return void */ - public function remove(string $name) + public function remove(string $name): void { $this->fields->remove($name); } @@ -267,10 +265,8 @@ public function get(string $name): FormField|array /** * Sets a named field. - * - * @return void */ - public function set(FormField $field) + public function set(FormField $field): void { $this->fields->add($field); } @@ -353,11 +349,9 @@ public function disableValidation(): static * * Expects a 'submit' button \DOMElement and finds the corresponding form element, or the form element itself. * - * @return void - * * @throws \LogicException If given node is not a button or input or does not have a form ancestor */ - protected function setNode(\DOMElement $node) + protected function setNode(\DOMElement $node): void { $this->button = $node; if ('button' === $node->nodeName || ('input' === $node->nodeName && \in_array(strtolower($node->getAttribute('type')), ['submit', 'button', 'image']))) { diff --git a/Image.php b/Image.php index 725e3ae..5573928 100644 --- a/Image.php +++ b/Image.php @@ -26,10 +26,7 @@ protected function getRawUri(): string return $this->node->getAttribute('src'); } - /** - * @return void - */ - protected function setNode(\DOMElement $node) + protected function setNode(\DOMElement $node): void { if ('img' !== $node->nodeName) { throw new \LogicException(sprintf('Unable to visualize a "%s" tag.', $node->nodeName)); diff --git a/Link.php b/Link.php index 681a2f7..d6c273a 100644 --- a/Link.php +++ b/Link.php @@ -23,10 +23,7 @@ protected function getRawUri(): string return $this->node->getAttribute('href'); } - /** - * @return void - */ - protected function setNode(\DOMElement $node) + protected function setNode(\DOMElement $node): void { if ('a' !== $node->nodeName && 'area' !== $node->nodeName && 'link' !== $node->nodeName) { throw new \LogicException(sprintf('Unable to navigate from a "%s" tag.', $node->nodeName)); From 80faaff9eed1b9ffb35b8729acf495599412c21f Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 6 Jul 2023 11:45:23 +0200 Subject: [PATCH 03/18] [HttpFoundation][DomCrawler] Add planned arguments to Crawler::innerText() and sendHeaders() methods --- CHANGELOG.md | 5 +++++ Crawler.php | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be1c0ba..204b19d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +7.0 +--- + + * Add argument `$normalizeWhitespace` to `Crawler::innerText()` + 6.3 --- diff --git a/Crawler.php b/Crawler.php index e2fcad0..619940c 100644 --- a/Crawler.php +++ b/Crawler.php @@ -564,10 +564,8 @@ public function text(string $default = null, bool $normalizeWhitespace = true): * * @param bool $normalizeWhitespace Whether whitespaces should be trimmed and normalized to single spaces */ - public function innerText(/* bool $normalizeWhitespace = true */): string + public function innerText(bool $normalizeWhitespace = true): string { - $normalizeWhitespace = 1 <= \func_num_args() ? func_get_arg(0) : true; - foreach ($this->getNode(0)->childNodes as $childNode) { if (\XML_TEXT_NODE !== $childNode->nodeType) { continue; From 5bd33472b8e91bd08790f2010cd4315d6d63502b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 21 Jul 2023 15:36:26 +0200 Subject: [PATCH 04/18] Add types to public and protected properties --- AbstractUriElement.php | 17 +++-------------- Crawler.php | 5 +---- Field/FormField.php | 30 ++++++------------------------ 3 files changed, 10 insertions(+), 42 deletions(-) diff --git a/AbstractUriElement.php b/AbstractUriElement.php index c0b76ca..9a3712b 100644 --- a/AbstractUriElement.php +++ b/AbstractUriElement.php @@ -18,20 +18,9 @@ */ abstract class AbstractUriElement { - /** - * @var \DOMElement - */ - protected $node; - - /** - * @var string|null The method to use for the element - */ - protected $method; - - /** - * @var string The URI of the page where the element is embedded (or the base href) - */ - protected $currentUri; + protected \DOMElement $node; + protected ?string $method; + protected ?string $currentUri; /** * @param \DOMElement $node A \DOMElement instance diff --git a/Crawler.php b/Crawler.php index 15773f4..be56712 100644 --- a/Crawler.php +++ b/Crawler.php @@ -23,10 +23,7 @@ */ class Crawler implements \Countable, \IteratorAggregate { - /** - * @var string|null - */ - protected $uri; + protected ?string $uri; /** * The default namespace prefix to be used with XPath and CSS expressions. diff --git a/Field/FormField.php b/Field/FormField.php index 54fe3bb..f364d52 100644 --- a/Field/FormField.php +++ b/Field/FormField.php @@ -18,30 +18,12 @@ */ abstract class FormField { - /** - * @var \DOMElement - */ - protected $node; - /** - * @var string - */ - protected $name; - /** - * @var string - */ - protected $value; - /** - * @var \DOMDocument - */ - protected $document; - /** - * @var \DOMXPath - */ - protected $xpath; - /** - * @var bool - */ - protected $disabled; + protected \DOMElement $node; + protected string $name; + protected string|array|null $value = null; + protected \DOMDocument $document; + protected \DOMXPath $xpath; + protected bool $disabled = false; /** * @param \DOMElement $node The node associated with this field From 043715b671345f9c2ac2e26ffc5ef564e0b1a2ec Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 17 Aug 2023 17:52:53 +0200 Subject: [PATCH 05/18] [DomCrawler] Add argument `$default` to `Crawler::attr()` --- CHANGELOG.md | 1 + Crawler.php | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 223c246..5339595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add argument `$normalizeWhitespace` to `Crawler::innerText()` + * Add argument `$default` to `Crawler::attr()` 6.4 --- diff --git a/Crawler.php b/Crawler.php index 50984f3..315e225 100644 --- a/Crawler.php +++ b/Crawler.php @@ -504,9 +504,8 @@ public function children(string $selector = null): static * * @throws \InvalidArgumentException When current node is empty */ - public function attr(string $attribute/* , string $default = null */): ?string + public function attr(string $attribute, string $default = null): ?string { - $default = \func_num_args() > 1 ? func_get_arg(1) : null; if (!$this->nodes) { if (null !== $default) { return $default; From 60442ee47d66edfa47cb67f7ce034bcbb3cf63ae Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 8 Dec 2023 16:21:59 +0100 Subject: [PATCH 06/18] Use faster hashing algorithms when possible --- Field/FileFormField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Field/FileFormField.php b/Field/FileFormField.php index a52ffcb..e125a3d 100644 --- a/Field/FileFormField.php +++ b/Field/FileFormField.php @@ -55,7 +55,7 @@ public function setValue(?string $value): void $name = $info['basename']; // copy to a tmp location - $tmp = sys_get_temp_dir().'/'.strtr(substr(base64_encode(hash('sha256', uniqid(mt_rand(), true), true)), 0, 7), '/', '_'); + $tmp = sys_get_temp_dir().'/'.strtr(substr(base64_encode(hash('xxh128', uniqid(mt_rand(), true), true)), 0, 7), '/', '_'); if (\array_key_exists('extension', $info)) { $tmp .= '.'.$info['extension']; } From e88135810579dcb18c0a1e237ba4be09e9e6d807 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Mon, 18 Mar 2024 20:27:13 +0100 Subject: [PATCH 07/18] chore: CS fixes --- Form.php | 6 +++--- FormFieldRegistry.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Form.php b/Form.php index 20c16ec..f427137 100644 --- a/Form.php +++ b/Form.php @@ -336,7 +336,7 @@ public function offsetUnset(mixed $name): void public function disableValidation(): static { foreach ($this->fields->all() as $field) { - if ($field instanceof Field\ChoiceFormField) { + if ($field instanceof ChoiceFormField) { $field->disableValidation(); } } @@ -444,14 +444,14 @@ private function addField(\DOMElement $node): void $nodeName = $node->nodeName; if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) { - $this->set(new Field\ChoiceFormField($node)); + $this->set(new ChoiceFormField($node)); } elseif ('input' == $nodeName && 'radio' == strtolower($node->getAttribute('type'))) { // there may be other fields with the same name that are no choice // fields already registered (see https://github.com/symfony/symfony/issues/11689) if ($this->has($node->getAttribute('name')) && $this->get($node->getAttribute('name')) instanceof ChoiceFormField) { $this->get($node->getAttribute('name'))->addChoice($node); } else { - $this->set(new Field\ChoiceFormField($node)); + $this->set(new ChoiceFormField($node)); } } elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) { $this->set(new Field\FileFormField($node)); diff --git a/FormFieldRegistry.php b/FormFieldRegistry.php index 9e15716..9a7bbbe 100644 --- a/FormFieldRegistry.php +++ b/FormFieldRegistry.php @@ -106,7 +106,7 @@ public function has(string $name): bool public function set(string $name, mixed $value): void { $target = &$this->get($name); - if ((!\is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) { + if ((!\is_array($value) && $target instanceof FormField) || $target instanceof Field\ChoiceFormField) { $target->setValue($value); } elseif (\is_array($value)) { $registry = new static(); From 8b8fa5642e0950f1f5ea636d22bdc60cdb3ce254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Ostroluck=C3=BD?= Date: Sun, 31 Mar 2024 15:15:18 +0200 Subject: [PATCH 08/18] Remove unnecessary empty usages --- Crawler.php | 4 ++-- Field/ChoiceFormField.php | 2 +- Form.php | 4 ++-- FormFieldRegistry.php | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Crawler.php b/Crawler.php index 85d9c1e..518c353 100644 --- a/Crawler.php +++ b/Crawler.php @@ -130,7 +130,7 @@ public function add(\DOMNodeList|\DOMNode|array|string|null $node): void */ public function addContent(string $content, ?string $type = null): void { - if (empty($type)) { + if (!$type) { $type = str_starts_with($content, 'filterRelativeXPath('descendant-or-self::base')->extract(['href']); $baseHref = current($base); - if (\count($base) && !empty($baseHref)) { + if (\count($base) && $baseHref) { if ($this->baseHref) { $linkNode = $dom->createElement('a'); $linkNode->setAttribute('href', $baseHref); diff --git a/Field/ChoiceFormField.php b/Field/ChoiceFormField.php index ac7ee8d..068436e 100644 --- a/Field/ChoiceFormField.php +++ b/Field/ChoiceFormField.php @@ -236,7 +236,7 @@ private function buildOptionValue(\DOMElement $node): array $option = []; $defaultDefaultValue = 'select' === $this->node->nodeName ? '' : 'on'; - $defaultValue = (isset($node->nodeValue) && !empty($node->nodeValue)) ? $node->nodeValue : $defaultDefaultValue; + $defaultValue = (isset($node->nodeValue) && $node->nodeValue) ? $node->nodeValue : $defaultDefaultValue; $option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue; $option['disabled'] = $node->hasAttribute('disabled'); diff --git a/Form.php b/Form.php index f427137..bd3b2ea 100644 --- a/Form.php +++ b/Form.php @@ -121,7 +121,7 @@ public function getPhpValues(): array $values = []; foreach ($this->getValues() as $name => $value) { $qs = http_build_query([$name => $value], '', '&'); - if (!empty($qs)) { + if ($qs) { parse_str($qs, $expandedValue); $varName = substr($name, 0, \strlen(key($expandedValue))); $values[] = [$varName => current($expandedValue)]; @@ -146,7 +146,7 @@ public function getPhpFiles(): array $values = []; foreach ($this->getFiles() as $name => $value) { $qs = http_build_query([$name => $value], '', '&'); - if (!empty($qs)) { + if ($qs) { parse_str($qs, $expandedValue); $varName = substr($name, 0, \strlen(key($expandedValue))); diff --git a/FormFieldRegistry.php b/FormFieldRegistry.php index 9a7bbbe..2a4e586 100644 --- a/FormFieldRegistry.php +++ b/FormFieldRegistry.php @@ -136,7 +136,7 @@ public function all(): array private function walk(array $array, ?string $base = '', array &$output = []): array { foreach ($array as $k => $v) { - $path = empty($base) ? $k : sprintf('%s[%s]', $base, $k); + $path = $base ? sprintf('%s[%s]', $base, $k) : $k; if (\is_array($v)) { $this->walk($v, $path, $output); } else { From dc7ff231a8b0550fccc44baa4a7b184225db227d Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 3 Jun 2024 15:27:28 +0200 Subject: [PATCH 09/18] use constructor property promotion --- AbstractUriElement.php | 9 +++++---- Crawler.php | 11 ++++++----- Field/FormField.php | 7 +++---- Form.php | 10 ++++++---- Test/Constraint/CrawlerAnySelectorTextContains.php | 10 ++++------ Test/Constraint/CrawlerAnySelectorTextSame.php | 11 ++++------- .../CrawlerSelectorAttributeValueSame.php | 14 +++++--------- Test/Constraint/CrawlerSelectorExists.php | 7 +++---- Test/Constraint/CrawlerSelectorTextContains.php | 10 ++++------ Test/Constraint/CrawlerSelectorTextSame.php | 11 ++++------- 10 files changed, 44 insertions(+), 56 deletions(-) diff --git a/AbstractUriElement.php b/AbstractUriElement.php index d75dcd2..ee64479 100644 --- a/AbstractUriElement.php +++ b/AbstractUriElement.php @@ -20,7 +20,6 @@ abstract class AbstractUriElement { protected \DOMElement $node; protected ?string $method; - protected ?string $currentUri; /** * @param \DOMElement $node A \DOMElement instance @@ -29,11 +28,13 @@ abstract class AbstractUriElement * * @throws \InvalidArgumentException if the node is not a link */ - public function __construct(\DOMElement $node, ?string $currentUri = null, ?string $method = 'GET') - { + public function __construct( + \DOMElement $node, + protected ?string $currentUri = null, + ?string $method = 'GET', + ) { $this->setNode($node); $this->method = $method ? strtoupper($method) : null; - $this->currentUri = $currentUri; $elementUriIsRelative = null === parse_url(trim($this->getRawUri()), \PHP_URL_SCHEME); $baseUriIsAbsolute = null !== $this->currentUri && \in_array(strtolower(substr($this->currentUri, 0, 4)), ['http', 'file']); diff --git a/Crawler.php b/Crawler.php index a328dd0..7f43fed 100644 --- a/Crawler.php +++ b/Crawler.php @@ -23,8 +23,6 @@ */ class Crawler implements \Countable, \IteratorAggregate { - protected ?string $uri; - /** * The default namespace prefix to be used with XPath and CSS expressions. */ @@ -60,9 +58,12 @@ class Crawler implements \Countable, \IteratorAggregate /** * @param \DOMNodeList|\DOMNode|\DOMNode[]|string|null $node A Node to use as the base for the crawling */ - public function __construct(\DOMNodeList|\DOMNode|array|string|null $node = null, ?string $uri = null, ?string $baseHref = null, bool $useHtml5Parser = true) - { - $this->uri = $uri; + public function __construct( + \DOMNodeList|\DOMNode|array|string|null $node = null, + protected ?string $uri = null, + ?string $baseHref = null, + bool $useHtml5Parser = true, + ) { $this->baseHref = $baseHref ?: $uri; $this->html5Parser = $useHtml5Parser ? new HTML5(['disable_html_ns' => true]) : null; $this->cachedNamespaces = new \ArrayObject(); diff --git a/Field/FormField.php b/Field/FormField.php index f364d52..a30bf4e 100644 --- a/Field/FormField.php +++ b/Field/FormField.php @@ -18,7 +18,6 @@ */ abstract class FormField { - protected \DOMElement $node; protected string $name; protected string|array|null $value = null; protected \DOMDocument $document; @@ -28,9 +27,9 @@ abstract class FormField /** * @param \DOMElement $node The node associated with this field */ - public function __construct(\DOMElement $node) - { - $this->node = $node; + public function __construct( + protected \DOMElement $node, + ) { $this->name = $node->getAttribute('name'); $this->xpath = new \DOMXPath($node->ownerDocument); diff --git a/Form.php b/Form.php index bd3b2ea..5710275 100644 --- a/Form.php +++ b/Form.php @@ -23,7 +23,6 @@ class Form extends Link implements \ArrayAccess { private \DOMElement $button; private FormFieldRegistry $fields; - private ?string $baseHref; /** * @param \DOMElement $node A \DOMElement instance @@ -33,10 +32,13 @@ class Form extends Link implements \ArrayAccess * * @throws \LogicException if the node is not a button inside a form tag */ - public function __construct(\DOMElement $node, ?string $currentUri = null, ?string $method = null, ?string $baseHref = null) - { + public function __construct( + \DOMElement $node, + ?string $currentUri = null, + ?string $method = null, + private ?string $baseHref = null, + ) { parent::__construct($node, $currentUri, $method); - $this->baseHref = $baseHref; $this->initialize(); } diff --git a/Test/Constraint/CrawlerAnySelectorTextContains.php b/Test/Constraint/CrawlerAnySelectorTextContains.php index f209499..6c58b69 100644 --- a/Test/Constraint/CrawlerAnySelectorTextContains.php +++ b/Test/Constraint/CrawlerAnySelectorTextContains.php @@ -16,14 +16,12 @@ final class CrawlerAnySelectorTextContains extends Constraint { - private string $selector; - private string $expectedText; private bool $hasNode = false; - public function __construct(string $selector, string $expectedText) - { - $this->selector = $selector; - $this->expectedText = $expectedText; + public function __construct( + private string $selector, + private string $expectedText, + ) { } public function toString(): string diff --git a/Test/Constraint/CrawlerAnySelectorTextSame.php b/Test/Constraint/CrawlerAnySelectorTextSame.php index f4c8320..428d02b 100644 --- a/Test/Constraint/CrawlerAnySelectorTextSame.php +++ b/Test/Constraint/CrawlerAnySelectorTextSame.php @@ -16,13 +16,10 @@ final class CrawlerAnySelectorTextSame extends Constraint { - private string $selector; - private string $expectedText; - - public function __construct(string $selector, string $expectedText) - { - $this->selector = $selector; - $this->expectedText = $expectedText; + public function __construct( + private string $selector, + private string $expectedText, + ) { } public function toString(): string diff --git a/Test/Constraint/CrawlerSelectorAttributeValueSame.php b/Test/Constraint/CrawlerSelectorAttributeValueSame.php index f8df35b..cc97317 100644 --- a/Test/Constraint/CrawlerSelectorAttributeValueSame.php +++ b/Test/Constraint/CrawlerSelectorAttributeValueSame.php @@ -16,15 +16,11 @@ final class CrawlerSelectorAttributeValueSame extends Constraint { - private string $selector; - private string $attribute; - private string $expectedText; - - public function __construct(string $selector, string $attribute, string $expectedText) - { - $this->selector = $selector; - $this->attribute = $attribute; - $this->expectedText = $expectedText; + public function __construct( + private string $selector, + private string $attribute, + private string $expectedText, + ) { } public function toString(): string diff --git a/Test/Constraint/CrawlerSelectorExists.php b/Test/Constraint/CrawlerSelectorExists.php index 5d0c100..a99ef64 100644 --- a/Test/Constraint/CrawlerSelectorExists.php +++ b/Test/Constraint/CrawlerSelectorExists.php @@ -16,11 +16,10 @@ final class CrawlerSelectorExists extends Constraint { - private string $selector; - public function __construct(string $selector) - { - $this->selector = $selector; + public function __construct( + private string $selector, + ) { } public function toString(): string diff --git a/Test/Constraint/CrawlerSelectorTextContains.php b/Test/Constraint/CrawlerSelectorTextContains.php index c70fff1..cb56bc9 100644 --- a/Test/Constraint/CrawlerSelectorTextContains.php +++ b/Test/Constraint/CrawlerSelectorTextContains.php @@ -16,15 +16,13 @@ final class CrawlerSelectorTextContains extends Constraint { - private string $selector; - private string $expectedText; private bool $hasNode = false; private string $nodeText; - public function __construct(string $selector, string $expectedText) - { - $this->selector = $selector; - $this->expectedText = $expectedText; + public function __construct( + private string $selector, + private string $expectedText, + ) { } public function toString(): string diff --git a/Test/Constraint/CrawlerSelectorTextSame.php b/Test/Constraint/CrawlerSelectorTextSame.php index 269e23f..4ff00dc 100644 --- a/Test/Constraint/CrawlerSelectorTextSame.php +++ b/Test/Constraint/CrawlerSelectorTextSame.php @@ -16,13 +16,10 @@ final class CrawlerSelectorTextSame extends Constraint { - private string $selector; - private string $expectedText; - - public function __construct(string $selector, string $expectedText) - { - $this->selector = $selector; - $this->expectedText = $expectedText; + public function __construct( + private string $selector, + private string $expectedText, + ) { } public function toString(): string From f3b43bef97f72eb2f969fedc20d92773e2e3b0af Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 20 Jun 2024 17:52:34 +0200 Subject: [PATCH 10/18] Prefix all sprintf() calls --- AbstractUriElement.php | 2 +- Crawler.php | 28 +++++++++---------- Field/ChoiceFormField.php | 16 +++++------ Field/FileFormField.php | 6 ++-- Field/FormField.php | 2 +- Field/InputFormField.php | 2 +- Field/TextareaFormField.php | 2 +- Form.php | 6 ++-- FormFieldRegistry.php | 6 ++-- Image.php | 2 +- Link.php | 2 +- .../CrawlerAnySelectorTextContains.php | 8 +++--- .../Constraint/CrawlerAnySelectorTextSame.php | 6 ++-- .../CrawlerSelectorAttributeValueSame.php | 2 +- Test/Constraint/CrawlerSelectorCount.php | 4 +-- Test/Constraint/CrawlerSelectorExists.php | 2 +- .../CrawlerSelectorTextContains.php | 4 +-- Test/Constraint/CrawlerSelectorTextSame.php | 2 +- Tests/ImageTest.php | 2 +- Tests/LinkTest.php | 6 ++-- 20 files changed, 55 insertions(+), 55 deletions(-) diff --git a/AbstractUriElement.php b/AbstractUriElement.php index ee64479..f685a43 100644 --- a/AbstractUriElement.php +++ b/AbstractUriElement.php @@ -39,7 +39,7 @@ public function __construct( $elementUriIsRelative = null === parse_url(trim($this->getRawUri()), \PHP_URL_SCHEME); $baseUriIsAbsolute = null !== $this->currentUri && \in_array(strtolower(substr($this->currentUri, 0, 4)), ['http', 'file']); if ($elementUriIsRelative && !$baseUriIsAbsolute) { - throw new \InvalidArgumentException(sprintf('The URL of the element is relative, so you must define its base URI passing an absolute URL to the constructor of the "%s" class ("%s" was passed).', __CLASS__, $this->currentUri)); + throw new \InvalidArgumentException(\sprintf('The URL of the element is relative, so you must define its base URI passing an absolute URL to the constructor of the "%s" class ("%s" was passed).', __CLASS__, $this->currentUri)); } } diff --git a/Crawler.php b/Crawler.php index 7f43fed..23da2ed 100644 --- a/Crawler.php +++ b/Crawler.php @@ -118,7 +118,7 @@ public function add(\DOMNodeList|\DOMNode|array|string|null $node): void } 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".', get_debug_type($node))); + throw new \InvalidArgumentException(\sprintf('Expecting a DOMNodeList or DOMNode instance, an array, a string, or null, but got "%s".', get_debug_type($node))); } } @@ -732,7 +732,7 @@ public function filter(string $selector): static public function selectLink(string $value): static { return $this->filterRelativeXPath( - sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %1$s) or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %1$s)]]', static::xpathLiteral(' '.$value.' ')) + \sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %1$s) or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %1$s)]]', static::xpathLiteral(' '.$value.' ')) ); } @@ -741,7 +741,7 @@ public function selectLink(string $value): static */ public function selectImage(string $value): static { - $xpath = sprintf('descendant-or-self::img[contains(normalize-space(string(@alt)), %s)]', static::xpathLiteral($value)); + $xpath = \sprintf('descendant-or-self::img[contains(normalize-space(string(@alt)), %s)]', static::xpathLiteral($value)); return $this->filterRelativeXPath($xpath); } @@ -752,7 +752,7 @@ public function selectImage(string $value): static public function selectButton(string $value): static { return $this->filterRelativeXPath( - sprintf('descendant-or-self::input[((contains(%1$s, "submit") or contains(%1$s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %2$s)) or (contains(%1$s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %2$s)) or @id=%3$s or @name=%3$s] | descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %2$s) or @id=%3$s or @name=%3$s]', 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value)) + \sprintf('descendant-or-self::input[((contains(%1$s, "submit") or contains(%1$s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %2$s)) or (contains(%1$s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %2$s)) or @id=%3$s or @name=%3$s] | descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %2$s) or @id=%3$s or @name=%3$s]', 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value)) ); } @@ -770,7 +770,7 @@ public function link(string $method = 'get'): Link $node = $this->getNode(0); if (!$node instanceof \DOMElement) { - throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_debug_type($node))); + throw new \InvalidArgumentException(\sprintf('The selected node should be instance of DOMElement, got "%s".', get_debug_type($node))); } return new Link($node, $this->baseHref, $method); @@ -788,7 +788,7 @@ public function 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_debug_type($node))); + throw new \InvalidArgumentException(\sprintf('The current node list should contain only DOMElement instances, "%s" found.', get_debug_type($node))); } $links[] = new Link($node, $this->baseHref, 'get'); @@ -811,7 +811,7 @@ public function image(): Image $node = $this->getNode(0); if (!$node instanceof \DOMElement) { - throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_debug_type($node))); + throw new \InvalidArgumentException(\sprintf('The selected node should be instance of DOMElement, got "%s".', get_debug_type($node))); } return new Image($node, $this->baseHref); @@ -827,7 +827,7 @@ public function 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_debug_type($node))); + throw new \InvalidArgumentException(\sprintf('The current node list should contain only DOMElement instances, "%s" found.', get_debug_type($node))); } $images[] = new Image($node, $this->baseHref); @@ -850,7 +850,7 @@ public function form(?array $values = null, ?string $method = null): Form $node = $this->getNode(0); if (!$node instanceof \DOMElement) { - throw new \InvalidArgumentException(sprintf('The selected node should be instance of DOMElement, got "%s".', get_debug_type($node))); + throw new \InvalidArgumentException(\sprintf('The selected node should be instance of DOMElement, got "%s".', get_debug_type($node))); } $form = new Form($node, $this->uri, $method, $this->baseHref); @@ -894,18 +894,18 @@ public function registerNamespace(string $prefix, string $namespace): void public static function xpathLiteral(string $s): string { if (!str_contains($s, "'")) { - return sprintf("'%s'", $s); + return \sprintf("'%s'", $s); } if (!str_contains($s, '"')) { - return sprintf('"%s"', $s); + return \sprintf('"%s"', $s); } $string = $s; $parts = []; while (true) { if (false !== $pos = strpos($string, "'")) { - $parts[] = sprintf("'%s'", substr($string, 0, $pos)); + $parts[] = \sprintf("'%s'", substr($string, 0, $pos)); $parts[] = "\"'\""; $string = substr($string, $pos + 1); } else { @@ -914,7 +914,7 @@ public static function xpathLiteral(string $s): string } } - return sprintf('concat(%s)', implode(', ', $parts)); + return \sprintf('concat(%s)', implode(', ', $parts)); } /** @@ -1154,7 +1154,7 @@ private function discoverNamespace(\DOMXPath $domxpath, string $prefix): ?string } // 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)); + $namespaces = $domxpath->query(\sprintf('(//namespace::*[name()="%s"])[last()]', $this->defaultNamespacePrefix === $prefix ? '' : $prefix)); return $this->cachedNamespaces[$prefix] = ($node = $namespaces->item(0)) ? $node->nodeValue : null; } diff --git a/Field/ChoiceFormField.php b/Field/ChoiceFormField.php index 068436e..a486ff2 100644 --- a/Field/ChoiceFormField.php +++ b/Field/ChoiceFormField.php @@ -74,7 +74,7 @@ public function select(string|array|bool $value): void public function tick(): void { 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 tick "%s" as it is not a checkbox (%s).', $this->name, $this->type)); } $this->setValue(true); @@ -88,7 +88,7 @@ public function tick(): void public function untick(): void { if ('checkbox' !== $this->type) { - throw new \LogicException(sprintf('You cannot untick "%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); @@ -110,16 +110,16 @@ public function setValue(string|array|bool|null $value): void } else { if (\is_array($value)) { if (!$this->multiple) { - throw new \InvalidArgumentException(sprintf('The value for "%s" cannot be an array.', $this->name)); + 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) { @@ -144,7 +144,7 @@ public function setValue(string|array|bool|null $value): void public function addChoice(\DOMElement $node): void { if (!$this->multiple && 'radio' !== $this->type) { - throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name)); + throw new \LogicException(\sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name)); } $option = $this->buildOptionValue($node); @@ -179,11 +179,11 @@ public function isMultiple(): bool protected function initialize(): void { if ('input' !== $this->node->nodeName && 'select' !== $this->node->nodeName) { - throw new \LogicException(sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName)); + throw new \LogicException(\sprintf('A ChoiceFormField can only be created from an input or select tag (%s given).', $this->node->nodeName)); } 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; diff --git a/Field/FileFormField.php b/Field/FileFormField.php index e125a3d..6ef7c8c 100644 --- a/Field/FileFormField.php +++ b/Field/FileFormField.php @@ -29,7 +29,7 @@ public function setErrorCode(int $error): void { $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)); + throw new \InvalidArgumentException(\sprintf('The error code "%s" is not valid.', $error)); } $this->value = ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0]; @@ -90,11 +90,11 @@ public function setFilePath(string $path): void protected function initialize(): void { if ('input' !== $this->node->nodeName) { - throw new \LogicException(sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName)); + throw new \LogicException(\sprintf('A FileFormField can only be created from an input tag (%s given).', $this->node->nodeName)); } 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 a30bf4e..fb1a0ae 100644 --- a/Field/FormField.php +++ b/Field/FormField.php @@ -44,7 +44,7 @@ public function getLabel(): ?\DOMElement $xpath = new \DOMXPath($this->node->ownerDocument); if ($this->node->hasAttribute('id')) { - $labels = $xpath->query(sprintf('descendant::label[@for="%s"]', $this->node->getAttribute('id'))); + $labels = $xpath->query(\sprintf('descendant::label[@for="%s"]', $this->node->getAttribute('id'))); if ($labels->length > 0) { return $labels->item(0); } diff --git a/Field/InputFormField.php b/Field/InputFormField.php index 2fd43d4..1e26e5c 100644 --- a/Field/InputFormField.php +++ b/Field/InputFormField.php @@ -29,7 +29,7 @@ class InputFormField extends FormField protected function initialize(): void { if ('input' !== $this->node->nodeName && 'button' !== $this->node->nodeName) { - throw new \LogicException(sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName)); + throw new \LogicException(\sprintf('An InputFormField can only be created from an input or button tag (%s given).', $this->node->nodeName)); } $type = strtolower($this->node->getAttribute('type')); diff --git a/Field/TextareaFormField.php b/Field/TextareaFormField.php index 46b151f..b246776 100644 --- a/Field/TextareaFormField.php +++ b/Field/TextareaFormField.php @@ -26,7 +26,7 @@ class TextareaFormField extends FormField protected function initialize(): void { if ('textarea' !== $this->node->nodeName) { - throw new \LogicException(sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName)); + throw new \LogicException(\sprintf('A TextareaFormField can only be created from a textarea tag (%s given).', $this->node->nodeName)); } $this->value = ''; diff --git a/Form.php b/Form.php index 5710275..8b77e5a 100644 --- a/Form.php +++ b/Form.php @@ -362,7 +362,7 @@ protected function setNode(\DOMElement $node): void $formId = $node->getAttribute('form'); $form = $node->ownerDocument->getElementById($formId); if (null === $form) { - throw new \LogicException(sprintf('The selected node has an invalid form attribute (%s).', $formId)); + throw new \LogicException(\sprintf('The selected node has an invalid form attribute (%s).', $formId)); } $this->node = $form; @@ -375,7 +375,7 @@ protected function setNode(\DOMElement $node): void } } while ('form' !== $node->nodeName); } elseif ('form' !== $node->nodeName) { - throw new \LogicException(sprintf('Unable to submit on a "%s" tag.', $node->nodeName)); + throw new \LogicException(\sprintf('Unable to submit on a "%s" tag.', $node->nodeName)); } $this->node = $node; @@ -420,7 +420,7 @@ private function initialize(): void // 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=%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) or ancestor::turbo-stream )]', $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) or ancestor::turbo-stream )]', $formId)); foreach ($fieldNodes as $node) { $this->addField($node); } diff --git a/FormFieldRegistry.php b/FormFieldRegistry.php index 2a4e586..ef8b644 100644 --- a/FormFieldRegistry.php +++ b/FormFieldRegistry.php @@ -76,7 +76,7 @@ public function &get(string $name): FormField|array while ($segments) { $path = array_shift($segments); if (!\is_array($target) || !\array_key_exists($path, $target)) { - throw new \InvalidArgumentException(sprintf('Unreachable field "%s".', $path)); + throw new \InvalidArgumentException(\sprintf('Unreachable field "%s".', $path)); } $target = &$target[$path]; } @@ -116,7 +116,7 @@ public function set(string $name, mixed $value): void $this->set($k, $v); } } else { - throw new \InvalidArgumentException(sprintf('Cannot set value on a compound field "%s".', $name)); + throw new \InvalidArgumentException(\sprintf('Cannot set value on a compound field "%s".', $name)); } } @@ -136,7 +136,7 @@ public function all(): array private function walk(array $array, ?string $base = '', array &$output = []): array { foreach ($array as $k => $v) { - $path = $base ? sprintf('%s[%s]', $base, $k) : $k; + $path = $base ? \sprintf('%s[%s]', $base, $k) : $k; if (\is_array($v)) { $this->walk($v, $path, $output); } else { diff --git a/Image.php b/Image.php index dc7c0b4..964b978 100644 --- a/Image.php +++ b/Image.php @@ -29,7 +29,7 @@ protected function getRawUri(): string protected function setNode(\DOMElement $node): void { if ('img' !== $node->nodeName) { - throw new \LogicException(sprintf('Unable to visualize a "%s" tag.', $node->nodeName)); + throw new \LogicException(\sprintf('Unable to visualize a "%s" tag.', $node->nodeName)); } $this->node = $node; diff --git a/Link.php b/Link.php index d6c273a..da08de4 100644 --- a/Link.php +++ b/Link.php @@ -26,7 +26,7 @@ protected function getRawUri(): string protected function setNode(\DOMElement $node): void { if ('a' !== $node->nodeName && 'area' !== $node->nodeName && 'link' !== $node->nodeName) { - throw new \LogicException(sprintf('Unable to navigate from a "%s" tag.', $node->nodeName)); + throw new \LogicException(\sprintf('Unable to navigate from a "%s" tag.', $node->nodeName)); } $this->node = $node; diff --git a/Test/Constraint/CrawlerAnySelectorTextContains.php b/Test/Constraint/CrawlerAnySelectorTextContains.php index 6c58b69..33fe1a4 100644 --- a/Test/Constraint/CrawlerAnySelectorTextContains.php +++ b/Test/Constraint/CrawlerAnySelectorTextContains.php @@ -27,16 +27,16 @@ public function __construct( public function toString(): string { if ($this->hasNode) { - return sprintf('the text of any node matching selector "%s" contains "%s"', $this->selector, $this->expectedText); + return \sprintf('the text of any node matching selector "%s" contains "%s"', $this->selector, $this->expectedText); } - return sprintf('the Crawler has a node matching selector "%s"', $this->selector); + return \sprintf('the Crawler has a node matching selector "%s"', $this->selector); } protected function matches($other): bool { if (!$other instanceof Crawler) { - throw new \InvalidArgumentException(sprintf('"%s" constraint expected an argument of type "%s", got "%s".', self::class, Crawler::class, get_debug_type($other))); + throw new \InvalidArgumentException(\sprintf('"%s" constraint expected an argument of type "%s", got "%s".', self::class, Crawler::class, get_debug_type($other))); } $other = $other->filter($this->selector); @@ -59,7 +59,7 @@ protected function matches($other): bool protected function failureDescription($other): string { if (!$other instanceof Crawler) { - throw new \InvalidArgumentException(sprintf('"%s" constraint expected an argument of type "%s", got "%s".', self::class, Crawler::class, get_debug_type($other))); + throw new \InvalidArgumentException(\sprintf('"%s" constraint expected an argument of type "%s", got "%s".', self::class, Crawler::class, get_debug_type($other))); } return $this->toString(); diff --git a/Test/Constraint/CrawlerAnySelectorTextSame.php b/Test/Constraint/CrawlerAnySelectorTextSame.php index 428d02b..8e4bee0 100644 --- a/Test/Constraint/CrawlerAnySelectorTextSame.php +++ b/Test/Constraint/CrawlerAnySelectorTextSame.php @@ -24,13 +24,13 @@ public function __construct( public function toString(): string { - return sprintf('has at least a node matching selector "%s" with content "%s"', $this->selector, $this->expectedText); + return \sprintf('has at least a node matching selector "%s" with content "%s"', $this->selector, $this->expectedText); } protected function matches($other): bool { if (!$other instanceof Crawler) { - throw new \InvalidArgumentException(sprintf('"%s" constraint expected an argument of type "%s", got "%s".', self::class, Crawler::class, get_debug_type($other))); + throw new \InvalidArgumentException(\sprintf('"%s" constraint expected an argument of type "%s", got "%s".', self::class, Crawler::class, get_debug_type($other))); } $other = $other->filter($this->selector); @@ -46,7 +46,7 @@ protected function matches($other): bool protected function failureDescription($other): string { if (!$other instanceof Crawler) { - throw new \InvalidArgumentException(sprintf('"%s" constraint expected an argument of type "%s", got "%s".', self::class, Crawler::class, get_debug_type($other))); + throw new \InvalidArgumentException(\sprintf('"%s" constraint expected an argument of type "%s", got "%s".', self::class, Crawler::class, get_debug_type($other))); } return 'the Crawler '.$this->toString(); diff --git a/Test/Constraint/CrawlerSelectorAttributeValueSame.php b/Test/Constraint/CrawlerSelectorAttributeValueSame.php index cc97317..2657792 100644 --- a/Test/Constraint/CrawlerSelectorAttributeValueSame.php +++ b/Test/Constraint/CrawlerSelectorAttributeValueSame.php @@ -25,7 +25,7 @@ public function __construct( public function toString(): string { - return sprintf('has a node matching selector "%s" with attribute "%s" of value "%s"', $this->selector, $this->attribute, $this->expectedText); + return \sprintf('has a node matching selector "%s" with attribute "%s" of value "%s"', $this->selector, $this->attribute, $this->expectedText); } /** diff --git a/Test/Constraint/CrawlerSelectorCount.php b/Test/Constraint/CrawlerSelectorCount.php index 22ee1db..f56ad3b 100644 --- a/Test/Constraint/CrawlerSelectorCount.php +++ b/Test/Constraint/CrawlerSelectorCount.php @@ -24,7 +24,7 @@ public function __construct( public function toString(): string { - return sprintf('selector "%s" count is "%d"', $this->selector, $this->count); + return \sprintf('selector "%s" count is "%d"', $this->selector, $this->count); } /** @@ -40,6 +40,6 @@ protected function matches($crawler): bool */ protected function failureDescription($crawler): string { - return sprintf('the Crawler selector "%s" was expected to be found %d time(s) but was found %d time(s)', $this->selector, $this->count, \count($crawler->filter($this->selector))); + return \sprintf('the Crawler selector "%s" was expected to be found %d time(s) but was found %d time(s)', $this->selector, $this->count, \count($crawler->filter($this->selector))); } } diff --git a/Test/Constraint/CrawlerSelectorExists.php b/Test/Constraint/CrawlerSelectorExists.php index a99ef64..e1ffbb3 100644 --- a/Test/Constraint/CrawlerSelectorExists.php +++ b/Test/Constraint/CrawlerSelectorExists.php @@ -24,7 +24,7 @@ public function __construct( public function toString(): string { - return sprintf('matches selector "%s"', $this->selector); + return \sprintf('matches selector "%s"', $this->selector); } /** diff --git a/Test/Constraint/CrawlerSelectorTextContains.php b/Test/Constraint/CrawlerSelectorTextContains.php index cb56bc9..add2b87 100644 --- a/Test/Constraint/CrawlerSelectorTextContains.php +++ b/Test/Constraint/CrawlerSelectorTextContains.php @@ -28,10 +28,10 @@ public function __construct( public function toString(): string { if ($this->hasNode) { - return sprintf('the text "%s" of the node matching selector "%s" contains "%s"', $this->nodeText, $this->selector, $this->expectedText); + return \sprintf('the text "%s" of the node matching selector "%s" contains "%s"', $this->nodeText, $this->selector, $this->expectedText); } - return sprintf('the Crawler has a node matching selector "%s"', $this->selector); + return \sprintf('the Crawler has a node matching selector "%s"', $this->selector); } /** diff --git a/Test/Constraint/CrawlerSelectorTextSame.php b/Test/Constraint/CrawlerSelectorTextSame.php index 4ff00dc..580e9c3 100644 --- a/Test/Constraint/CrawlerSelectorTextSame.php +++ b/Test/Constraint/CrawlerSelectorTextSame.php @@ -24,7 +24,7 @@ public function __construct( public function toString(): string { - return sprintf('has a node matching selector "%s" with content "%s"', $this->selector, $this->expectedText); + return \sprintf('has a node matching selector "%s" with content "%s"', $this->selector, $this->expectedText); } /** diff --git a/Tests/ImageTest.php b/Tests/ImageTest.php index 4fdf7a9..61c448a 100644 --- a/Tests/ImageTest.php +++ b/Tests/ImageTest.php @@ -50,7 +50,7 @@ public function testAbsoluteBaseUriIsMandatoryWhenImageUrlIsRelative() public function testGetUri($url, $currentUri, $expected) { $dom = new \DOMDocument(); - $dom->loadHTML(sprintf('foo', $url)); + $dom->loadHTML(\sprintf('foo', $url)); $image = new Image($dom->getElementsByTagName('img')->item(0), $currentUri); $this->assertEquals($expected, $image->getUri()); diff --git a/Tests/LinkTest.php b/Tests/LinkTest.php index 9349088..3360c32 100644 --- a/Tests/LinkTest.php +++ b/Tests/LinkTest.php @@ -75,7 +75,7 @@ public function testGetMethod() public function testGetUri($url, $currentUri, $expected) { $dom = new \DOMDocument(); - $dom->loadHTML(sprintf('foo', $url)); + $dom->loadHTML(\sprintf('foo', $url)); $link = new Link($dom->getElementsByTagName('a')->item(0), $currentUri); $this->assertEquals($expected, $link->getUri()); @@ -87,7 +87,7 @@ public function testGetUri($url, $currentUri, $expected) public function testGetUriOnArea($url, $currentUri, $expected) { $dom = new \DOMDocument(); - $dom->loadHTML(sprintf('', $url)); + $dom->loadHTML(\sprintf('', $url)); $link = new Link($dom->getElementsByTagName('area')->item(0), $currentUri); $this->assertEquals($expected, $link->getUri()); @@ -99,7 +99,7 @@ public function testGetUriOnArea($url, $currentUri, $expected) public function testGetUriOnLink($url, $currentUri, $expected) { $dom = new \DOMDocument(); - $dom->loadHTML(sprintf('', $url)); + $dom->loadHTML(\sprintf('', $url)); $link = new Link($dom->getElementsByTagName('link')->item(0), $currentUri); $this->assertEquals($expected, $link->getUri()); From 86c99fa339ed82d312994e687b9be728809a147d Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Sun, 16 Jun 2024 17:17:26 +0200 Subject: [PATCH 11/18] chore: CS fixes --- Tests/FormTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Tests/FormTest.php b/Tests/FormTest.php index fcbd216..a169831 100644 --- a/Tests/FormTest.php +++ b/Tests/FormTest.php @@ -252,7 +252,7 @@ public static function provideInitializeValues() 'appends the submitted button value but not other submit buttons', ' ', - ['foobar' => ['InputFormField', 'foobar']], + ['foobar' => ['InputFormField', 'foobar']], ], [ 'turns an image input into x and y fields', @@ -263,38 +263,38 @@ public static function provideInitializeValues() 'returns textareas', ' ', - ['foo' => ['TextareaFormField', 'foo']], + ['foo' => ['TextareaFormField', 'foo']], ], [ 'returns inputs', ' ', - ['foo' => ['InputFormField', 'foo']], + ['foo' => ['InputFormField', 'foo']], ], [ 'returns checkboxes', ' ', - ['foo' => ['ChoiceFormField', 'foo']], + ['foo' => ['ChoiceFormField', 'foo']], ], [ 'returns not-checked checkboxes', ' ', - ['foo' => ['ChoiceFormField', false]], + ['foo' => ['ChoiceFormField', false]], ], [ 'returns radio buttons', ' ', - ['foo' => ['ChoiceFormField', 'bar']], + ['foo' => ['ChoiceFormField', 'bar']], ], [ 'returns file inputs', ' ', - ['foo' => ['FileFormField', ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]]], + ['foo' => ['FileFormField', ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => 4, 'size' => 0]]], ], ]; } @@ -830,7 +830,7 @@ public function testFormRegistrySetValues() 3 => 3, 'bar' => [ 'baz' => 'fbb', - ], + ], ]); } From 4861f0c8878548c99975bf7cb69c351066688433 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 6 Jul 2024 09:57:16 +0200 Subject: [PATCH 12/18] Update .gitattributes --- .gitattributes | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 84c7add..14c3c35 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,3 @@ /Tests export-ignore /phpunit.xml.dist export-ignore -/.gitattributes export-ignore -/.gitignore export-ignore +/.git* export-ignore From 3d4c11e4362a45e5c6c3a2c2f9acdb3c7e21d83e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 23 Jul 2024 12:30:55 +0200 Subject: [PATCH 13/18] stop using uniqid() to create temporary files --- Field/FileFormField.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Field/FileFormField.php b/Field/FileFormField.php index 6ef7c8c..5580fd8 100644 --- a/Field/FileFormField.php +++ b/Field/FileFormField.php @@ -55,8 +55,9 @@ public function setValue(?string $value): void $name = $info['basename']; // copy to a tmp location - $tmp = sys_get_temp_dir().'/'.strtr(substr(base64_encode(hash('xxh128', uniqid(mt_rand(), true), true)), 0, 7), '/', '_'); + $tmp = tempnam(sys_get_temp_dir(), $name); if (\array_key_exists('extension', $info)) { + unlink($tmp); $tmp .= '.'.$info['extension']; } if (is_file($tmp)) { From f160cc494dcb50a7315f4bfb68d6c6b1b910c447 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 29 Jul 2024 09:18:34 +0200 Subject: [PATCH 14/18] fix test on Windows --- Tests/Field/FileFormFieldTest.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Tests/Field/FileFormFieldTest.php b/Tests/Field/FileFormFieldTest.php index 7934402..00e84b1 100644 --- a/Tests/Field/FileFormFieldTest.php +++ b/Tests/Field/FileFormFieldTest.php @@ -71,9 +71,16 @@ public function testSetValue($method) $field->$method(__DIR__.'/../Fixtures/no-extension'); $value = $field->getValue(); + $tmpName = $value['tmp_name']; + + // Windows creates temporary files with a .tmp extension + if ('\\' === \DIRECTORY_SEPARATOR && str_ends_with($tmpName, '.tmp')) { + $tmpName = substr($tmpName, 0, -4); + } + $this->assertArrayNotHasKey( 'extension', - pathinfo($value['tmp_name']), + pathinfo($tmpName), "->$method() does not add a file extension in the tmp_name copy" ); } From 9a63ca5257a8efaed96a3df8bdfb032fda00d6ce Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 5 Aug 2024 09:12:25 +0200 Subject: [PATCH 15/18] Fix multiple CS errors --- Test/Constraint/CrawlerSelectorExists.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Test/Constraint/CrawlerSelectorExists.php b/Test/Constraint/CrawlerSelectorExists.php index e1ffbb3..7d9a607 100644 --- a/Test/Constraint/CrawlerSelectorExists.php +++ b/Test/Constraint/CrawlerSelectorExists.php @@ -16,7 +16,6 @@ final class CrawlerSelectorExists extends Constraint { - public function __construct( private string $selector, ) { From 01a9a2280d53222c26014d7b8d9349532f336926 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 5 Sep 2024 08:55:30 +0200 Subject: [PATCH 16/18] no longer use the internal TestFailure class --- .../CrawlerAnySelectorTextContainsTest.php | 29 ++++++++++--------- .../CrawlerAnySelectorTextSameTest.php | 10 ++----- .../CrawlerSelectorAttributeValueSameTest.php | 12 ++------ .../Constraint/CrawlerSelectorExistsTest.php | 12 ++------ .../CrawlerSelectorTextContainsTest.php | 29 ++++++++++--------- .../CrawlerSelectorTextSameTest.php | 12 ++------ 6 files changed, 43 insertions(+), 61 deletions(-) diff --git a/Tests/Test/Constraint/CrawlerAnySelectorTextContainsTest.php b/Tests/Test/Constraint/CrawlerAnySelectorTextContainsTest.php index d3c4d8a..d76335a 100644 --- a/Tests/Test/Constraint/CrawlerAnySelectorTextContainsTest.php +++ b/Tests/Test/Constraint/CrawlerAnySelectorTextContainsTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Test\Constraint\CrawlerAnySelectorTextContains; @@ -27,23 +26,25 @@ public function testConstraint() self::assertTrue($constraint->evaluate(new Crawler('
  • Bar
  • Foo'), '', true)); self::assertTrue($constraint->evaluate(new Crawler('
    • Bar
    • Foo Bar Baz'), '', true)); self::assertFalse($constraint->evaluate(new Crawler('
      • Bar
      • Baz'), '', true)); + } - try { - $constraint->evaluate(new Crawler('
        • Bar
        • Baz')); + public function testDoesNotMatchIfNodeDoesContainExpectedText() + { + $constraint = new CrawlerAnySelectorTextContains('ul li', 'Foo'); - self::fail(); - } catch (ExpectationFailedException $e) { - self::assertEquals("Failed asserting that the text of any node matching selector \"ul li\" contains \"Foo\".\n", TestFailure::exceptionToString($e)); - } + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the text of any node matching selector "ul li" contains "Foo".'); - try { - $constraint->evaluate(new Crawler('Foobar')); + $constraint->evaluate(new Crawler('<ul><li>Bar</li><li>Baz')); + } + + public function testDoesNotMatchIfNodeDoesNotExist() + { + $constraint = new CrawlerAnySelectorTextContains('ul li', 'Foo'); - self::fail(); - } catch (ExpectationFailedException $e) { - self::assertEquals("Failed asserting that the Crawler has a node matching selector \"ul li\".\n", TestFailure::exceptionToString($e)); + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Crawler has a node matching selector "ul li".'); - return; - } + $constraint->evaluate(new Crawler('<html><head><title>Foobar')); } } diff --git a/Tests/Test/Constraint/CrawlerAnySelectorTextSameTest.php b/Tests/Test/Constraint/CrawlerAnySelectorTextSameTest.php index 265d55c..ef88766 100644 --- a/Tests/Test/Constraint/CrawlerAnySelectorTextSameTest.php +++ b/Tests/Test/Constraint/CrawlerAnySelectorTextSameTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Test\Constraint\CrawlerAnySelectorTextSame; @@ -28,12 +27,9 @@ public function testConstraint() self::assertFalse($constraint->evaluate(new Crawler('<ul><li>Bar</li><li>Foo Bar Baz'), '', true)); self::assertFalse($constraint->evaluate(new Crawler('<ul><li>Bar</li><li>Baz'), '', true)); - try { - $constraint->evaluate(new Crawler('<ul><li>Bar</li><li>Baz')); + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Crawler has at least a node matching selector "ul li" with content "Foo".'); - self::fail(); - } catch (ExpectationFailedException $e) { - self::assertEquals("Failed asserting that the Crawler has at least a node matching selector \"ul li\" with content \"Foo\".\n", TestFailure::exceptionToString($e)); - } + $constraint->evaluate(new Crawler('<ul><li>Bar</li><li>Baz')); } } diff --git a/Tests/Test/Constraint/CrawlerSelectorAttributeValueSameTest.php b/Tests/Test/Constraint/CrawlerSelectorAttributeValueSameTest.php index 47ecdc8..c8c0b8c 100644 --- a/Tests/Test/Constraint/CrawlerSelectorAttributeValueSameTest.php +++ b/Tests/Test/Constraint/CrawlerSelectorAttributeValueSameTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorAttributeValueSame; @@ -26,14 +25,9 @@ public function testConstraint() $this->assertFalse($constraint->evaluate(new Crawler('<html><body><form><input type="text" name="username">'), '', true)); $this->assertFalse($constraint->evaluate(new Crawler('<html><head><title>Bar'), '', true)); - try { - $constraint->evaluate(new Crawler('<html><head><title>Bar')); - } catch (ExpectationFailedException $e) { - $this->assertEquals("Failed asserting that the Crawler has a node matching selector \"input[name=\"username\"]\" with attribute \"value\" of value \"Fabien\".\n", TestFailure::exceptionToString($e)); + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Crawler has a node matching selector "input[name="username"]" with attribute "value" of value "Fabien".'); - return; - } - - $this->fail(); + $constraint->evaluate(new Crawler('<html><head><title>Bar')); } } diff --git a/Tests/Test/Constraint/CrawlerSelectorExistsTest.php b/Tests/Test/Constraint/CrawlerSelectorExistsTest.php index cf5b2a5..2f67201 100644 --- a/Tests/Test/Constraint/CrawlerSelectorExistsTest.php +++ b/Tests/Test/Constraint/CrawlerSelectorExistsTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorExists; @@ -26,14 +25,9 @@ public function testConstraint() $constraint = new CrawlerSelectorExists('h1'); $this->assertFalse($constraint->evaluate(new Crawler('<html><head><title>'), '', true)); - try { - $constraint->evaluate(new Crawler('<html><head><title>')); - } catch (ExpectationFailedException $e) { - $this->assertEquals("Failed asserting that the Crawler matches selector \"h1\".\n", TestFailure::exceptionToString($e)); + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Crawler matches selector "h1".'); - return; - } - - $this->fail(); + $constraint->evaluate(new Crawler('<html><head><title>')); } } diff --git a/Tests/Test/Constraint/CrawlerSelectorTextContainsTest.php b/Tests/Test/Constraint/CrawlerSelectorTextContainsTest.php index 0d7656e..a3bef43 100644 --- a/Tests/Test/Constraint/CrawlerSelectorTextContainsTest.php +++ b/Tests/Test/Constraint/CrawlerSelectorTextContainsTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorTextContains; @@ -25,21 +24,25 @@ public function testConstraint() $this->assertTrue($constraint->evaluate(new Crawler('<html><head><title>Foobar'), '', true)); $this->assertFalse($constraint->evaluate(new Crawler('<html><head><title>Bar'), '', true)); $this->assertFalse($constraint->evaluate(new Crawler('<html><head></head><body>Bar'), '', true)); + } + + public function testDoesNotMatchIfNodeTextIsNotExpectedValue() + { + $constraint = new CrawlerSelectorTextContains('title', 'Foo'); - try { - $constraint->evaluate(new Crawler('<html><head><title>Bar')); + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the text "Bar" of the node matching selector "title" contains "Foo".'); - $this->fail(); - } catch (ExpectationFailedException $e) { - $this->assertEquals("Failed asserting that the text \"Bar\" of the node matching selector \"title\" contains \"Foo\".\n", TestFailure::exceptionToString($e)); - } + $constraint->evaluate(new Crawler('<html><head><title>Bar')); + } + + public function testDoesNotMatchIfNodeDoesNotExist() + { + $constraint = new CrawlerSelectorTextContains('title', 'Foo'); - try { - $constraint->evaluate(new Crawler('<html><head></head><body>Bar')); + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Crawler has a node matching selector "title".'); - $this->fail(); - } catch (ExpectationFailedException $e) { - $this->assertEquals("Failed asserting that the Crawler has a node matching selector \"title\".\n", TestFailure::exceptionToString($e)); - } + $constraint->evaluate(new Crawler('<html><head></head><body>Bar')); } } diff --git a/Tests/Test/Constraint/CrawlerSelectorTextSameTest.php b/Tests/Test/Constraint/CrawlerSelectorTextSameTest.php index aa0d9a9..ea9915a 100644 --- a/Tests/Test/Constraint/CrawlerSelectorTextSameTest.php +++ b/Tests/Test/Constraint/CrawlerSelectorTextSameTest.php @@ -13,7 +13,6 @@ use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestFailure; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorTextSame; @@ -25,14 +24,9 @@ public function testConstraint() $this->assertTrue($constraint->evaluate(new Crawler('<html><head><title>Foo'), '', true)); $this->assertFalse($constraint->evaluate(new Crawler('<html><head><title>Bar'), '', true)); - try { - $constraint->evaluate(new Crawler('<html><head><title>Bar')); - } catch (ExpectationFailedException $e) { - $this->assertEquals("Failed asserting that the Crawler has a node matching selector \"title\" with content \"Foo\".\n", TestFailure::exceptionToString($e)); + $this->expectException(ExpectationFailedException::class); + $this->expectExceptionMessage('Failed asserting that the Crawler has a node matching selector "title" with content "Foo".'); - return; - } - - $this->fail(); + $constraint->evaluate(new Crawler('<html><head><title>Bar')); } } From 5b9fdd22272288fc99a09c198faac4df855e5dc4 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois <alex.daubois@gmail.com> Date: Mon, 14 Oct 2024 20:03:05 +0200 Subject: [PATCH 17/18] Reduce common control flows --- Form.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Form.php b/Form.php index 8b77e5a..eb3787b 100644 --- a/Form.php +++ b/Form.php @@ -421,16 +421,14 @@ private function initialize(): void $formId = Crawler::xpathLiteral($this->node->getAttribute('id')); $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) or ancestor::turbo-stream )]', $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)] )[( not(ancestor::template) or ancestor::turbo-stream )]', $this->node); - foreach ($fieldNodes as $node) { - $this->addField($node); - } + } + + foreach ($fieldNodes as $node) { + $this->addField($node); } if ($this->baseHref && '' !== $this->node->getAttribute('action')) { From 0fabbc3d6a9c473b716a93fc8e7a537adb396166 Mon Sep 17 00:00:00 2001 From: Christian Flothmann <christian.flothmann@open.de> Date: Sun, 2 Mar 2025 16:03:52 +0100 Subject: [PATCH 18/18] replace assertEmpty() with stricter assertions --- Tests/Html5ParserCrawlerTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/Html5ParserCrawlerTest.php b/Tests/Html5ParserCrawlerTest.php index 58c1db8..79b8b51 100644 --- a/Tests/Html5ParserCrawlerTest.php +++ b/Tests/Html5ParserCrawlerTest.php @@ -43,7 +43,7 @@ public function testHtml5ParserWithInvalidHeadedContent(string $content) { $crawler = $this->createCrawler(); $crawler->addHtmlContent($content); - self::assertEmpty($crawler->filterXPath('//h1')->text(), '->addHtmlContent failed as expected'); + self::assertSame('', $crawler->filterXPath('//h1')->text(), '->addHtmlContent failed as expected'); } public function testHtml5ParserNotSameAsNativeParserForSpecificHtml()