upgraded dependencies

This commit is contained in:
RafficMohammed
2023-01-08 01:59:16 +05:30
parent 51056e3aad
commit f9ae387337
6895 changed files with 133617 additions and 178680 deletions

View File

@@ -56,7 +56,7 @@ abstract class AbstractUriElement
/**
* Gets the node associated with this link.
*
* @return \DOMElement A \DOMElement instance
* @return \DOMElement
*/
public function getNode()
{
@@ -66,7 +66,7 @@ abstract class AbstractUriElement
/**
* Gets the method associated with this link.
*
* @return string The method
* @return string
*/
public function getMethod()
{
@@ -76,50 +76,11 @@ abstract class AbstractUriElement
/**
* Gets the URI associated with this link.
*
* @return string The URI
* @return string
*/
public function getUri()
{
$uri = trim($this->getRawUri());
// absolute URL?
if (null !== parse_url($uri, \PHP_URL_SCHEME)) {
return $uri;
}
// empty URI
if (!$uri) {
return $this->currentUri;
}
// an anchor
if ('#' === $uri[0]) {
return $this->cleanupAnchor($this->currentUri).$uri;
}
$baseUri = $this->cleanupUri($this->currentUri);
if ('?' === $uri[0]) {
return $baseUri.$uri;
}
// absolute URL with relative schema
if (str_starts_with($uri, '//')) {
return preg_replace('#^([^/]*)//.*$#', '$1', $baseUri).$uri;
}
$baseUri = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUri);
// absolute path
if ('/' === $uri[0]) {
return $baseUri.$uri;
}
// relative 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;
return UriResolver::resolve($this->getRawUri(), $this->currentUri);
}
/**
@@ -136,7 +97,7 @@ abstract class AbstractUriElement
*
* @return string
*/
protected function canonicalizePath($path)
protected function canonicalizePath(string $path)
{
if ('' === $path || '/' === $path) {
return $path;
@@ -167,36 +128,4 @@ abstract class AbstractUriElement
* @throws \LogicException If given node is not an anchor
*/
abstract protected function setNode(\DOMElement $node);
/**
* Removes the query string and the anchor from the given uri.
*/
private function cleanupUri(string $uri): string
{
return $this->cleanupQuery($this->cleanupAnchor($uri));
}
/**
* Remove the query string from the uri.
*/
private function cleanupQuery(string $uri): string
{
if (false !== $pos = strpos($uri, '?')) {
return substr($uri, 0, $pos);
}
return $uri;
}
/**
* Remove the anchor from the uri.
*/
private function cleanupAnchor(string $uri): string
{
if (false !== $pos = strpos($uri, '#')) {
return substr($uri, 0, $pos);
}
return $uri;
}
}

View File

@@ -1,6 +1,30 @@
CHANGELOG
=========
5.4
---
* Add `Crawler::innerText` method.
5.3
---
* The `parents()` method is deprecated. Use `ancestors()` instead.
* Marked the `containsOption()`, `availableOptionValues()`, and `disableValidation()` methods of the
`ChoiceFormField` class as internal
5.1.0
-----
* Added an internal cache layer on top of the CssSelectorConverter
* Added `UriResolver` to resolve an URI according to a base URI
5.0.0
-----
* Added argument `$selector` to `Crawler::children()`
* Added argument `$default` to `Crawler::text()` and `html()`
4.4.0
-----

View File

@@ -18,6 +18,8 @@ use Symfony\Component\CssSelector\CssSelectorConverter;
* Crawler eases navigation of a list of \DOMNode objects.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @implements \IteratorAggregate<int, \DOMNode>
*/
class Crawler implements \Countable, \IteratorAggregate
{
@@ -40,6 +42,13 @@ class Crawler implements \Countable, \IteratorAggregate
*/
private $namespaces = [];
/**
* A map of cached namespaces.
*
* @var \ArrayObject
*/
private $cachedNamespaces;
/**
* The base href value.
*
@@ -53,7 +62,7 @@ class Crawler implements \Countable, \IteratorAggregate
private $document;
/**
* @var \DOMNode[]
* @var list<\DOMNode>
*/
private $nodes = [];
@@ -77,6 +86,7 @@ class Crawler implements \Countable, \IteratorAggregate
$this->uri = $uri;
$this->baseHref = $baseHref ?: $uri;
$this->html5Parser = class_exists(HTML5::class) ? new HTML5(['disable_html_ns' => true]) : null;
$this->cachedNamespaces = new \ArrayObject();
$this->add($node);
}
@@ -108,6 +118,7 @@ class Crawler implements \Countable, \IteratorAggregate
{
$this->nodes = [];
$this->document = null;
$this->cachedNamespaces = new \ArrayObject();
}
/**
@@ -131,7 +142,7 @@ class Crawler implements \Countable, \IteratorAggregate
} 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".', get_debug_type($node)));
}
}
@@ -141,11 +152,8 @@ class Crawler implements \Countable, \IteratorAggregate
* 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 string|null $type The content type of the string
*/
public function addContent($content, $type = null)
public function addContent(string $content, string $type = null)
{
if (empty($type)) {
$type = str_starts_with($content, '<?xml') ? 'application/xml' : 'text/html';
@@ -184,11 +192,8 @@ class Crawler implements \Countable, \IteratorAggregate
* internal errors via libxml_use_internal_errors(true)
* and then, get the errors via libxml_get_errors(). Be
* sure to clear errors with libxml_clear_errors() afterward.
*
* @param string $content The HTML content
* @param string $charset The charset
*/
public function addHtmlContent($content, $charset = 'UTF-8')
public function addHtmlContent(string $content, string $charset = 'UTF-8')
{
$dom = $this->parseHtmlString($content, $charset);
$this->addDocument($dom);
@@ -218,13 +223,11 @@ class Crawler implements \Countable, \IteratorAggregate
* and then, get the errors via libxml_get_errors(). Be
* sure to clear errors with libxml_clear_errors() afterward.
*
* @param string $content The XML content
* @param string $charset The charset
* @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
* @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
*/
public function addXmlContent($content, $charset = 'UTF-8', $options = \LIBXML_NONET)
public function addXmlContent(string $content, string $charset = 'UTF-8', int $options = \LIBXML_NONET)
{
// remove the default namespace if it's the only namespace to make XPath expressions simpler
if (!preg_match('/xmlns:/', $content)) {
@@ -321,11 +324,9 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Returns a node given its position in the node list.
*
* @param int $position The position
*
* @return static
*/
public function eq($position)
public function eq(int $position)
{
if (isset($this->nodes[$position])) {
return $this->createSubCrawler($this->nodes[$position]);
@@ -363,12 +364,9 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Slices the list of nodes by $offset and $length.
*
* @param int $offset
* @param int $length
*
* @return static
*/
public function slice($offset = 0, $length = null)
public function slice(int $offset = 0, int $length = null)
{
return $this->createSubCrawler(\array_slice($this->nodes, $offset, $length));
}
@@ -502,13 +500,27 @@ class Crawler implements \Countable, \IteratorAggregate
}
/**
* Returns the parents nodes of the current selection.
* Returns the parent nodes of the current selection.
*
* @return static
*
* @throws \InvalidArgumentException When current node is empty
*/
public function parents()
{
trigger_deprecation('symfony/dom-crawler', '5.3', 'The %s() method is deprecated, use ancestors() instead.', __METHOD__);
return $this->ancestors();
}
/**
* Returns the ancestors of the current selection.
*
* @return static
*
* @throws \InvalidArgumentException When the current node is empty
*/
public function ancestors()
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -529,20 +541,13 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Returns the children nodes of the current selection.
*
* @param string|null $selector An optional CSS selector to filter children
*
* @return static
*
* @throws \InvalidArgumentException When current node is empty
* @throws \RuntimeException If the CssSelector Component is not available and $selector is provided
*/
public function children(/* string $selector = null */)
public function children(string $selector = null)
{
if (\func_num_args() < 1 && __CLASS__ !== static::class && __CLASS__ !== (new \ReflectionMethod($this, __FUNCTION__))->getDeclaringClass()->getName() && !$this instanceof \PHPUnit\Framework\MockObject\MockObject && !$this instanceof \Prophecy\Prophecy\ProphecySubjectInterface && !$this instanceof \Mockery\MockInterface) {
@trigger_error(sprintf('The "%s()" method will have a new "string $selector = null" argument in version 5.0, not defining it is deprecated since Symfony 4.2.', __METHOD__), \E_USER_DEPRECATED);
}
$selector = 0 < \func_num_args() ? func_get_arg(0) : null;
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
}
@@ -562,13 +567,11 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Returns the attribute value of the first node of the list.
*
* @param string $attribute The attribute name
*
* @return string|null The attribute value or null if the attribute does not exist
* @return string|null
*
* @throws \InvalidArgumentException When current node is empty
*/
public function attr($attribute)
public function attr(string $attribute)
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -582,7 +585,7 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Returns the node name of the first node of the list.
*
* @return string The node name
* @return string
*
* @throws \InvalidArgumentException When current node is empty
*/
@@ -603,15 +606,15 @@ class Crawler implements \Countable, \IteratorAggregate
* @param string|null $default When not null: the value to return when the current node is empty
* @param bool $normalizeWhitespace Whether whitespaces should be trimmed and normalized to single spaces
*
* @return string The node value
* @return string
*
* @throws \InvalidArgumentException When current node is empty
*/
public function text(/* string $default = null, bool $normalizeWhitespace = true */)
public function text(string $default = null, bool $normalizeWhitespace = true)
{
if (!$this->nodes) {
if (0 < \func_num_args() && null !== func_get_arg(0)) {
return (string) func_get_arg(0);
if (null !== $default) {
return $default;
}
throw new \InvalidArgumentException('The current node list is empty.');
@@ -619,35 +622,35 @@ class Crawler implements \Countable, \IteratorAggregate
$text = $this->getNode(0)->nodeValue;
if (\func_num_args() <= 1) {
if (trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $text), " \n\r\t\x0C") !== $text) {
@trigger_error(sprintf('"%s()" will normalize whitespaces by default in Symfony 5.0, set the second "$normalizeWhitespace" argument to false to retrieve the non-normalized version of the text.', __METHOD__), \E_USER_DEPRECATED);
}
return $text;
}
if (\func_num_args() > 1 && func_get_arg(1)) {
if ($normalizeWhitespace) {
return trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $text), " \n\r\t\x0C");
}
return $text;
}
/**
* Returns only the inner text that is the direct descendent of the current node, excluding any child nodes.
*/
public function innerText(): string
{
return $this->filterXPath('.//text()')->text();
}
/**
* Returns the first node of the list as HTML.
*
* @param string|null $default When not null: the value to return when the current node is empty
*
* @return string The node html
* @return string
*
* @throws \InvalidArgumentException When current node is empty
*/
public function html(/* string $default = null */)
public function html(string $default = null)
{
if (!$this->nodes) {
if (0 < \func_num_args() && null !== func_get_arg(0)) {
return (string) func_get_arg(0);
if (null !== $default) {
return $default;
}
throw new \InvalidArgumentException('The current node list is empty.');
@@ -690,11 +693,9 @@ class Crawler implements \Countable, \IteratorAggregate
* 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
* @return array|Crawler
*/
public function evaluate($xpath)
public function evaluate(string $xpath)
{
if (null === $this->document) {
throw new \LogicException('Cannot evaluate the expression on an uninitialized crawler.');
@@ -723,13 +724,10 @@ class Crawler implements \Countable, \IteratorAggregate
*
* $crawler->filter('h1 a')->extract(['_text', 'href']);
*
* @param array $attributes An array of attributes
*
* @return array An array of extracted values
* @return array
*/
public function extract($attributes)
public function extract(array $attributes)
{
$attributes = (array) $attributes;
$count = \count($attributes);
$data = [];
@@ -759,11 +757,9 @@ class Crawler implements \Countable, \IteratorAggregate
* This means that a child selector "div" or "./div" will match only
* the div elements of the current crawler, not their children.
*
* @param string $xpath An XPath expression
*
* @return static
*/
public function filterXPath($xpath)
public function filterXPath(string $xpath)
{
$xpath = $this->relativize($xpath);
@@ -780,13 +776,11 @@ class Crawler implements \Countable, \IteratorAggregate
*
* This method only works if you have installed the CssSelector Symfony Component.
*
* @param string $selector A CSS selector
*
* @return static
*
* @throws \RuntimeException if the CssSelector Component is not available
*/
public function filter($selector)
public function filter(string $selector)
{
$converter = $this->createCssSelectorConverter();
@@ -797,11 +791,9 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Selects links by name or alt value for clickable images.
*
* @param string $value The link text
*
* @return static
*/
public function selectLink($value)
public function selectLink(string $value)
{
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.' '))
@@ -811,11 +803,9 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Selects images by alt value.
*
* @param string $value The image alt
*
* @return static A new instance of Crawler with the filtered list of nodes
* @return static
*/
public function selectImage($value)
public function selectImage(string $value)
{
$xpath = sprintf('descendant-or-self::img[contains(normalize-space(string(@alt)), %s)]', static::xpathLiteral($value));
@@ -825,11 +815,9 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Selects a button by name or alt value for images.
*
* @param string $value The button text
*
* @return static
*/
public function selectButton($value)
public function selectButton(string $value)
{
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))
@@ -839,13 +827,11 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Returns a Link object for the first node in the list.
*
* @param string $method The method for the link (get by default)
*
* @return Link A Link instance
* @return Link
*
* @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement
*/
public function link($method = 'get')
public function link(string $method = 'get')
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -854,7 +840,7 @@ class Crawler implements \Countable, \IteratorAggregate
$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_debug_type($node)));
}
return new Link($node, $this->baseHref, $method);
@@ -863,7 +849,7 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Returns an array of Link objects for the nodes in the list.
*
* @return Link[] An array of Link instances
* @return Link[]
*
* @throws \InvalidArgumentException If the current node list contains non-DOMElement instances
*/
@@ -872,7 +858,7 @@ class Crawler implements \Countable, \IteratorAggregate
$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_debug_type($node)));
}
$links[] = new Link($node, $this->baseHref, 'get');
@@ -884,7 +870,7 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Returns an Image object for the first node in the list.
*
* @return Image An Image instance
* @return Image
*
* @throws \InvalidArgumentException If the current node list is empty
*/
@@ -897,7 +883,7 @@ class Crawler implements \Countable, \IteratorAggregate
$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_debug_type($node)));
}
return new Image($node, $this->baseHref);
@@ -906,14 +892,14 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Returns an array of Image objects for the nodes in the list.
*
* @return Image[] An array of Image instances
* @return Image[]
*/
public function images()
{
$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_debug_type($node)));
}
$images[] = new Image($node, $this->baseHref);
@@ -925,14 +911,11 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Returns a Form object for the first node in the list.
*
* @param array $values An array of values for the form fields
* @param string $method The method for the form
*
* @return Form A Form instance
* @return Form
*
* @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement
*/
public function form(array $values = null, $method = null)
public function form(array $values = null, string $method = null)
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -941,7 +924,7 @@ class Crawler implements \Countable, \IteratorAggregate
$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_debug_type($node)));
}
$form = new Form($node, $this->uri, $method, $this->baseHref);
@@ -955,19 +938,13 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* Overloads a default namespace prefix to be used with XPath and CSS expressions.
*
* @param string $prefix
*/
public function setDefaultNamespacePrefix($prefix)
public function setDefaultNamespacePrefix(string $prefix)
{
$this->defaultNamespacePrefix = $prefix;
}
/**
* @param string $prefix
* @param string $namespace
*/
public function registerNamespace($prefix, $namespace)
public function registerNamespace(string $prefix, string $namespace)
{
$this->namespaces[$prefix] = $namespace;
}
@@ -988,11 +965,9 @@ class Crawler implements \Countable, \IteratorAggregate
* echo Crawler::xpathLiteral('a\'b"c');
* //prints concat('a', "'", 'b"c')
*
* @param string $s String to be escaped
*
* @return string Converted string
* @return string
*/
public static function xpathLiteral($s)
public static function xpathLiteral(string $s)
{
if (!str_contains($s, "'")) {
return sprintf("'%s'", $s);
@@ -1025,14 +1000,16 @@ class Crawler implements \Countable, \IteratorAggregate
*
* @return static
*/
private function filterRelativeXPath(string $xpath)
private function filterRelativeXPath(string $xpath): object
{
$prefixes = $this->findNamespacePrefixes($xpath);
$crawler = $this->createSubCrawler(null);
if (null === $this->document) {
return $crawler;
}
$domxpath = $this->createDOMXPath($this->document, $this->findNamespacePrefixes($xpath));
foreach ($this->nodes as $node) {
$domxpath = $this->createDOMXPath($node->ownerDocument, $prefixes);
$crawler->add($domxpath->query($xpath, $node));
}
@@ -1130,11 +1107,9 @@ class Crawler implements \Countable, \IteratorAggregate
}
/**
* @param int $position
*
* @return \DOMNode|null
*/
public function getNode($position)
public function getNode(int $position)
{
return $this->nodes[$position] ?? null;
}
@@ -1149,7 +1124,7 @@ class Crawler implements \Countable, \IteratorAggregate
}
/**
* @return \ArrayIterator|\DOMNode[]
* @return \ArrayIterator<int, \DOMNode>
*/
#[\ReturnTypeWillChange]
public function getIterator()
@@ -1158,12 +1133,9 @@ class Crawler implements \Countable, \IteratorAggregate
}
/**
* @param \DOMElement $node
* @param string $siblingDir
*
* @return array
*/
protected function sibling($node, $siblingDir = 'nextSibling')
protected function sibling(\DOMNode $node, string $siblingDir = 'nextSibling')
{
$nodes = [];
@@ -1250,14 +1222,18 @@ class Crawler implements \Countable, \IteratorAggregate
*/
private function discoverNamespace(\DOMXPath $domxpath, string $prefix): ?string
{
if (isset($this->namespaces[$prefix])) {
if (\array_key_exists($prefix, $this->namespaces)) {
return $this->namespaces[$prefix];
}
if ($this->cachedNamespaces->offsetExists($prefix)) {
return $this->cachedNamespaces[$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));
return ($node = $namespaces->item(0)) ? $node->nodeValue : null;
return $this->cachedNamespaces[$prefix] = ($node = $namespaces->item(0)) ? $node->nodeValue : null;
}
private function findNamespacePrefixes(string $xpath): array
@@ -1276,12 +1252,13 @@ class Crawler implements \Countable, \IteratorAggregate
*
* @return static
*/
private function createSubCrawler($nodes)
private function createSubCrawler($nodes): object
{
$crawler = new static($nodes, $this->uri, $this->baseHref);
$crawler->isHtml = $this->isHtml;
$crawler->document = $this->document;
$crawler->namespaces = $this->namespaces;
$crawler->cachedNamespaces = $this->cachedNamespaces;
$crawler->html5Parser = $this->html5Parser;
return $crawler;

View File

@@ -113,7 +113,7 @@ class ChoiceFormField extends FormField
/**
* Sets the value of the field.
*
* @param string|array|bool $value The value of the field
* @param string|array|bool|null $value The value of the field
*
* @throws \InvalidArgumentException When value type provided is not correct
*/
@@ -176,7 +176,7 @@ class ChoiceFormField extends FormField
/**
* Returns the type of the choice field (radio, select, or checkbox).
*
* @return string The type
* @return string
*/
public function getType()
{
@@ -186,7 +186,7 @@ class ChoiceFormField extends FormField
/**
* Returns true if the field accepts multiple values.
*
* @return bool true if the field accepts multiple values, false otherwise
* @return bool
*/
public function isMultiple()
{
@@ -268,12 +268,11 @@ class ChoiceFormField extends FormField
/**
* Checks whether given value is in the existing options.
*
* @param string $optionValue
* @param array $options
* @internal since Symfony 5.3
*
* @return bool
*/
public function containsOption($optionValue, $options)
public function containsOption(string $optionValue, array $options)
{
if ($this->validationDisabled) {
return true;
@@ -291,6 +290,8 @@ class ChoiceFormField extends FormField
/**
* Returns list of available field options.
*
* @internal since Symfony 5.3
*
* @return array
*/
public function availableOptionValues()
@@ -307,7 +308,9 @@ class ChoiceFormField extends FormField
/**
* Disables the internal validation of the field.
*
* @return self
* @internal since Symfony 5.3
*
* @return $this
*/
public function disableValidation()
{

View File

@@ -25,7 +25,7 @@ class FileFormField extends FormField
*
* @throws \InvalidArgumentException When error code doesn't exist
*/
public function setErrorCode($error)
public function setErrorCode(int $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)) {
@@ -37,20 +37,16 @@ class FileFormField extends FormField
/**
* Sets the value of the field.
*
* @param string $value The value of the field
*/
public function upload($value)
public function upload(?string $value)
{
$this->setValue($value);
}
/**
* Sets the value of the field.
*
* @param string|null $value The value of the field
*/
public function setValue($value)
public function setValue(?string $value)
{
if (null !== $value && is_readable($value)) {
$error = \UPLOAD_ERR_OK;
@@ -80,10 +76,8 @@ class FileFormField extends FormField
/**
* Sets path to the file as string for simulating HTTP request.
*
* @param string $path The path to the file
*/
public function setFilePath($path)
public function setFilePath(string $path)
{
parent::setValue($path);
}

View File

@@ -79,7 +79,7 @@ abstract class FormField
/**
* Returns the name of the field.
*
* @return string The name of the field
* @return string
*/
public function getName()
{
@@ -89,7 +89,7 @@ abstract class FormField
/**
* Gets the value of the field.
*
* @return string|array The value of the field
* @return string|array|null
*/
public function getValue()
{
@@ -98,18 +98,16 @@ abstract class FormField
/**
* Sets the value of the field.
*
* @param string|array|bool|null $value The value of the field
*/
public function setValue($value)
public function setValue(?string $value)
{
$this->value = (string) $value;
$this->value = $value ?? '';
}
/**
* Returns true if the field should be included in the submitted values.
*
* @return bool true if the field should be included in the submitted values, false otherwise
* @return bool
*/
public function hasValue()
{

View File

@@ -55,7 +55,7 @@ class Form extends Link implements \ArrayAccess
/**
* Gets the form node associated with this form.
*
* @return \DOMElement A \DOMElement instance
* @return \DOMElement
*/
public function getFormNode()
{
@@ -83,7 +83,7 @@ class Form extends Link implements \ArrayAccess
*
* The returned array does not include file fields (@see getFiles).
*
* @return array An array of field values
* @return array
*/
public function getValues()
{
@@ -104,7 +104,7 @@ class Form extends Link implements \ArrayAccess
/**
* Gets the file field values.
*
* @return array An array of file field values
* @return array
*/
public function getFiles()
{
@@ -133,7 +133,7 @@ class Form extends Link implements \ArrayAccess
* This method converts fields with the array notation
* (like foo[bar] to arrays) like PHP does.
*
* @return array An array of field values
* @return array
*/
public function getPhpValues()
{
@@ -143,11 +143,11 @@ class Form extends Link implements \ArrayAccess
if (!empty($qs)) {
parse_str($qs, $expandedValue);
$varName = substr($name, 0, \strlen(key($expandedValue)));
$values = array_replace_recursive($values, [$varName => current($expandedValue)]);
$values[] = [$varName => current($expandedValue)];
}
}
return $values;
return array_replace_recursive([], ...$values);
}
/**
@@ -160,7 +160,7 @@ class Form extends Link implements \ArrayAccess
* For a compound file field foo[bar] it will create foo[bar][name],
* instead of foo[name][bar] which would be found in $_FILES.
*
* @return array An array of file field values
* @return array
*/
public function getPhpFiles()
{
@@ -182,11 +182,11 @@ class Form extends Link implements \ArrayAccess
reset($expandedValue);
$values = array_replace_recursive($values, [$varName => current($expandedValue)]);
$values[] = [$varName => current($expandedValue)];
}
}
return $values;
return array_replace_recursive([], ...$values);
}
/**
@@ -196,7 +196,7 @@ class Form extends Link implements \ArrayAccess
* This method merges the value if the method is GET to mimics
* browser behavior.
*
* @return string The URI
* @return string
*/
public function getUri()
{
@@ -234,7 +234,7 @@ class Form extends Link implements \ArrayAccess
*
* If no method is defined in the form, GET is returned.
*
* @return string The method
* @return string
*/
public function getMethod()
{
@@ -263,21 +263,17 @@ class Form extends Link implements \ArrayAccess
/**
* Returns true if the named field exists.
*
* @param string $name The field name
*
* @return bool true if the field exists, false otherwise
* @return bool
*/
public function has($name)
public function has(string $name)
{
return $this->fields->has($name);
}
/**
* Removes a field from the form.
*
* @param string $name The field name
*/
public function remove($name)
public function remove(string $name)
{
$this->fields->remove($name);
}
@@ -285,13 +281,11 @@ class Form extends Link implements \ArrayAccess
/**
* Gets a named field.
*
* @param string $name The field name
*
* @return FormField|FormField[]|FormField[][] The value of the field
* @return FormField|FormField[]|FormField[][]
*
* @throws \InvalidArgumentException When field is not present in this form
*/
public function get($name)
public function get(string $name)
{
return $this->fields->get($name);
}
@@ -319,7 +313,7 @@ class Form extends Link implements \ArrayAccess
*
* @param string $name The field name
*
* @return bool true if the field exists, false otherwise
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($name)
@@ -332,7 +326,7 @@ class Form extends Link implements \ArrayAccess
*
* @param string $name The field name
*
* @return FormField|FormField[]|FormField[][] The value of the field
* @return FormField|FormField[]|FormField[][]
*
* @throws \InvalidArgumentException if the field does not exist
*/
@@ -374,7 +368,7 @@ class Form extends Link implements \ArrayAccess
/**
* Disables validation.
*
* @return self
* @return $this
*/
public function disableValidation()
{

View File

@@ -47,7 +47,7 @@ class FormFieldRegistry
}
/**
* Removes a field based on the fully qualifed name and its children from the registry.
* Removes a field based on the fully qualified name and its children from the registry.
*/
public function remove(string $name)
{
@@ -64,9 +64,9 @@ class FormFieldRegistry
}
/**
* Returns the value of the field based on the fully qualifed name and its children.
* Returns the value of the field based on the fully qualified name and its children.
*
* @return FormField|FormField[]|FormField[][] The value of the field
* @return FormField|FormField[]|FormField[][]
*
* @throws \InvalidArgumentException if the field does not exist
*/
@@ -87,8 +87,6 @@ class FormFieldRegistry
/**
* Tests whether the form has the given field based on the fully qualified name.
*
* @return bool Whether the form has the given field
*/
public function has(string $name): bool
{
@@ -157,7 +155,7 @@ class FormFieldRegistry
*
* getSegments('base[foo][3][]') = ['base', 'foo, '3', ''];
*
* @return string[] The list of segments
* @return string[]
*/
private function getSegments(string $name): array
{

View File

@@ -0,0 +1,136 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\DomCrawler;
/**
* The UriResolver class takes an URI (relative, absolute, fragment, etc.)
* and turns it into an absolute URI against another given base URI.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
class UriResolver
{
/**
* Resolves a URI according to a base URI.
*
* For example if $uri=/foo/bar and $baseUri=https://symfony.com it will
* return https://symfony.com/foo/bar
*
* If the $uri is not absolute you must pass an absolute $baseUri
*/
public static function resolve(string $uri, ?string $baseUri): string
{
$uri = trim($uri);
// absolute URL?
if (null !== parse_url($uri, \PHP_URL_SCHEME)) {
return $uri;
}
if (null === $baseUri) {
throw new \InvalidArgumentException('The URI is relative, so you must define its base URI passing an absolute URL.');
}
// empty URI
if (!$uri) {
return $baseUri;
}
// an anchor
if ('#' === $uri[0]) {
return self::cleanupAnchor($baseUri).$uri;
}
$baseUriCleaned = self::cleanupUri($baseUri);
if ('?' === $uri[0]) {
return $baseUriCleaned.$uri;
}
// absolute URL with relative schema
if (0 === strpos($uri, '//')) {
return preg_replace('#^([^/]*)//.*$#', '$1', $baseUriCleaned).$uri;
}
$baseUriCleaned = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUriCleaned);
// absolute path
if ('/' === $uri[0]) {
return $baseUriCleaned.$uri;
}
// relative path
$path = parse_url(substr($baseUri, \strlen($baseUriCleaned)), \PHP_URL_PATH);
$path = self::canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri);
return $baseUriCleaned.('' === $path || '/' !== $path[0] ? '/' : '').$path;
}
/**
* Returns the canonicalized URI path (see RFC 3986, section 5.2.4).
*/
private static function canonicalizePath(string $path): string
{
if ('' === $path || '/' === $path) {
return $path;
}
if ('.' === substr($path, -1)) {
$path .= '/';
}
$output = [];
foreach (explode('/', $path) as $segment) {
if ('..' === $segment) {
array_pop($output);
} elseif ('.' !== $segment) {
$output[] = $segment;
}
}
return implode('/', $output);
}
/**
* Removes the query string and the anchor from the given uri.
*/
private static function cleanupUri(string $uri): string
{
return self::cleanupQuery(self::cleanupAnchor($uri));
}
/**
* Removes the query string from the uri.
*/
private static function cleanupQuery(string $uri): string
{
if (false !== $pos = strpos($uri, '?')) {
return substr($uri, 0, $pos);
}
return $uri;
}
/**
* Removes the anchor from the uri.
*/
private static function cleanupAnchor(string $uri): string
{
if (false !== $pos = strpos($uri, '#')) {
return substr($uri, 0, $pos);
}
return $uri;
}
}

View File

@@ -16,13 +16,14 @@
}
],
"require": {
"php": ">=7.1.3",
"php": ">=7.2.5",
"symfony/deprecation-contracts": "^2.1|^3",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php80": "^1.16"
},
"require-dev": {
"symfony/css-selector": "^3.4|^4.0|^5.0",
"symfony/css-selector": "^4.4|^5.0|^6.0",
"masterminds/html5": "^2.6"
},
"conflict": {