initial commit
This commit is contained in:
111
vendor/symfony/dom-crawler/AbstractUriElement.php
vendored
Normal file
111
vendor/symfony/dom-crawler/AbstractUriElement.php
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Any HTML element that can link to an URI.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class AbstractUriElement
|
||||
{
|
||||
protected \DOMElement $node;
|
||||
protected ?string $method;
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node A \DOMElement instance
|
||||
* @param string|null $currentUri The URI of the page where the link is embedded (or the base href)
|
||||
* @param string|null $method The method to use for the link (GET by default)
|
||||
*
|
||||
* @throws \InvalidArgumentException if the node is not a link
|
||||
*/
|
||||
public function __construct(
|
||||
\DOMElement $node,
|
||||
protected ?string $currentUri = null,
|
||||
?string $method = 'GET',
|
||||
) {
|
||||
$this->setNode($node);
|
||||
$this->method = $method ? strtoupper($method) : null;
|
||||
|
||||
$elementUriIsRelative = !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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the node associated with this link.
|
||||
*/
|
||||
public function getNode(): \DOMElement
|
||||
{
|
||||
return $this->node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the method associated with this link.
|
||||
*/
|
||||
public function getMethod(): string
|
||||
{
|
||||
return $this->method ?? 'GET';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URI associated with this link.
|
||||
*/
|
||||
public function getUri(): string
|
||||
{
|
||||
return UriResolver::resolve($this->getRawUri(), $this->currentUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns raw URI data.
|
||||
*/
|
||||
abstract protected function getRawUri(): string;
|
||||
|
||||
/**
|
||||
* Returns the canonicalized URI path (see RFC 3986, section 5.2.4).
|
||||
*
|
||||
* @param string $path URI path
|
||||
*/
|
||||
protected function canonicalizePath(string $path): string
|
||||
{
|
||||
if ('' === $path || '/' === $path) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
if (str_ends_with($path, '.')) {
|
||||
$path .= '/';
|
||||
}
|
||||
|
||||
$output = [];
|
||||
|
||||
foreach (explode('/', $path) as $segment) {
|
||||
if ('..' === $segment) {
|
||||
array_pop($output);
|
||||
} elseif ('.' !== $segment) {
|
||||
$output[] = $segment;
|
||||
}
|
||||
}
|
||||
|
||||
return implode('/', $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets current \DOMElement instance.
|
||||
*
|
||||
* @param \DOMElement $node A \DOMElement instance
|
||||
*
|
||||
* @throws \LogicException If given node is not an anchor
|
||||
*/
|
||||
abstract protected function setNode(\DOMElement $node): void;
|
||||
}
|
||||
130
vendor/symfony/dom-crawler/CHANGELOG.md
vendored
Normal file
130
vendor/symfony/dom-crawler/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
7.0
|
||||
---
|
||||
|
||||
* Add argument `$normalizeWhitespace` to `Crawler::innerText()`
|
||||
* Add argument `$default` to `Crawler::attr()`
|
||||
|
||||
6.4
|
||||
---
|
||||
|
||||
* Add `CrawlerAnySelectorTextContains` test constraint
|
||||
* Add `CrawlerAnySelectorTextSame` test constraint
|
||||
* Add argument `$default` to `Crawler::attr()`
|
||||
|
||||
6.3
|
||||
---
|
||||
|
||||
* Add `$useHtml5Parser` argument to `Crawler`
|
||||
* Add `CrawlerSelectorCount` test constraint
|
||||
* Add argument `$normalizeWhitespace` to `Crawler::innerText()`
|
||||
* Make `Crawler::innerText()` return the first non-empty text
|
||||
|
||||
6.0
|
||||
---
|
||||
|
||||
* Remove `Crawler::parents()` method, use `ancestors()` instead
|
||||
|
||||
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
|
||||
-----
|
||||
|
||||
* Added `Form::getName()` method.
|
||||
* Added `Crawler::matches()` method.
|
||||
* Added `Crawler::closest()` method.
|
||||
* Added `Crawler::outerHtml()` method.
|
||||
* Added an argument to the `Crawler::text()` method to opt-in normalizing whitespaces.
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* Added PHPUnit constraints: `CrawlerSelectorAttributeValueSame`, `CrawlerSelectorExists`, `CrawlerSelectorTextContains`
|
||||
and `CrawlerSelectorTextSame`
|
||||
* Added return of element name (`_name`) in `extract()` method.
|
||||
* Added ability to return a default value in `text()` and `html()` instead of throwing an exception when node is empty.
|
||||
* When available, the [html5-php library](https://github.com/Masterminds/html5-php) is used to
|
||||
parse HTML added to a Crawler for better support of HTML5 tags.
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
* The `$currentUri` constructor argument of the `AbstractUriElement`, `Link` and
|
||||
`Image` classes is now optional.
|
||||
* The `Crawler::children()` method will have a new `$selector` argument in version 5.0,
|
||||
not defining it is deprecated.
|
||||
|
||||
3.1.0
|
||||
-----
|
||||
|
||||
* All the URI parsing logic have been abstracted in the `AbstractUriElement` class.
|
||||
The `Link` class is now a child of `AbstractUriElement`.
|
||||
* Added an `Image` class to crawl images and parse their `src` attribute,
|
||||
and `selectImage`, `image`, `images` methods in the `Crawler` (the image version of the equivalent `link` methods).
|
||||
|
||||
2.5.0
|
||||
-----
|
||||
|
||||
* [BC BREAK] The default value for checkbox and radio inputs without a value attribute have changed
|
||||
from '1' to 'on' to match the HTML specification.
|
||||
* [BC BREAK] The typehints on the `Link`, `Form` and `FormField` classes have been changed from
|
||||
`\DOMNode` to `DOMElement`. Using any other type of `DOMNode` was triggering fatal errors in previous
|
||||
versions. Code extending these classes will need to update the typehints when overwriting these methods.
|
||||
|
||||
2.4.0
|
||||
-----
|
||||
|
||||
* `Crawler::addXmlContent()` removes the default document namespace again if it's an only namespace.
|
||||
* added support for automatic discovery and explicit registration of document
|
||||
namespaces for `Crawler::filterXPath()` and `Crawler::filter()`
|
||||
* improved content type guessing in `Crawler::addContent()`
|
||||
* [BC BREAK] `Crawler::addXmlContent()` no longer removes the default document
|
||||
namespace
|
||||
|
||||
2.3.0
|
||||
-----
|
||||
|
||||
* added Crawler::html()
|
||||
* [BC BREAK] Crawler::each() and Crawler::reduce() now return Crawler instances instead of DomElement instances
|
||||
* added schema relative URL support to links
|
||||
* added support for HTML5 'form' attribute
|
||||
|
||||
2.2.0
|
||||
-----
|
||||
|
||||
* added a way to set raw path to the file in FileFormField - necessary for
|
||||
simulating HTTP requests
|
||||
|
||||
2.1.0
|
||||
-----
|
||||
|
||||
* added support for the HTTP PATCH method
|
||||
* refactored the Form class internals to support multi-dimensional fields
|
||||
(the public API is backward compatible)
|
||||
* added a way to get parsing errors for Crawler::addHtmlContent() and
|
||||
Crawler::addXmlContent() via libxml functions
|
||||
* added support for submitting a form without a submit button
|
||||
1237
vendor/symfony/dom-crawler/Crawler.php
vendored
Normal file
1237
vendor/symfony/dom-crawler/Crawler.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
299
vendor/symfony/dom-crawler/Field/ChoiceFormField.php
vendored
Normal file
299
vendor/symfony/dom-crawler/Field/ChoiceFormField.php
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
<?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\Field;
|
||||
|
||||
/**
|
||||
* ChoiceFormField represents a choice form field.
|
||||
*
|
||||
* It is constructed from an HTML select tag, or an HTML checkbox, or radio inputs.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ChoiceFormField extends FormField
|
||||
{
|
||||
private string $type;
|
||||
private bool $multiple;
|
||||
private array $options;
|
||||
private bool $validationDisabled = false;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function hasValue(): bool
|
||||
{
|
||||
// don't send a value for unchecked checkboxes
|
||||
if (\in_array($this->type, ['checkbox', 'radio']) && null === $this->value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current selected option is disabled.
|
||||
*/
|
||||
public function isDisabled(): bool
|
||||
{
|
||||
if ('checkbox' === $this->type) {
|
||||
return parent::isDisabled();
|
||||
}
|
||||
|
||||
if (parent::isDisabled() && 'select' === $this->type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($this->options as $option) {
|
||||
if ($option['value'] == $this->value && $option['disabled']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the field.
|
||||
*/
|
||||
public function select(string|array|bool $value): void
|
||||
{
|
||||
$this->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ticks a checkbox.
|
||||
*
|
||||
* @throws \LogicException When the type provided is not correct
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
$this->setValue(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unticks a checkbox.
|
||||
*
|
||||
* @throws \LogicException When the type provided is not correct
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
$this->setValue(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the field.
|
||||
*
|
||||
* @throws \InvalidArgumentException When value type provided is not correct
|
||||
*/
|
||||
public function setValue(string|array|bool|null $value): void
|
||||
{
|
||||
if ('checkbox' === $this->type && false === $value) {
|
||||
// uncheck
|
||||
$this->value = null;
|
||||
} elseif ('checkbox' === $this->type && true === $value) {
|
||||
// check
|
||||
$this->value = $this->options[0]['value'];
|
||||
} else {
|
||||
if (\is_array($value)) {
|
||||
if (!$this->multiple) {
|
||||
throw new \InvalidArgumentException(\sprintf('The value for "%s" cannot be an array.', $this->name));
|
||||
}
|
||||
|
||||
foreach ($value as $v) {
|
||||
if (!$this->containsOption($v, $this->options)) {
|
||||
throw new \InvalidArgumentException(\sprintf('Input "%s" cannot take "%s" as a value (possible values: "%s").', $this->name, $v, implode('", "', $this->availableOptionValues())));
|
||||
}
|
||||
}
|
||||
} 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())));
|
||||
}
|
||||
|
||||
if ($this->multiple) {
|
||||
$value = (array) $value;
|
||||
}
|
||||
|
||||
if (\is_array($value)) {
|
||||
$this->value = $value;
|
||||
} else {
|
||||
parent::setValue($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a choice to the current ones.
|
||||
*
|
||||
* @throws \LogicException When choice provided is not multiple nor radio
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
$option = $this->buildOptionValue($node);
|
||||
$this->options[] = $option;
|
||||
|
||||
if ($node->hasAttribute('checked')) {
|
||||
$this->value = $option['value'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the choice field (radio, select, or checkbox).
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the field accepts multiple values.
|
||||
*/
|
||||
public function isMultiple(): bool
|
||||
{
|
||||
return $this->multiple;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the form field.
|
||||
*
|
||||
* @throws \LogicException When node type is incorrect
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
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')));
|
||||
}
|
||||
|
||||
$this->value = null;
|
||||
$this->options = [];
|
||||
$this->multiple = false;
|
||||
|
||||
if ('input' == $this->node->nodeName) {
|
||||
$this->type = strtolower($this->node->getAttribute('type'));
|
||||
$optionValue = $this->buildOptionValue($this->node);
|
||||
$this->options[] = $optionValue;
|
||||
|
||||
if ($this->node->hasAttribute('checked')) {
|
||||
$this->value = $optionValue['value'];
|
||||
}
|
||||
} else {
|
||||
$this->type = 'select';
|
||||
if ($this->node->hasAttribute('multiple')) {
|
||||
$this->multiple = true;
|
||||
$this->value = [];
|
||||
$this->name = str_replace('[]', '', $this->name);
|
||||
}
|
||||
|
||||
$found = false;
|
||||
foreach ($this->xpath->query('descendant::option', $this->node) as $option) {
|
||||
$optionValue = $this->buildOptionValue($option);
|
||||
$this->options[] = $optionValue;
|
||||
|
||||
if ($option->hasAttribute('selected')) {
|
||||
$found = true;
|
||||
if ($this->multiple) {
|
||||
$this->value[] = $optionValue['value'];
|
||||
} else {
|
||||
$this->value = $optionValue['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no option is selected and if it is a simple select box, take the first option as the value
|
||||
if (!$found && !$this->multiple && $this->options) {
|
||||
$this->value = $this->options[0]['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns option value with associated disabled flag.
|
||||
*/
|
||||
private function buildOptionValue(\DOMElement $node): array
|
||||
{
|
||||
$option = [];
|
||||
|
||||
$defaultDefaultValue = 'select' === $this->node->nodeName ? '' : 'on';
|
||||
$defaultValue = (isset($node->nodeValue) && $node->nodeValue) ? $node->nodeValue : $defaultDefaultValue;
|
||||
$option['value'] = $node->hasAttribute('value') ? $node->getAttribute('value') : $defaultValue;
|
||||
$option['disabled'] = $node->hasAttribute('disabled');
|
||||
|
||||
return $option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether given value is in the existing options.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function containsOption(string $optionValue, array $options): bool
|
||||
{
|
||||
if ($this->validationDisabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($options as $option) {
|
||||
if ($option['value'] == $optionValue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of available field options.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function availableOptionValues(): array
|
||||
{
|
||||
$values = [];
|
||||
|
||||
foreach ($this->options as $option) {
|
||||
$values[] = $option['value'];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the internal validation of the field.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function disableValidation(): static
|
||||
{
|
||||
$this->validationDisabled = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
103
vendor/symfony/dom-crawler/Field/FileFormField.php
vendored
Normal file
103
vendor/symfony/dom-crawler/Field/FileFormField.php
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
<?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\Field;
|
||||
|
||||
/**
|
||||
* FileFormField represents a file form field (an HTML file input tag).
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class FileFormField extends FormField
|
||||
{
|
||||
/**
|
||||
* Sets the PHP error code associated with the field.
|
||||
*
|
||||
* @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)
|
||||
*
|
||||
* @throws \InvalidArgumentException When error code doesn't exist
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
$this->value = ['name' => '', 'type' => '', 'tmp_name' => '', 'error' => $error, 'size' => 0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the field.
|
||||
*/
|
||||
public function upload(?string $value): void
|
||||
{
|
||||
$this->setValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the field.
|
||||
*/
|
||||
public function setValue(?string $value): void
|
||||
{
|
||||
if (null !== $value && is_readable($value)) {
|
||||
$error = \UPLOAD_ERR_OK;
|
||||
$size = filesize($value);
|
||||
$info = pathinfo($value);
|
||||
$name = $info['basename'];
|
||||
|
||||
// copy to a tmp location
|
||||
$tmp = tempnam(sys_get_temp_dir(), $name);
|
||||
if (\array_key_exists('extension', $info)) {
|
||||
unlink($tmp);
|
||||
$tmp .= '.'.$info['extension'];
|
||||
}
|
||||
if (is_file($tmp)) {
|
||||
unlink($tmp);
|
||||
}
|
||||
copy($value, $tmp);
|
||||
$value = $tmp;
|
||||
} else {
|
||||
$error = \UPLOAD_ERR_NO_FILE;
|
||||
$size = 0;
|
||||
$name = '';
|
||||
$value = '';
|
||||
}
|
||||
|
||||
$this->value = ['name' => $name, 'type' => '', 'tmp_name' => $value, 'error' => $error, 'size' => $size];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets path to the file as string for simulating HTTP request.
|
||||
*/
|
||||
public function setFilePath(string $path): void
|
||||
{
|
||||
parent::setValue($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the form field.
|
||||
*
|
||||
* @throws \LogicException When node type is incorrect
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
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')));
|
||||
}
|
||||
|
||||
$this->setValue(null);
|
||||
}
|
||||
}
|
||||
102
vendor/symfony/dom-crawler/Field/FormField.php
vendored
Normal file
102
vendor/symfony/dom-crawler/Field/FormField.php
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
<?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\Field;
|
||||
|
||||
/**
|
||||
* FormField is the abstract class for all form fields.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class FormField
|
||||
{
|
||||
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
|
||||
*/
|
||||
public function __construct(
|
||||
protected \DOMElement $node,
|
||||
) {
|
||||
$this->name = $node->getAttribute('name');
|
||||
$this->xpath = new \DOMXPath($node->ownerDocument);
|
||||
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label tag associated to the field or null if none.
|
||||
*/
|
||||
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')));
|
||||
if ($labels->length > 0) {
|
||||
return $labels->item(0);
|
||||
}
|
||||
}
|
||||
|
||||
$labels = $xpath->query('ancestor::label[1]', $this->node);
|
||||
|
||||
return $labels->length > 0 ? $labels->item(0) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the field.
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the field.
|
||||
*/
|
||||
public function getValue(): string|array|null
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the field.
|
||||
*/
|
||||
public function setValue(?string $value): void
|
||||
{
|
||||
$this->value = $value ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the field should be included in the submitted values.
|
||||
*/
|
||||
public function hasValue(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current field is disabled.
|
||||
*/
|
||||
public function isDisabled(): bool
|
||||
{
|
||||
return $this->node->hasAttribute('disabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the form field.
|
||||
*/
|
||||
abstract protected function initialize(): void;
|
||||
}
|
||||
46
vendor/symfony/dom-crawler/Field/InputFormField.php
vendored
Normal file
46
vendor/symfony/dom-crawler/Field/InputFormField.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?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\Field;
|
||||
|
||||
/**
|
||||
* InputFormField represents an input form field (an HTML input tag).
|
||||
*
|
||||
* For inputs with type of file, checkbox, or radio, there are other more
|
||||
* specialized classes (cf. FileFormField and ChoiceFormField).
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class InputFormField extends FormField
|
||||
{
|
||||
/**
|
||||
* Initializes the form field.
|
||||
*
|
||||
* @throws \LogicException When node type is incorrect
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
$type = strtolower($this->node->getAttribute('type'));
|
||||
if ('checkbox' === $type) {
|
||||
throw new \LogicException('Checkboxes should be instances of ChoiceFormField.');
|
||||
}
|
||||
|
||||
if ('file' === $type) {
|
||||
throw new \LogicException('File inputs should be instances of FileFormField.');
|
||||
}
|
||||
|
||||
$this->value = $this->node->getAttribute('value');
|
||||
}
|
||||
}
|
||||
37
vendor/symfony/dom-crawler/Field/TextareaFormField.php
vendored
Normal file
37
vendor/symfony/dom-crawler/Field/TextareaFormField.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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\Field;
|
||||
|
||||
/**
|
||||
* TextareaFormField represents a textarea form field (an HTML textarea tag).
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TextareaFormField extends FormField
|
||||
{
|
||||
/**
|
||||
* Initializes the form field.
|
||||
*
|
||||
* @throws \LogicException When node type is incorrect
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
$this->value = '';
|
||||
foreach ($this->node->childNodes as $node) {
|
||||
$this->value .= $node->wholeText;
|
||||
}
|
||||
}
|
||||
}
|
||||
463
vendor/symfony/dom-crawler/Form.php
vendored
Normal file
463
vendor/symfony/dom-crawler/Form.php
vendored
Normal file
@@ -0,0 +1,463 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\DomCrawler\Field\ChoiceFormField;
|
||||
use Symfony\Component\DomCrawler\Field\FormField;
|
||||
|
||||
/**
|
||||
* Form represents an HTML form.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Form extends Link implements \ArrayAccess
|
||||
{
|
||||
private \DOMElement $button;
|
||||
private FormFieldRegistry $fields;
|
||||
|
||||
/**
|
||||
* @param \DOMElement $node A \DOMElement instance
|
||||
* @param string|null $currentUri The URI of the page where the form is embedded
|
||||
* @param string|null $method The method to use for the link (if null, it defaults to the method defined by the form)
|
||||
* @param string|null $baseHref The URI of the <base> used for relative links, but not for empty action
|
||||
*
|
||||
* @throws \LogicException if the node is not a button inside a form tag
|
||||
*/
|
||||
public function __construct(
|
||||
\DOMElement $node,
|
||||
?string $currentUri = null,
|
||||
?string $method = null,
|
||||
private ?string $baseHref = null,
|
||||
) {
|
||||
parent::__construct($node, $currentUri, $method);
|
||||
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the form node associated with this form.
|
||||
*/
|
||||
public function getFormNode(): \DOMElement
|
||||
{
|
||||
return $this->node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the fields.
|
||||
*
|
||||
* @param array $values An array of field values
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValues(array $values): static
|
||||
{
|
||||
foreach ($values as $name => $value) {
|
||||
$this->fields->set($name, $value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the field values.
|
||||
*
|
||||
* The returned array does not include file fields (@see getFiles).
|
||||
*/
|
||||
public function getValues(): array
|
||||
{
|
||||
$values = [];
|
||||
foreach ($this->fields->all() as $name => $field) {
|
||||
if ($field->isDisabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$field instanceof Field\FileFormField && $field->hasValue()) {
|
||||
$values[$name] = $field->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file field values.
|
||||
*/
|
||||
public function getFiles(): array
|
||||
{
|
||||
if (!\in_array($this->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$files = [];
|
||||
|
||||
foreach ($this->fields->all() as $name => $field) {
|
||||
if ($field->isDisabled()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($field instanceof Field\FileFormField) {
|
||||
$files[$name] = $field->getValue();
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the field values as PHP.
|
||||
*
|
||||
* This method converts fields with the array notation
|
||||
* (like foo[bar] to arrays) like PHP does.
|
||||
*/
|
||||
public function getPhpValues(): array
|
||||
{
|
||||
$values = [];
|
||||
foreach ($this->getValues() as $name => $value) {
|
||||
$qs = http_build_query([$name => $value], '', '&');
|
||||
if ($qs) {
|
||||
parse_str($qs, $expandedValue);
|
||||
$varName = substr($name, 0, \strlen(key($expandedValue)));
|
||||
$values[] = [$varName => current($expandedValue)];
|
||||
}
|
||||
}
|
||||
|
||||
return array_replace_recursive([], ...$values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file field values as PHP.
|
||||
*
|
||||
* This method converts fields with the array notation
|
||||
* (like foo[bar] to arrays) like PHP does.
|
||||
* The returned array is consistent with the array for field values
|
||||
* (@see getPhpValues), rather than uploaded files found in $_FILES.
|
||||
* For a compound file field foo[bar] it will create foo[bar][name],
|
||||
* instead of foo[name][bar] which would be found in $_FILES.
|
||||
*/
|
||||
public function getPhpFiles(): array
|
||||
{
|
||||
$values = [];
|
||||
foreach ($this->getFiles() as $name => $value) {
|
||||
$qs = http_build_query([$name => $value], '', '&');
|
||||
if ($qs) {
|
||||
parse_str($qs, $expandedValue);
|
||||
$varName = substr($name, 0, \strlen(key($expandedValue)));
|
||||
|
||||
array_walk_recursive(
|
||||
$expandedValue,
|
||||
function (&$value, $key) {
|
||||
if (ctype_digit($value) && ('size' === $key || 'error' === $key)) {
|
||||
$value = (int) $value;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
reset($expandedValue);
|
||||
|
||||
$values[] = [$varName => current($expandedValue)];
|
||||
}
|
||||
}
|
||||
|
||||
return array_replace_recursive([], ...$values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URI of the form.
|
||||
*
|
||||
* The returned URI is not the same as the form "action" attribute.
|
||||
* This method merges the value if the method is GET to mimics
|
||||
* browser behavior.
|
||||
*/
|
||||
public function getUri(): string
|
||||
{
|
||||
$uri = parent::getUri();
|
||||
|
||||
if (!\in_array($this->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
|
||||
$currentParameters = [];
|
||||
if ($query = parse_url($uri, \PHP_URL_QUERY)) {
|
||||
parse_str($query, $currentParameters);
|
||||
}
|
||||
|
||||
$queryString = http_build_query(array_merge($currentParameters, $this->getValues()), '', '&');
|
||||
|
||||
$pos = strpos($uri, '?');
|
||||
$base = false === $pos ? $uri : substr($uri, 0, $pos);
|
||||
$uri = rtrim($base.'?'.$queryString, '?');
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
protected function getRawUri(): string
|
||||
{
|
||||
// If the form was created from a button rather than the form node, check for HTML5 action overrides
|
||||
if ($this->button !== $this->node && $this->button->getAttribute('formaction')) {
|
||||
return $this->button->getAttribute('formaction');
|
||||
}
|
||||
|
||||
return $this->node->getAttribute('action');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the form method.
|
||||
*
|
||||
* If no method is defined in the form, GET is returned.
|
||||
*/
|
||||
public function getMethod(): string
|
||||
{
|
||||
if (null !== $this->method) {
|
||||
return $this->method;
|
||||
}
|
||||
|
||||
// If the form was created from a button rather than the form node, check for HTML5 method override
|
||||
if ($this->button !== $this->node && $this->button->getAttribute('formmethod')) {
|
||||
return strtoupper($this->button->getAttribute('formmethod'));
|
||||
}
|
||||
|
||||
return $this->node->getAttribute('method') ? strtoupper($this->node->getAttribute('method')) : 'GET';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the form name.
|
||||
*
|
||||
* If no name is defined on the form, an empty string is returned.
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->node->getAttribute('name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the named field exists.
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return $this->fields->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a field from the form.
|
||||
*/
|
||||
public function remove(string $name): void
|
||||
{
|
||||
$this->fields->remove($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a named field.
|
||||
*
|
||||
* @return FormField|FormField[]|FormField[][]
|
||||
*
|
||||
* @throws \InvalidArgumentException When field is not present in this form
|
||||
*/
|
||||
public function get(string $name): FormField|array
|
||||
{
|
||||
return $this->fields->get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a named field.
|
||||
*/
|
||||
public function set(FormField $field): void
|
||||
{
|
||||
$this->fields->add($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all fields.
|
||||
*
|
||||
* @return FormField[]
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->fields->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the named field exists.
|
||||
*
|
||||
* @param string $name The field name
|
||||
*/
|
||||
public function offsetExists(mixed $name): bool
|
||||
{
|
||||
return $this->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a field.
|
||||
*
|
||||
* @param string $name The field name
|
||||
*
|
||||
* @return FormField|FormField[]|FormField[][]
|
||||
*
|
||||
* @throws \InvalidArgumentException if the field does not exist
|
||||
*/
|
||||
public function offsetGet(mixed $name): FormField|array
|
||||
{
|
||||
return $this->fields->get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a field.
|
||||
*
|
||||
* @param string $name The field name
|
||||
* @param string|array $value The value of the field
|
||||
*
|
||||
* @throws \InvalidArgumentException if the field does not exist
|
||||
*/
|
||||
public function offsetSet(mixed $name, mixed $value): void
|
||||
{
|
||||
$this->fields->set($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a field from the form.
|
||||
*
|
||||
* @param string $name The field name
|
||||
*/
|
||||
public function offsetUnset(mixed $name): void
|
||||
{
|
||||
$this->fields->remove($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables validation.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function disableValidation(): static
|
||||
{
|
||||
foreach ($this->fields->all() as $field) {
|
||||
if ($field instanceof ChoiceFormField) {
|
||||
$field->disableValidation();
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the node for the form.
|
||||
*
|
||||
* Expects a 'submit' button \DOMElement and finds the corresponding form element, or the form element itself.
|
||||
*
|
||||
* @throws \LogicException If given node is not a button or input or does not have a form ancestor
|
||||
*/
|
||||
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']))) {
|
||||
if ($node->hasAttribute('form')) {
|
||||
// if the node has the HTML5-compliant 'form' attribute, use it
|
||||
$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));
|
||||
}
|
||||
$this->node = $form;
|
||||
|
||||
return;
|
||||
}
|
||||
// we loop until we find a form ancestor
|
||||
do {
|
||||
if (null === $node = $node->parentNode) {
|
||||
throw new \LogicException('The selected node does not have a form ancestor.');
|
||||
}
|
||||
} while ('form' !== $node->nodeName);
|
||||
} elseif ('form' !== $node->nodeName) {
|
||||
throw new \LogicException(\sprintf('Unable to submit on a "%s" tag.', $node->nodeName));
|
||||
}
|
||||
|
||||
$this->node = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds form elements related to this form.
|
||||
*
|
||||
* Creates an internal copy of the submitted 'button' element and
|
||||
* the form node or the entire document depending on whether we need
|
||||
* to find non-descendant elements through HTML5 'form' attribute.
|
||||
*/
|
||||
private function initialize(): void
|
||||
{
|
||||
$this->fields = new FormFieldRegistry();
|
||||
|
||||
$xpath = new \DOMXPath($this->node->ownerDocument);
|
||||
|
||||
// add submitted button if it has a valid name
|
||||
if ('form' !== $this->button->nodeName && $this->button->hasAttribute('name') && $this->button->getAttribute('name')) {
|
||||
if ('input' == $this->button->nodeName && 'image' == strtolower($this->button->getAttribute('type'))) {
|
||||
$name = $this->button->getAttribute('name');
|
||||
$this->button->setAttribute('value', '0');
|
||||
|
||||
// temporarily change the name of the input node for the x coordinate
|
||||
$this->button->setAttribute('name', $name.'.x');
|
||||
$this->set(new Field\InputFormField($this->button));
|
||||
|
||||
// temporarily change the name of the input node for the y coordinate
|
||||
$this->button->setAttribute('name', $name.'.y');
|
||||
$this->set(new Field\InputFormField($this->button));
|
||||
|
||||
// restore the original name of the input node
|
||||
$this->button->setAttribute('name', $name);
|
||||
} else {
|
||||
$this->set(new Field\InputFormField($this->button));
|
||||
}
|
||||
}
|
||||
|
||||
// find form elements corresponding to the current form
|
||||
if ($this->node->hasAttribute('id')) {
|
||||
// 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));
|
||||
} 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);
|
||||
}
|
||||
|
||||
if ($this->baseHref && '' !== $this->node->getAttribute('action')) {
|
||||
$this->currentUri = $this->baseHref;
|
||||
}
|
||||
}
|
||||
|
||||
private function addField(\DOMElement $node): void
|
||||
{
|
||||
if (!$node->hasAttribute('name') || !$node->getAttribute('name')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$nodeName = $node->nodeName;
|
||||
if ('select' == $nodeName || 'input' == $nodeName && 'checkbox' == strtolower($node->getAttribute('type'))) {
|
||||
$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 ChoiceFormField($node));
|
||||
}
|
||||
} elseif ('input' == $nodeName && 'file' == strtolower($node->getAttribute('type'))) {
|
||||
$this->set(new Field\FileFormField($node));
|
||||
} elseif ('input' == $nodeName && !\in_array(strtolower($node->getAttribute('type')), ['submit', 'button', 'image'])) {
|
||||
$this->set(new Field\InputFormField($node));
|
||||
} elseif ('textarea' == $nodeName) {
|
||||
$this->set(new Field\TextareaFormField($node));
|
||||
}
|
||||
}
|
||||
}
|
||||
175
vendor/symfony/dom-crawler/FormFieldRegistry.php
vendored
Normal file
175
vendor/symfony/dom-crawler/FormFieldRegistry.php
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
<?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;
|
||||
|
||||
use Symfony\Component\DomCrawler\Field\FormField;
|
||||
|
||||
/**
|
||||
* This is an internal class that must not be used directly.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class FormFieldRegistry
|
||||
{
|
||||
private array $fields = [];
|
||||
private string $base = '';
|
||||
|
||||
/**
|
||||
* Adds a field to the registry.
|
||||
*/
|
||||
public function add(FormField $field): void
|
||||
{
|
||||
$segments = $this->getSegments($field->getName());
|
||||
|
||||
$target = &$this->fields;
|
||||
while ($segments) {
|
||||
if (!\is_array($target)) {
|
||||
$target = [];
|
||||
}
|
||||
$path = array_shift($segments);
|
||||
if ('' === $path) {
|
||||
$target = &$target[];
|
||||
} else {
|
||||
$target = &$target[$path];
|
||||
}
|
||||
}
|
||||
$target = $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a field based on the fully qualified name and its children from the registry.
|
||||
*/
|
||||
public function remove(string $name): void
|
||||
{
|
||||
$segments = $this->getSegments($name);
|
||||
$target = &$this->fields;
|
||||
while (\count($segments) > 1) {
|
||||
$path = array_shift($segments);
|
||||
if (!\is_array($target) || !\array_key_exists($path, $target)) {
|
||||
return;
|
||||
}
|
||||
$target = &$target[$path];
|
||||
}
|
||||
unset($target[array_shift($segments)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the field based on the fully qualified name and its children.
|
||||
*
|
||||
* @return FormField|FormField[]|FormField[][]
|
||||
*
|
||||
* @throws \InvalidArgumentException if the field does not exist
|
||||
*/
|
||||
public function &get(string $name): FormField|array
|
||||
{
|
||||
$segments = $this->getSegments($name);
|
||||
$target = &$this->fields;
|
||||
while ($segments) {
|
||||
$path = array_shift($segments);
|
||||
if (!\is_array($target) || !\array_key_exists($path, $target)) {
|
||||
throw new \InvalidArgumentException(\sprintf('Unreachable field "%s".', $path));
|
||||
}
|
||||
$target = &$target[$path];
|
||||
}
|
||||
|
||||
return $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the form has the given field based on the fully qualified name.
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
try {
|
||||
$this->get($name);
|
||||
|
||||
return true;
|
||||
} catch (\InvalidArgumentException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a field based on the fully qualified name and its children.
|
||||
*
|
||||
* @throws \InvalidArgumentException if the field does not exist
|
||||
*/
|
||||
public function set(string $name, mixed $value): void
|
||||
{
|
||||
$target = &$this->get($name);
|
||||
if ((!\is_array($value) && $target instanceof FormField) || $target instanceof Field\ChoiceFormField) {
|
||||
$target->setValue($value);
|
||||
} elseif (\is_array($value)) {
|
||||
$registry = new static();
|
||||
$registry->base = $name;
|
||||
$registry->fields = $value;
|
||||
foreach ($registry->all() as $k => $v) {
|
||||
$this->set($k, $v);
|
||||
}
|
||||
} else {
|
||||
throw new \InvalidArgumentException(\sprintf('Cannot set value on a compound field "%s".', $name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of field with their value.
|
||||
*
|
||||
* @return FormField[] The list of fields as [string] Fully qualified name => (mixed) value)
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->walk($this->fields, $this->base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a PHP array in a list of fully qualified name / value.
|
||||
*/
|
||||
private function walk(array $array, ?string $base = '', array &$output = []): array
|
||||
{
|
||||
foreach ($array as $k => $v) {
|
||||
$path = $base ? \sprintf('%s[%s]', $base, $k) : $k;
|
||||
if (\is_array($v)) {
|
||||
$this->walk($v, $path, $output);
|
||||
} else {
|
||||
$output[$path] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a field name into segments as a web browser would do.
|
||||
*
|
||||
* getSegments('base[foo][3][]') = ['base', 'foo, '3', ''];
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function getSegments(string $name): array
|
||||
{
|
||||
if (preg_match('/^(?P<base>[^[]+)(?P<extra>(\[.*)|$)/', $name, $m)) {
|
||||
$segments = [$m['base']];
|
||||
while (!empty($m['extra'])) {
|
||||
$extra = $m['extra'];
|
||||
if (preg_match('/^\[(?P<segment>.*?)\](?P<extra>.*)$/', $extra, $m)) {
|
||||
$segments[] = $m['segment'];
|
||||
} else {
|
||||
$segments[] = $extra;
|
||||
}
|
||||
}
|
||||
|
||||
return $segments;
|
||||
}
|
||||
|
||||
return [$name];
|
||||
}
|
||||
}
|
||||
37
vendor/symfony/dom-crawler/Image.php
vendored
Normal file
37
vendor/symfony/dom-crawler/Image.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Image represents an HTML image (an HTML img tag).
|
||||
*/
|
||||
class Image extends AbstractUriElement
|
||||
{
|
||||
public function __construct(\DOMElement $node, ?string $currentUri = null)
|
||||
{
|
||||
parent::__construct($node, $currentUri, 'GET');
|
||||
}
|
||||
|
||||
protected function getRawUri(): string
|
||||
{
|
||||
return $this->node->getAttribute('src');
|
||||
}
|
||||
|
||||
protected function setNode(\DOMElement $node): void
|
||||
{
|
||||
if ('img' !== $node->nodeName) {
|
||||
throw new \LogicException(\sprintf('Unable to visualize a "%s" tag.', $node->nodeName));
|
||||
}
|
||||
|
||||
$this->node = $node;
|
||||
}
|
||||
}
|
||||
19
vendor/symfony/dom-crawler/LICENSE
vendored
Normal file
19
vendor/symfony/dom-crawler/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2004-present Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
34
vendor/symfony/dom-crawler/Link.php
vendored
Normal file
34
vendor/symfony/dom-crawler/Link.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Link represents an HTML link (an HTML a, area or link tag).
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Link extends AbstractUriElement
|
||||
{
|
||||
protected function getRawUri(): string
|
||||
{
|
||||
return $this->node->getAttribute('href');
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
$this->node = $node;
|
||||
}
|
||||
}
|
||||
13
vendor/symfony/dom-crawler/README.md
vendored
Normal file
13
vendor/symfony/dom-crawler/README.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
DomCrawler Component
|
||||
====================
|
||||
|
||||
The DomCrawler component eases DOM navigation for HTML and XML documents.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/dom_crawler.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
67
vendor/symfony/dom-crawler/Test/Constraint/CrawlerAnySelectorTextContains.php
vendored
Normal file
67
vendor/symfony/dom-crawler/Test/Constraint/CrawlerAnySelectorTextContains.php
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
final class CrawlerAnySelectorTextContains extends Constraint
|
||||
{
|
||||
private bool $hasNode = false;
|
||||
|
||||
public function __construct(
|
||||
private string $selector,
|
||||
private string $expectedText,
|
||||
) {
|
||||
}
|
||||
|
||||
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 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)));
|
||||
}
|
||||
|
||||
$other = $other->filter($this->selector);
|
||||
if (!\count($other)) {
|
||||
$this->hasNode = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->hasNode = true;
|
||||
|
||||
$nodes = $other->each(fn (Crawler $node) => $node->text(null, true));
|
||||
$matches = array_filter($nodes, function (string $node): bool {
|
||||
return str_contains($node, $this->expectedText);
|
||||
});
|
||||
|
||||
return 0 < \count($matches);
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
return $this->toString();
|
||||
}
|
||||
}
|
||||
54
vendor/symfony/dom-crawler/Test/Constraint/CrawlerAnySelectorTextSame.php
vendored
Normal file
54
vendor/symfony/dom-crawler/Test/Constraint/CrawlerAnySelectorTextSame.php
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
final class CrawlerAnySelectorTextSame extends Constraint
|
||||
{
|
||||
public function __construct(
|
||||
private string $selector,
|
||||
private string $expectedText,
|
||||
) {
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
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)));
|
||||
}
|
||||
|
||||
$other = $other->filter($this->selector);
|
||||
if (!\count($other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$nodes = $other->each(fn (Crawler $node) => trim($node->text(null, true)));
|
||||
|
||||
return \in_array($this->expectedText, $nodes, true);
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
return 'the Crawler '.$this->toString();
|
||||
}
|
||||
}
|
||||
51
vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorAttributeValueSame.php
vendored
Normal file
51
vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorAttributeValueSame.php
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
final class CrawlerSelectorAttributeValueSame extends Constraint
|
||||
{
|
||||
public function __construct(
|
||||
private string $selector,
|
||||
private string $attribute,
|
||||
private string $expectedText,
|
||||
) {
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return \sprintf('has a node matching selector "%s" with attribute "%s" of value "%s"', $this->selector, $this->attribute, $this->expectedText);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Crawler $crawler
|
||||
*/
|
||||
protected function matches($crawler): bool
|
||||
{
|
||||
$crawler = $crawler->filter($this->selector);
|
||||
if (!\count($crawler)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->expectedText === trim($crawler->attr($this->attribute) ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Crawler $crawler
|
||||
*/
|
||||
protected function failureDescription($crawler): string
|
||||
{
|
||||
return 'the Crawler '.$this->toString();
|
||||
}
|
||||
}
|
||||
45
vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorCount.php
vendored
Normal file
45
vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorCount.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
final class CrawlerSelectorCount extends Constraint
|
||||
{
|
||||
public function __construct(
|
||||
private readonly int $count,
|
||||
private readonly string $selector,
|
||||
) {
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return \sprintf('selector "%s" count is "%d"', $this->selector, $this->count);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Crawler $crawler
|
||||
*/
|
||||
protected function matches($crawler): bool
|
||||
{
|
||||
return $this->count === \count($crawler->filter($this->selector));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Crawler $crawler
|
||||
*/
|
||||
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)));
|
||||
}
|
||||
}
|
||||
44
vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorExists.php
vendored
Normal file
44
vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorExists.php
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
final class CrawlerSelectorExists extends Constraint
|
||||
{
|
||||
public function __construct(
|
||||
private string $selector,
|
||||
) {
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return \sprintf('matches selector "%s"', $this->selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Crawler $crawler
|
||||
*/
|
||||
protected function matches($crawler): bool
|
||||
{
|
||||
return 0 < \count($crawler->filter($this->selector));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Crawler $crawler
|
||||
*/
|
||||
protected function failureDescription($crawler): string
|
||||
{
|
||||
return 'the Crawler '.$this->toString();
|
||||
}
|
||||
}
|
||||
62
vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextContains.php
vendored
Normal file
62
vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextContains.php
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
final class CrawlerSelectorTextContains extends Constraint
|
||||
{
|
||||
private bool $hasNode = false;
|
||||
private string $nodeText;
|
||||
|
||||
public function __construct(
|
||||
private string $selector,
|
||||
private string $expectedText,
|
||||
) {
|
||||
}
|
||||
|
||||
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 Crawler has a node matching selector "%s"', $this->selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Crawler $crawler
|
||||
*/
|
||||
protected function matches($crawler): bool
|
||||
{
|
||||
$crawler = $crawler->filter($this->selector);
|
||||
if (!\count($crawler)) {
|
||||
$this->hasNode = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->hasNode = true;
|
||||
$this->nodeText = $crawler->text(null, true);
|
||||
|
||||
return str_contains($this->nodeText, $this->expectedText);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Crawler $crawler
|
||||
*/
|
||||
protected function failureDescription($crawler): string
|
||||
{
|
||||
return $this->toString();
|
||||
}
|
||||
}
|
||||
50
vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextSame.php
vendored
Normal file
50
vendor/symfony/dom-crawler/Test/Constraint/CrawlerSelectorTextSame.php
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
<?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\Test\Constraint;
|
||||
|
||||
use PHPUnit\Framework\Constraint\Constraint;
|
||||
use Symfony\Component\DomCrawler\Crawler;
|
||||
|
||||
final class CrawlerSelectorTextSame extends Constraint
|
||||
{
|
||||
public function __construct(
|
||||
private string $selector,
|
||||
private string $expectedText,
|
||||
) {
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return \sprintf('has a node matching selector "%s" with content "%s"', $this->selector, $this->expectedText);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Crawler $crawler
|
||||
*/
|
||||
protected function matches($crawler): bool
|
||||
{
|
||||
$crawler = $crawler->filter($this->selector);
|
||||
if (!\count($crawler)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->expectedText === trim($crawler->text(null, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Crawler $crawler
|
||||
*/
|
||||
protected function failureDescription($crawler): string
|
||||
{
|
||||
return 'the Crawler '.$this->toString();
|
||||
}
|
||||
}
|
||||
136
vendor/symfony/dom-crawler/UriResolver.php
vendored
Normal file
136
vendor/symfony/dom-crawler/UriResolver.php
vendored
Normal 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(\strlen($uri) !== strcspn($uri, '?#') ? $uri : $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 (str_starts_with($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 (str_ends_with($path, '.')) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
34
vendor/symfony/dom-crawler/composer.json
vendored
Normal file
34
vendor/symfony/dom-crawler/composer.json
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "symfony/dom-crawler",
|
||||
"type": "library",
|
||||
"description": "Eases DOM navigation for HTML and XML documents",
|
||||
"keywords": [],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/polyfill-ctype": "~1.8",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"masterminds/html5": "^2.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/css-selector": "^6.4|^7.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\DomCrawler\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
Reference in New Issue
Block a user