ヤミRoot VoidGate
User / IP
:
216.73.216.81
Host / Server
:
146.88.233.70 / dev.loger.cm
System
:
Linux hybrid1120.fr.ns.planethoster.net 3.10.0-957.21.2.el7.x86_64 #1 SMP Wed Jun 5 14:26:44 UTC 2019 x86_64
Command
|
Upload
|
Create
Mass Deface
|
Jumping
|
Symlink
|
Reverse Shell
Ping
|
Port Scan
|
DNS Lookup
|
Whois
|
Header
|
cURL
:
/
home
/
logercm
/
dev.loger.cm
/
fixtures
/
assert
/
Viewing: browser-kit.tar
Exception/BadMethodCallException.php 0000644 00000000542 15120140424 0013507 0 ustar 00 <?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\BrowserKit\Exception; class BadMethodCallException extends \BadMethodCallException { } Test/Constraint/BrowserCookieValueSame.php 0000644 00000003536 15120140424 0014700 0 ustar 00 <?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\BrowserKit\Test\Constraint; use PHPUnit\Framework\Constraint\Constraint; use Symfony\Component\BrowserKit\AbstractBrowser; final class BrowserCookieValueSame extends Constraint { private $name; private $value; private $raw; private $path; private $domain; public function __construct(string $name, string $value, bool $raw = false, string $path = '/', string $domain = null) { $this->name = $name; $this->path = $path; $this->domain = $domain; $this->value = $value; $this->raw = $raw; } /** * {@inheritdoc} */ public function toString(): string { $str = sprintf('has cookie "%s"', $this->name); if ('/' !== $this->path) { $str .= sprintf(' with path "%s"', $this->path); } if ($this->domain) { $str .= sprintf(' for domain "%s"', $this->domain); } $str .= sprintf(' with %svalue "%s"', $this->raw ? 'raw ' : '', $this->value); return $str; } /** * @param AbstractBrowser $browser * * {@inheritdoc} */ protected function matches($browser): bool { $cookie = $browser->getCookieJar()->get($this->name, $this->path, $this->domain); if (!$cookie) { return false; } return $this->value === ($this->raw ? $cookie->getRawValue() : $cookie->getValue()); } /** * @param AbstractBrowser $browser * * {@inheritdoc} */ protected function failureDescription($browser): string { return 'the Browser '.$this->toString(); } } Test/Constraint/BrowserHasCookie.php 0000644 00000002753 15120140424 0013531 0 ustar 00 <?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\BrowserKit\Test\Constraint; use PHPUnit\Framework\Constraint\Constraint; use Symfony\Component\BrowserKit\AbstractBrowser; final class BrowserHasCookie extends Constraint { private $name; private $path; private $domain; public function __construct(string $name, string $path = '/', string $domain = null) { $this->name = $name; $this->path = $path; $this->domain = $domain; } /** * {@inheritdoc} */ public function toString(): string { $str = sprintf('has cookie "%s"', $this->name); if ('/' !== $this->path) { $str .= sprintf(' with path "%s"', $this->path); } if ($this->domain) { $str .= sprintf(' for domain "%s"', $this->domain); } return $str; } /** * @param AbstractBrowser $browser * * {@inheritdoc} */ protected function matches($browser): bool { return null !== $browser->getCookieJar()->get($this->name, $this->path, $this->domain); } /** * @param AbstractBrowser $browser * * {@inheritdoc} */ protected function failureDescription($browser): string { return 'the Browser '.$this->toString(); } } AbstractBrowser.php 0000644 00000052626 15120140424 0010370 0 ustar 00 <?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\BrowserKit; use Symfony\Component\BrowserKit\Exception\BadMethodCallException; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Form; use Symfony\Component\DomCrawler\Link; use Symfony\Component\Process\PhpProcess; /** * Simulates a browser. * * To make the actual request, you need to implement the doRequest() method. * * If you want to be able to run requests in their own process (insulated flag), * you need to also implement the getScript() method. * * @author Fabien Potencier <fabien@symfony.com> */ abstract class AbstractBrowser { protected $history; protected $cookieJar; protected $server = []; protected $internalRequest; protected $request; protected $internalResponse; protected $response; protected $crawler; protected $insulated = false; protected $redirect; protected $followRedirects = true; protected $followMetaRefresh = false; private $maxRedirects = -1; private $redirectCount = 0; private $redirects = []; private $isMainRequest = true; /** * @param array $server The server parameters (equivalent of $_SERVER) */ public function __construct(array $server = [], History $history = null, CookieJar $cookieJar = null) { $this->setServerParameters($server); $this->history = $history ?? new History(); $this->cookieJar = $cookieJar ?? new CookieJar(); } /** * Sets whether to automatically follow redirects or not. */ public function followRedirects(bool $followRedirects = true) { $this->followRedirects = $followRedirects; } /** * Sets whether to automatically follow meta refresh redirects or not. */ public function followMetaRefresh(bool $followMetaRefresh = true) { $this->followMetaRefresh = $followMetaRefresh; } /** * Returns whether client automatically follows redirects or not. * * @return bool */ public function isFollowingRedirects() { return $this->followRedirects; } /** * Sets the maximum number of redirects that crawler can follow. */ public function setMaxRedirects(int $maxRedirects) { $this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects; $this->followRedirects = -1 != $this->maxRedirects; } /** * Returns the maximum number of redirects that crawler can follow. * * @return int */ public function getMaxRedirects() { return $this->maxRedirects; } /** * Sets the insulated flag. * * @throws \RuntimeException When Symfony Process Component is not installed */ public function insulate(bool $insulated = true) { if ($insulated && !class_exists(\Symfony\Component\Process\Process::class)) { throw new \LogicException('Unable to isolate requests as the Symfony Process Component is not installed.'); } $this->insulated = $insulated; } /** * Sets server parameters. */ public function setServerParameters(array $server) { $this->server = array_merge([ 'HTTP_USER_AGENT' => 'Symfony BrowserKit', ], $server); } /** * Sets single server parameter. */ public function setServerParameter(string $key, string $value) { $this->server[$key] = $value; } /** * Gets single server parameter for specified key. * * @param mixed $default A default value when key is undefined * * @return mixed */ public function getServerParameter(string $key, $default = '') { return $this->server[$key] ?? $default; } public function xmlHttpRequest(string $method, string $uri, array $parameters = [], array $files = [], array $server = [], string $content = null, bool $changeHistory = true): Crawler { $this->setServerParameter('HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'); try { return $this->request($method, $uri, $parameters, $files, $server, $content, $changeHistory); } finally { unset($this->server['HTTP_X_REQUESTED_WITH']); } } /** * Converts the request parameters into a JSON string and uses it as request content. */ public function jsonRequest(string $method, string $uri, array $parameters = [], array $server = [], bool $changeHistory = true): Crawler { $content = json_encode($parameters); $this->setServerParameter('CONTENT_TYPE', 'application/json'); $this->setServerParameter('HTTP_ACCEPT', 'application/json'); try { return $this->request($method, $uri, [], [], $server, $content, $changeHistory); } finally { unset($this->server['CONTENT_TYPE']); unset($this->server['HTTP_ACCEPT']); } } /** * Returns the History instance. * * @return History */ public function getHistory() { return $this->history; } /** * Returns the CookieJar instance. * * @return CookieJar */ public function getCookieJar() { return $this->cookieJar; } /** * Returns the current Crawler instance. * * @return Crawler */ public function getCrawler() { if (null === $this->crawler) { throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__)); } return $this->crawler; } /** * Returns the current BrowserKit Response instance. * * @return Response */ public function getInternalResponse() { if (null === $this->internalResponse) { throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__)); } return $this->internalResponse; } /** * Returns the current origin response instance. * * The origin response is the response instance that is returned * by the code that handles requests. * * @return object * * @see doRequest() */ public function getResponse() { if (null === $this->response) { throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__)); } return $this->response; } /** * Returns the current BrowserKit Request instance. * * @return Request */ public function getInternalRequest() { if (null === $this->internalRequest) { throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__)); } return $this->internalRequest; } /** * Returns the current origin Request instance. * * The origin request is the request instance that is sent * to the code that handles requests. * * @return object * * @see doRequest() */ public function getRequest() { if (null === $this->request) { throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__)); } return $this->request; } /** * Clicks on a given link. * * @return Crawler */ public function click(Link $link) { if ($link instanceof Form) { return $this->submit($link); } return $this->request($link->getMethod(), $link->getUri()); } /** * Clicks the first link (or clickable image) that contains the given text. * * @param string $linkText The text of the link or the alt attribute of the clickable image */ public function clickLink(string $linkText): Crawler { if (null === $this->crawler) { throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__)); } return $this->click($this->crawler->selectLink($linkText)->link()); } /** * Submits a form. * * @param array $values An array of form field values * @param array $serverParameters An array of server parameters * * @return Crawler */ public function submit(Form $form, array $values = [], array $serverParameters = []) { $form->setValues($values); return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles(), $serverParameters); } /** * Finds the first form that contains a button with the given content and * uses it to submit the given form field values. * * @param string $button The text content, id, value or name of the form <button> or <input type="submit"> * @param array $fieldValues Use this syntax: ['my_form[name]' => '...', 'my_form[email]' => '...'] * @param string $method The HTTP method used to submit the form * @param array $serverParameters These values override the ones stored in $_SERVER (HTTP headers must include an HTTP_ prefix as PHP does) */ public function submitForm(string $button, array $fieldValues = [], string $method = 'POST', array $serverParameters = []): Crawler { if (null === $this->crawler) { throw new BadMethodCallException(sprintf('The "request()" method must be called before "%s()".', __METHOD__)); } $buttonNode = $this->crawler->selectButton($button); $form = $buttonNode->form($fieldValues, $method); return $this->submit($form, [], $serverParameters); } /** * Calls a URI. * * @param string $method The request method * @param string $uri The URI to fetch * @param array $parameters The Request parameters * @param array $files The files * @param array $server The server parameters (HTTP headers are referenced with an HTTP_ prefix as PHP does) * @param string $content The raw body data * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload()) * * @return Crawler */ public function request(string $method, string $uri, array $parameters = [], array $files = [], array $server = [], string $content = null, bool $changeHistory = true) { if ($this->isMainRequest) { $this->redirectCount = 0; } else { ++$this->redirectCount; } $originalUri = $uri; $uri = $this->getAbsoluteUri($uri); $server = array_merge($this->server, $server); if (!empty($server['HTTP_HOST']) && null === parse_url($originalUri, \PHP_URL_HOST)) { $uri = preg_replace('{^(https?\://)'.preg_quote($this->extractHost($uri)).'}', '${1}'.$server['HTTP_HOST'], $uri); } if (isset($server['HTTPS']) && null === parse_url($originalUri, \PHP_URL_SCHEME)) { $uri = preg_replace('{^'.parse_url($uri, \PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri); } if (!isset($server['HTTP_REFERER']) && !$this->history->isEmpty()) { $server['HTTP_REFERER'] = $this->history->current()->getUri(); } if (empty($server['HTTP_HOST'])) { $server['HTTP_HOST'] = $this->extractHost($uri); } $server['HTTPS'] = 'https' == parse_url($uri, \PHP_URL_SCHEME); $this->internalRequest = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content); $this->request = $this->filterRequest($this->internalRequest); if (true === $changeHistory) { $this->history->add($this->internalRequest); } if ($this->insulated) { $this->response = $this->doRequestInProcess($this->request); } else { $this->response = $this->doRequest($this->request); } $this->internalResponse = $this->filterResponse($this->response); $this->cookieJar->updateFromResponse($this->internalResponse, $uri); $status = $this->internalResponse->getStatusCode(); if ($status >= 300 && $status < 400) { $this->redirect = $this->internalResponse->getHeader('Location'); } else { $this->redirect = null; } if ($this->followRedirects && $this->redirect) { $this->redirects[serialize($this->history->current())] = true; return $this->crawler = $this->followRedirect(); } $this->crawler = $this->createCrawlerFromContent($this->internalRequest->getUri(), $this->internalResponse->getContent(), $this->internalResponse->getHeader('Content-Type') ?? ''); // Check for meta refresh redirect if ($this->followMetaRefresh && null !== $redirect = $this->getMetaRefreshUrl()) { $this->redirect = $redirect; $this->redirects[serialize($this->history->current())] = true; $this->crawler = $this->followRedirect(); } return $this->crawler; } /** * Makes a request in another process. * * @return object * * @throws \RuntimeException When processing returns exit code */ protected function doRequestInProcess(object $request) { $deprecationsFile = tempnam(sys_get_temp_dir(), 'deprec'); putenv('SYMFONY_DEPRECATIONS_SERIALIZE='.$deprecationsFile); $_ENV['SYMFONY_DEPRECATIONS_SERIALIZE'] = $deprecationsFile; $process = new PhpProcess($this->getScript($request), null, null); $process->run(); if (file_exists($deprecationsFile)) { $deprecations = file_get_contents($deprecationsFile); unlink($deprecationsFile); foreach ($deprecations ? unserialize($deprecations) : [] as $deprecation) { if ($deprecation[0]) { // unsilenced on purpose trigger_error($deprecation[1], \E_USER_DEPRECATED); } else { @trigger_error($deprecation[1], \E_USER_DEPRECATED); } } } if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) { throw new \RuntimeException(sprintf('OUTPUT: %s ERROR OUTPUT: %s.', $process->getOutput(), $process->getErrorOutput())); } return unserialize($process->getOutput()); } /** * Makes a request. * * @return object */ abstract protected function doRequest(object $request); /** * Returns the script to execute when the request must be insulated. * * @param object $request An origin request instance * * @throws \LogicException When this abstract class is not implemented */ protected function getScript(object $request) { throw new \LogicException('To insulate requests, you need to override the getScript() method.'); } /** * Filters the BrowserKit request to the origin one. * * @return object */ protected function filterRequest(Request $request) { return $request; } /** * Filters the origin response to the BrowserKit one. * * @return Response */ protected function filterResponse(object $response) { return $response; } /** * Creates a crawler. * * This method returns null if the DomCrawler component is not available. * * @return Crawler|null */ protected function createCrawlerFromContent(string $uri, string $content, string $type) { if (!class_exists(Crawler::class)) { return null; } $crawler = new Crawler(null, $uri); $crawler->addContent($content, $type); return $crawler; } /** * Goes back in the browser history. * * @return Crawler */ public function back() { do { $request = $this->history->back(); } while (\array_key_exists(serialize($request), $this->redirects)); return $this->requestFromRequest($request, false); } /** * Goes forward in the browser history. * * @return Crawler */ public function forward() { do { $request = $this->history->forward(); } while (\array_key_exists(serialize($request), $this->redirects)); return $this->requestFromRequest($request, false); } /** * Reloads the current browser. * * @return Crawler */ public function reload() { return $this->requestFromRequest($this->history->current(), false); } /** * Follow redirects? * * @return Crawler * * @throws \LogicException If request was not a redirect */ public function followRedirect() { if (empty($this->redirect)) { throw new \LogicException('The request was not redirected.'); } if (-1 !== $this->maxRedirects) { if ($this->redirectCount > $this->maxRedirects) { $this->redirectCount = 0; throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects)); } } $request = $this->internalRequest; if (\in_array($this->internalResponse->getStatusCode(), [301, 302, 303])) { $method = 'GET'; $files = []; $content = null; } else { $method = $request->getMethod(); $files = $request->getFiles(); $content = $request->getContent(); } if ('GET' === strtoupper($method)) { // Don't forward parameters for GET request as it should reach the redirection URI $parameters = []; } else { $parameters = $request->getParameters(); } $server = $request->getServer(); $server = $this->updateServerFromUri($server, $this->redirect); $this->isMainRequest = false; $response = $this->request($method, $this->redirect, $parameters, $files, $server, $content); $this->isMainRequest = true; return $response; } /** * @see https://dev.w3.org/html5/spec-preview/the-meta-element.html#attr-meta-http-equiv-refresh */ private function getMetaRefreshUrl(): ?string { $metaRefresh = $this->getCrawler()->filter('head meta[http-equiv="refresh"]'); foreach ($metaRefresh->extract(['content']) as $content) { if (preg_match('/^\s*0\s*;\s*URL\s*=\s*(?|\'([^\']++)|"([^"]++)|([^\'"].*))/i', $content, $m)) { return str_replace("\t\r\n", '', rtrim($m[1])); } } return null; } /** * Restarts the client. * * It flushes history and all cookies. */ public function restart() { $this->cookieJar->clear(); $this->history->clear(); } /** * Takes a URI and converts it to absolute if it is not already absolute. * * @return string */ protected function getAbsoluteUri(string $uri) { // already absolute? if (0 === strpos($uri, 'http://') || 0 === strpos($uri, 'https://')) { return $uri; } if (!$this->history->isEmpty()) { $currentUri = $this->history->current()->getUri(); } else { $currentUri = sprintf('http%s://%s/', isset($this->server['HTTPS']) ? 's' : '', $this->server['HTTP_HOST'] ?? 'localhost' ); } // protocol relative URL if ('' !== trim($uri, '/') && str_starts_with($uri, '//')) { return parse_url($currentUri, \PHP_URL_SCHEME).':'.$uri; } // anchor or query string parameters? if (!$uri || '#' == $uri[0] || '?' == $uri[0]) { return preg_replace('/[#?].*?$/', '', $currentUri).$uri; } if ('/' !== $uri[0]) { $path = parse_url($currentUri, \PHP_URL_PATH); if ('/' !== substr($path, -1)) { $path = substr($path, 0, strrpos($path, '/') + 1); } $uri = $path.$uri; } return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri; } /** * Makes a request from a Request object directly. * * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload()) * * @return Crawler */ protected function requestFromRequest(Request $request, bool $changeHistory = true) { return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory); } private function updateServerFromUri(array $server, string $uri): array { $server['HTTP_HOST'] = $this->extractHost($uri); $scheme = parse_url($uri, \PHP_URL_SCHEME); $server['HTTPS'] = null === $scheme ? $server['HTTPS'] : 'https' == $scheme; unset($server['HTTP_IF_NONE_MATCH'], $server['HTTP_IF_MODIFIED_SINCE']); return $server; } private function extractHost(string $uri): ?string { $host = parse_url($uri, \PHP_URL_HOST); if ($port = parse_url($uri, \PHP_URL_PORT)) { return $host.':'.$port; } return $host; } } CHANGELOG.md 0000644 00000003233 15120140424 0006347 0 ustar 00 CHANGELOG ========= 5.3 --- * Added `jsonRequest` method to `AbstractBrowser` * Allowed sending a body with GET requests when a content-type is defined 5.2.0 ----- * [BC BREAK] Request parameters are now casted to string in `Request::__construct()`. 4.3.0 ----- * Added PHPUnit constraints: `BrowserCookieValueSame` and `BrowserHasCookie` * Added `HttpBrowser`, an implementation of a browser with the HttpClient component * Renamed `Client` to `AbstractBrowser` * Marked `Response` final. * Deprecated `Response::buildHeader()` * Deprecated `Response::getStatus()`, use `Response::getStatusCode()` instead 4.2.0 ----- * The method `Client::submit()` will have a new `$serverParameters` argument in version 5.0, not defining it is deprecated * Added ability to read the "samesite" attribute of cookies using `Cookie::getSameSite()` 3.4.0 ----- * [BC BREAK] Client will skip redirects during history navigation (back and forward calls) according to W3C Browsers recommendation 3.3.0 ----- * [BC BREAK] The request method is dropped from POST to GET when the response status code is 301. 3.2.0 ----- * Client HTTP user agent has been changed to 'Symfony BrowserKit' 2.3.0 ----- * [BC BREAK] `Client::followRedirect()` won't redirect responses with a non-3xx Status Code and `Location` header anymore, as per http://tools.ietf.org/html/rfc2616#section-14.30 * added `Client::getInternalRequest()` and `Client::getInternalResponse()` to have access to the BrowserKit internal request and response objects 2.1.0 ----- * [BC BREAK] The CookieJar internals have changed to allow cookies with the same name on different sub-domains/sub-paths Cookie.php 0000644 00000020650 15120140424 0006462 0 ustar 00 <?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\BrowserKit; /** * Cookie represents an HTTP cookie. * * @author Fabien Potencier <fabien@symfony.com> */ class Cookie { /** * Handles dates as defined by RFC 2616 section 3.3.1, and also some other * non-standard, but common formats. */ private const DATE_FORMATS = [ 'D, d M Y H:i:s T', 'D, d-M-y H:i:s T', 'D, d-M-Y H:i:s T', 'D, d-m-y H:i:s T', 'D, d-m-Y H:i:s T', 'D M j G:i:s Y', 'D M d H:i:s Y T', ]; protected $name; protected $value; protected $expires; protected $path; protected $domain; protected $secure; protected $httponly; protected $rawValue; private $samesite; /** * Sets a cookie. * * @param string $name The cookie name * @param string|null $value The value of the cookie * @param string|null $expires The time the cookie expires * @param string|null $path The path on the server in which the cookie will be available on * @param string $domain The domain that the cookie is available * @param bool $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client * @param bool $httponly The cookie httponly flag * @param bool $encodedValue Whether the value is encoded or not * @param string|null $samesite The cookie samesite attribute */ public function __construct(string $name, ?string $value, string $expires = null, string $path = null, string $domain = '', bool $secure = false, bool $httponly = true, bool $encodedValue = false, string $samesite = null) { if ($encodedValue) { $this->value = urldecode($value); $this->rawValue = $value; } else { $this->value = $value; $this->rawValue = rawurlencode($value ?? ''); } $this->name = $name; $this->path = empty($path) ? '/' : $path; $this->domain = $domain; $this->secure = $secure; $this->httponly = $httponly; $this->samesite = $samesite; if (null !== $expires) { $timestampAsDateTime = \DateTime::createFromFormat('U', $expires); if (false === $timestampAsDateTime) { throw new \UnexpectedValueException(sprintf('The cookie expiration time "%s" is not valid.', $expires)); } $this->expires = $timestampAsDateTime->format('U'); } } /** * Returns the HTTP representation of the Cookie. * * @return string */ public function __toString() { $cookie = sprintf('%s=%s', $this->name, $this->rawValue); if (null !== $this->expires) { $dateTime = \DateTime::createFromFormat('U', $this->expires, new \DateTimeZone('GMT')); $cookie .= '; expires='.str_replace('+0000', '', $dateTime->format(self::DATE_FORMATS[0])); } if ('' !== $this->domain) { $cookie .= '; domain='.$this->domain; } if ($this->path) { $cookie .= '; path='.$this->path; } if ($this->secure) { $cookie .= '; secure'; } if ($this->httponly) { $cookie .= '; httponly'; } if (null !== $this->samesite) { $cookie .= '; samesite='.$this->samesite; } return $cookie; } /** * Creates a Cookie instance from a Set-Cookie header value. * * @return static * * @throws \InvalidArgumentException */ public static function fromString(string $cookie, string $url = null) { $parts = explode(';', $cookie); if (!str_contains($parts[0], '=')) { throw new \InvalidArgumentException(sprintf('The cookie string "%s" is not valid.', $parts[0])); } [$name, $value] = explode('=', array_shift($parts), 2); $values = [ 'name' => trim($name), 'value' => trim($value), 'expires' => null, 'path' => '/', 'domain' => '', 'secure' => false, 'httponly' => false, 'passedRawValue' => true, 'samesite' => null, ]; if (null !== $url) { if ((false === $urlParts = parse_url($url)) || !isset($urlParts['host'])) { throw new \InvalidArgumentException(sprintf('The URL "%s" is not valid.', $url)); } $values['domain'] = $urlParts['host']; $values['path'] = isset($urlParts['path']) ? substr($urlParts['path'], 0, strrpos($urlParts['path'], '/')) : ''; } foreach ($parts as $part) { $part = trim($part); if ('secure' === strtolower($part)) { // Ignore the secure flag if the original URI is not given or is not HTTPS if (!$url || !isset($urlParts['scheme']) || 'https' != $urlParts['scheme']) { continue; } $values['secure'] = true; continue; } if ('httponly' === strtolower($part)) { $values['httponly'] = true; continue; } if (2 === \count($elements = explode('=', $part, 2))) { if ('expires' === strtolower($elements[0])) { $elements[1] = self::parseDate($elements[1]); } $values[strtolower($elements[0])] = $elements[1]; } } return new static( $values['name'], $values['value'], $values['expires'], $values['path'], $values['domain'], $values['secure'], $values['httponly'], $values['passedRawValue'], $values['samesite'] ); } private static function parseDate(string $dateValue): ?string { // trim single quotes around date if present if (($length = \strlen($dateValue)) > 1 && "'" === $dateValue[0] && "'" === $dateValue[$length - 1]) { $dateValue = substr($dateValue, 1, -1); } foreach (self::DATE_FORMATS as $dateFormat) { if (false !== $date = \DateTime::createFromFormat($dateFormat, $dateValue, new \DateTimeZone('GMT'))) { return $date->format('U'); } } // attempt a fallback for unusual formatting if (false !== $date = date_create($dateValue, new \DateTimeZone('GMT'))) { return $date->format('U'); } return null; } /** * Gets the name of the cookie. * * @return string */ public function getName() { return $this->name; } /** * Gets the value of the cookie. * * @return string */ public function getValue() { return $this->value; } /** * Gets the raw value of the cookie. * * @return string */ public function getRawValue() { return $this->rawValue; } /** * Gets the expires time of the cookie. * * @return string|null */ public function getExpiresTime() { return $this->expires; } /** * Gets the path of the cookie. * * @return string */ public function getPath() { return $this->path; } /** * Gets the domain of the cookie. * * @return string */ public function getDomain() { return $this->domain; } /** * Returns the secure flag of the cookie. * * @return bool */ public function isSecure() { return $this->secure; } /** * Returns the httponly flag of the cookie. * * @return bool */ public function isHttpOnly() { return $this->httponly; } /** * Returns true if the cookie has expired. * * @return bool */ public function isExpired() { return null !== $this->expires && 0 != $this->expires && $this->expires <= time(); } /** * Gets the samesite attribute of the cookie. */ public function getSameSite(): ?string { return $this->samesite; } } CookieJar.php 0000644 00000014252 15120140424 0007120 0 ustar 00 <?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\BrowserKit; /** * CookieJar. * * @author Fabien Potencier <fabien@symfony.com> */ class CookieJar { protected $cookieJar = []; public function set(Cookie $cookie) { $this->cookieJar[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie; } /** * Gets a cookie by name. * * You should never use an empty domain, but if you do so, * this method returns the first cookie for the given name/path * (this behavior ensures a BC behavior with previous versions of * Symfony). * * @return Cookie|null */ public function get(string $name, string $path = '/', string $domain = null) { $this->flushExpiredCookies(); foreach ($this->cookieJar as $cookieDomain => $pathCookies) { if ($cookieDomain && $domain) { $cookieDomain = '.'.ltrim($cookieDomain, '.'); if (!str_ends_with('.'.$domain, $cookieDomain)) { continue; } } foreach ($pathCookies as $cookiePath => $namedCookies) { if (!str_starts_with($path, $cookiePath)) { continue; } if (isset($namedCookies[$name])) { return $namedCookies[$name]; } } } return null; } /** * Removes a cookie by name. * * You should never use an empty domain, but if you do so, * all cookies for the given name/path expire (this behavior * ensures a BC behavior with previous versions of Symfony). */ public function expire(string $name, ?string $path = '/', string $domain = null) { if (null === $path) { $path = '/'; } if (empty($domain)) { // an empty domain means any domain // this should never happen but it allows for a better BC $domains = array_keys($this->cookieJar); } else { $domains = [$domain]; } foreach ($domains as $domain) { unset($this->cookieJar[$domain][$path][$name]); if (empty($this->cookieJar[$domain][$path])) { unset($this->cookieJar[$domain][$path]); if (empty($this->cookieJar[$domain])) { unset($this->cookieJar[$domain]); } } } } /** * Removes all the cookies from the jar. */ public function clear() { $this->cookieJar = []; } /** * Updates the cookie jar from a response Set-Cookie headers. * * @param string[] $setCookies Set-Cookie headers from an HTTP response */ public function updateFromSetCookie(array $setCookies, string $uri = null) { $cookies = []; foreach ($setCookies as $cookie) { foreach (explode(',', $cookie) as $i => $part) { if (0 === $i || preg_match('/^(?P<token>\s*[0-9A-Za-z!#\$%\&\'\*\+\-\.^_`\|~]+)=/', $part)) { $cookies[] = ltrim($part); } else { $cookies[\count($cookies) - 1] .= ','.$part; } } } foreach ($cookies as $cookie) { try { $this->set(Cookie::fromString($cookie, $uri)); } catch (\InvalidArgumentException $e) { // invalid cookies are just ignored } } } /** * Updates the cookie jar from a Response object. */ public function updateFromResponse(Response $response, string $uri = null) { $this->updateFromSetCookie($response->getHeader('Set-Cookie', false), $uri); } /** * Returns not yet expired cookies. * * @return Cookie[] */ public function all() { $this->flushExpiredCookies(); $flattenedCookies = []; foreach ($this->cookieJar as $path) { foreach ($path as $cookies) { foreach ($cookies as $cookie) { $flattenedCookies[] = $cookie; } } } return $flattenedCookies; } /** * Returns not yet expired cookie values for the given URI. * * @return array */ public function allValues(string $uri, bool $returnsRawValue = false) { $this->flushExpiredCookies(); $parts = array_replace(['path' => '/'], parse_url($uri)); $cookies = []; foreach ($this->cookieJar as $domain => $pathCookies) { if ($domain) { $domain = '.'.ltrim($domain, '.'); if ($domain != substr('.'.$parts['host'], -\strlen($domain))) { continue; } } foreach ($pathCookies as $path => $namedCookies) { if ($path != substr($parts['path'], 0, \strlen($path))) { continue; } foreach ($namedCookies as $cookie) { if ($cookie->isSecure() && 'https' != $parts['scheme']) { continue; } $cookies[$cookie->getName()] = $returnsRawValue ? $cookie->getRawValue() : $cookie->getValue(); } } } return $cookies; } /** * Returns not yet expired raw cookie values for the given URI. * * @return array */ public function allRawValues(string $uri) { return $this->allValues($uri, true); } /** * Removes all expired cookies. */ public function flushExpiredCookies() { foreach ($this->cookieJar as $domain => $pathCookies) { foreach ($pathCookies as $path => $namedCookies) { foreach ($namedCookies as $name => $cookie) { if ($cookie->isExpired()) { unset($this->cookieJar[$domain][$path][$name]); } } } } } } History.php 0000644 00000004165 15120140424 0006715 0 ustar 00 <?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\BrowserKit; /** * History. * * @author Fabien Potencier <fabien@symfony.com> */ class History { protected $stack = []; protected $position = -1; /** * Clears the history. */ public function clear() { $this->stack = []; $this->position = -1; } /** * Adds a Request to the history. */ public function add(Request $request) { $this->stack = \array_slice($this->stack, 0, $this->position + 1); $this->stack[] = clone $request; $this->position = \count($this->stack) - 1; } /** * Returns true if the history is empty. * * @return bool */ public function isEmpty() { return 0 == \count($this->stack); } /** * Goes back in the history. * * @return Request * * @throws \LogicException if the stack is already on the first page */ public function back() { if ($this->position < 1) { throw new \LogicException('You are already on the first page.'); } return clone $this->stack[--$this->position]; } /** * Goes forward in the history. * * @return Request * * @throws \LogicException if the stack is already on the last page */ public function forward() { if ($this->position > \count($this->stack) - 2) { throw new \LogicException('You are already on the last page.'); } return clone $this->stack[++$this->position]; } /** * Returns the current element in the history. * * @return Request * * @throws \LogicException if the stack is empty */ public function current() { if (-1 == $this->position) { throw new \LogicException('The page history is empty.'); } return clone $this->stack[$this->position]; } } HttpBrowser.php 0000644 00000012142 15120140424 0007531 0 ustar 00 <?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\BrowserKit; use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\Mime\Part\AbstractPart; use Symfony\Component\Mime\Part\DataPart; use Symfony\Component\Mime\Part\Multipart\FormDataPart; use Symfony\Component\Mime\Part\TextPart; use Symfony\Contracts\HttpClient\HttpClientInterface; /** * An implementation of a browser using the HttpClient component * to make real HTTP requests. * * @author Fabien Potencier <fabien@symfony.com> */ class HttpBrowser extends AbstractBrowser { private $client; public function __construct(HttpClientInterface $client = null, History $history = null, CookieJar $cookieJar = null) { if (!$client && !class_exists(HttpClient::class)) { throw new \LogicException(sprintf('You cannot use "%s" as the HttpClient component is not installed. Try running "composer require symfony/http-client".', __CLASS__)); } $this->client = $client ?? HttpClient::create(); parent::__construct([], $history, $cookieJar); } /** * @param Request $request */ protected function doRequest(object $request): Response { $headers = $this->getHeaders($request); [$body, $extraHeaders] = $this->getBodyAndExtraHeaders($request, $headers); $response = $this->client->request($request->getMethod(), $request->getUri(), [ 'headers' => array_merge($headers, $extraHeaders), 'body' => $body, 'max_redirects' => 0, ]); return new Response($response->getContent(false), $response->getStatusCode(), $response->getHeaders(false)); } /** * @return array [$body, $headers] */ private function getBodyAndExtraHeaders(Request $request, array $headers): array { if (\in_array($request->getMethod(), ['GET', 'HEAD']) && !isset($headers['content-type'])) { return ['', []]; } if (!class_exists(AbstractPart::class)) { throw new \LogicException('You cannot pass non-empty bodies as the Mime component is not installed. Try running "composer require symfony/mime".'); } if (null !== $content = $request->getContent()) { if (isset($headers['content-type'])) { return [$content, []]; } $part = new TextPart($content, 'utf-8', 'plain', '8bit'); return [$part->bodyToString(), $part->getPreparedHeaders()->toArray()]; } $fields = $request->getParameters(); if ($uploadedFiles = $this->getUploadedFiles($request->getFiles())) { $part = new FormDataPart(array_replace_recursive($fields, $uploadedFiles)); return [$part->bodyToIterable(), $part->getPreparedHeaders()->toArray()]; } if (empty($fields)) { return ['', []]; } array_walk_recursive($fields, $caster = static function (&$v) use (&$caster) { if (\is_object($v)) { if ($vars = get_object_vars($v)) { array_walk_recursive($vars, $caster); $v = $vars; } elseif (method_exists($v, '__toString')) { $v = (string) $v; } } }); return [http_build_query($fields, '', '&'), ['Content-Type' => 'application/x-www-form-urlencoded']]; } protected function getHeaders(Request $request): array { $headers = []; foreach ($request->getServer() as $key => $value) { $key = strtolower(str_replace('_', '-', $key)); $contentHeaders = ['content-length' => true, 'content-md5' => true, 'content-type' => true]; if (str_starts_with($key, 'http-')) { $headers[substr($key, 5)] = $value; } elseif (isset($contentHeaders[$key])) { // CONTENT_* are not prefixed with HTTP_ $headers[$key] = $value; } } $cookies = []; foreach ($this->getCookieJar()->allRawValues($request->getUri()) as $name => $value) { $cookies[] = $name.'='.$value; } if ($cookies) { $headers['cookie'] = implode('; ', $cookies); } return $headers; } /** * Recursively go through the list. If the file has a tmp_name, convert it to a DataPart. * Keep the original hierarchy. */ private function getUploadedFiles(array $files): array { $uploadedFiles = []; foreach ($files as $name => $file) { if (!\is_array($file)) { return $uploadedFiles; } if (!isset($file['tmp_name'])) { $uploadedFiles[$name] = $this->getUploadedFiles($file); } if (isset($file['tmp_name'])) { $uploadedFiles[$name] = DataPart::fromPath($file['tmp_name'], $file['name']); } } return $uploadedFiles; } } LICENSE 0000644 00000002054 15120140424 0005543 0 ustar 00 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. README.md 0000644 00000001273 15120140424 0006017 0 ustar 00 BrowserKit Component ==================== The BrowserKit component simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically. The component comes with a concrete implementation that uses the HttpClient component to make real HTTP requests. Resources --------- * [Documentation](https://symfony.com/doc/current/components/browser_kit/introduction.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) Request.php 0000644 00000005001 15120140424 0006672 0 ustar 00 <?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\BrowserKit; /** * @author Fabien Potencier <fabien@symfony.com> */ class Request { protected $uri; protected $method; protected $parameters; protected $files; protected $cookies; protected $server; protected $content; /** * @param string $uri The request URI * @param string $method The HTTP method request * @param array $parameters The request parameters * @param array $files An array of uploaded files * @param array $cookies An array of cookies * @param array $server An array of server parameters * @param string $content The raw body data */ public function __construct(string $uri, string $method, array $parameters = [], array $files = [], array $cookies = [], array $server = [], string $content = null) { $this->uri = $uri; $this->method = $method; array_walk_recursive($parameters, static function (&$value) { $value = (string) $value; }); $this->parameters = $parameters; $this->files = $files; $this->cookies = $cookies; $this->server = $server; $this->content = $content; } /** * Gets the request URI. * * @return string */ public function getUri() { return $this->uri; } /** * Gets the request HTTP method. * * @return string */ public function getMethod() { return $this->method; } /** * Gets the request parameters. * * @return array */ public function getParameters() { return $this->parameters; } /** * Gets the request server files. * * @return array */ public function getFiles() { return $this->files; } /** * Gets the request cookies. * * @return array */ public function getCookies() { return $this->cookies; } /** * Gets the request server parameters. * * @return array */ public function getServer() { return $this->server; } /** * Gets the request raw body data. * * @return string|null */ public function getContent() { return $this->content; } } Response.php 0000644 00000005053 15120140424 0007047 0 ustar 00 <?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\BrowserKit; /** * @author Fabien Potencier <fabien@symfony.com> */ final class Response { private $content; private $status; private $headers; /** * The headers array is a set of key/value pairs. If a header is present multiple times * then the value is an array of all the values. * * @param string $content The content of the response * @param int $status The response status code * @param array $headers An array of headers */ public function __construct(string $content = '', int $status = 200, array $headers = []) { $this->content = $content; $this->status = $status; $this->headers = $headers; } /** * Converts the response object to string containing all headers and the response content. */ public function __toString(): string { $headers = ''; foreach ($this->headers as $name => $value) { if (\is_string($value)) { $headers .= sprintf("%s: %s\n", $name, $value); } else { foreach ($value as $headerValue) { $headers .= sprintf("%s: %s\n", $name, $headerValue); } } } return $headers."\n".$this->content; } /** * Gets the response content. */ public function getContent(): string { return $this->content; } public function getStatusCode(): int { return $this->status; } /** * Gets the response headers. */ public function getHeaders(): array { return $this->headers; } /** * Gets a response header. * * @return string|array|null The first header value if $first is true, an array of values otherwise */ public function getHeader(string $header, bool $first = true) { $normalizedHeader = str_replace('-', '_', strtolower($header)); foreach ($this->headers as $key => $value) { if (str_replace('-', '_', strtolower($key)) === $normalizedHeader) { if ($first) { return \is_array($value) ? (\count($value) ? $value[0] : '') : $value; } return \is_array($value) ? $value : [$value]; } } return $first ? null : []; } } composer.json 0000644 00000002145 15120140424 0007261 0 ustar 00 { "name": "symfony/browser-kit", "type": "library", "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "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": ">=7.2.5", "symfony/dom-crawler": "^4.4|^5.0|^6.0", "symfony/polyfill-php80": "^1.16" }, "require-dev": { "symfony/css-selector": "^4.4|^5.0|^6.0", "symfony/http-client": "^4.4|^5.0|^6.0", "symfony/mime": "^4.4|^5.0|^6.0", "symfony/process": "^4.4|^5.0|^6.0" }, "suggest": { "symfony/process": "" }, "autoload": { "psr-4": { "Symfony\\Component\\BrowserKit\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "minimum-stability": "dev" }
Coded With 💗 by
0x6ick