Move into nested docroot

This commit is contained in:
Rob Davies 2017-02-13 15:31:17 +00:00
parent 83a0d3a149
commit c8b70abde9
13405 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,3 @@
vendor/
composer.lock
phpunit.xml

View file

@ -0,0 +1,18 @@
CHANGELOG
=========
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

View file

@ -0,0 +1,596 @@
<?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\DomCrawler\Crawler;
use Symfony\Component\DomCrawler\Link;
use Symfony\Component\DomCrawler\Form;
use Symfony\Component\Process\PhpProcess;
/**
* Client 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 Client
{
protected $history;
protected $cookieJar;
protected $server = array();
protected $internalRequest;
protected $request;
protected $internalResponse;
protected $response;
protected $crawler;
protected $insulated = false;
protected $redirect;
protected $followRedirects = true;
private $maxRedirects = -1;
private $redirectCount = 0;
private $isMainRequest = true;
/**
* Constructor.
*
* @param array $server The server parameters (equivalent of $_SERVER)
* @param History $history A History instance to store the browser history
* @param CookieJar $cookieJar A CookieJar instance to store the cookies
*/
public function __construct(array $server = array(), 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.
*
* @param bool $followRedirect Whether to follow redirects
*/
public function followRedirects($followRedirect = true)
{
$this->followRedirects = (bool) $followRedirect;
}
/**
* Returns whether client automatically follows redirects or not.
*
* @return bool
*/
public function isFollowingRedirects()
{
return $this->followRedirects;
}
/**
* Sets the maximum number of requests that crawler can follow.
*
* @param int $maxRedirects
*/
public function setMaxRedirects($maxRedirects)
{
$this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects;
$this->followRedirects = -1 != $this->maxRedirects;
}
/**
* Returns the maximum number of requests that crawler can follow.
*
* @return int
*/
public function getMaxRedirects()
{
return $this->maxRedirects;
}
/**
* Sets the insulated flag.
*
* @param bool $insulated Whether to insulate the requests or not
*
* @throws \RuntimeException When Symfony Process Component is not installed
*/
public function insulate($insulated = true)
{
if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) {
throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.');
}
$this->insulated = (bool) $insulated;
}
/**
* Sets server parameters.
*
* @param array $server An array of server parameters
*/
public function setServerParameters(array $server)
{
$this->server = array_merge(array(
'HTTP_USER_AGENT' => 'Symfony2 BrowserKit',
), $server);
}
/**
* Sets single server parameter.
*
* @param string $key A key of the parameter
* @param string $value A value of the parameter
*/
public function setServerParameter($key, $value)
{
$this->server[$key] = $value;
}
/**
* Gets single server parameter for specified key.
*
* @param string $key A key of the parameter to get
* @param string $default A default value when key is undefined
*
* @return string A value of the parameter
*/
public function getServerParameter($key, $default = '')
{
return isset($this->server[$key]) ? $this->server[$key] : $default;
}
/**
* Returns the History instance.
*
* @return History A History instance
*/
public function getHistory()
{
return $this->history;
}
/**
* Returns the CookieJar instance.
*
* @return CookieJar A CookieJar instance
*/
public function getCookieJar()
{
return $this->cookieJar;
}
/**
* Returns the current Crawler instance.
*
* @return Crawler|null A Crawler instance
*/
public function getCrawler()
{
return $this->crawler;
}
/**
* Returns the current BrowserKit Response instance.
*
* @return Response|null A BrowserKit Response instance
*/
public function getInternalResponse()
{
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|null A response instance
*
* @see doRequest()
*/
public function getResponse()
{
return $this->response;
}
/**
* Returns the current BrowserKit Request instance.
*
* @return Request|null A BrowserKit Request instance
*/
public function getInternalRequest()
{
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|null A Request instance
*
* @see doRequest()
*/
public function getRequest()
{
return $this->request;
}
/**
* Clicks on a given link.
*
* @param Link $link A Link instance
*
* @return Crawler
*/
public function click(Link $link)
{
if ($link instanceof Form) {
return $this->submit($link);
}
return $this->request($link->getMethod(), $link->getUri());
}
/**
* Submits a form.
*
* @param Form $form A Form instance
* @param array $values An array of form field values
*
* @return Crawler
*/
public function submit(Form $form, array $values = array())
{
$form->setValues($values);
return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles());
}
/**
* 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 a 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($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true)
{
if ($this->isMainRequest) {
$this->redirectCount = 0;
} else {
++$this->redirectCount;
}
$uri = $this->getAbsoluteUri($uri);
$server = array_merge($this->server, $server);
if (isset($server['HTTPS'])) {
$uri = preg_replace('{^'.parse_url($uri, PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri);
}
if (!$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->getStatus();
if ($status >= 300 && $status < 400) {
$this->redirect = $this->internalResponse->getHeader('Location');
} else {
$this->redirect = null;
}
if ($this->followRedirects && $this->redirect) {
return $this->crawler = $this->followRedirect();
}
return $this->crawler = $this->createCrawlerFromContent($this->internalRequest->getUri(), $this->internalResponse->getContent(), $this->internalResponse->getHeader('Content-Type'));
}
/**
* Makes a request in another process.
*
* @param object $request An origin request instance
*
* @return object An origin response instance
*
* @throws \RuntimeException When processing returns exit code
*/
protected function doRequestInProcess($request)
{
$process = new PhpProcess($this->getScript($request), null, null);
$process->run();
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.
*
* @param object $request An origin request instance
*
* @return object An origin response instance
*/
abstract protected function doRequest($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($request)
{
throw new \LogicException('To insulate requests, you need to override the getScript() method.');
}
/**
* Filters the BrowserKit request to the origin one.
*
* @param Request $request The BrowserKit Request to filter
*
* @return object An origin request instance
*/
protected function filterRequest(Request $request)
{
return $request;
}
/**
* Filters the origin response to the BrowserKit one.
*
* @param object $response The origin response to filter
*
* @return Response An BrowserKit Response instance
*/
protected function filterResponse($response)
{
return $response;
}
/**
* Creates a crawler.
*
* This method returns null if the DomCrawler component is not available.
*
* @param string $uri A URI
* @param string $content Content for the crawler to use
* @param string $type Content type
*
* @return Crawler|null
*/
protected function createCrawlerFromContent($uri, $content, $type)
{
if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
return;
}
$crawler = new Crawler(null, $uri);
$crawler->addContent($content, $type);
return $crawler;
}
/**
* Goes back in the browser history.
*
* @return Crawler
*/
public function back()
{
return $this->requestFromRequest($this->history->back(), false);
}
/**
* Goes forward in the browser history.
*
* @return Crawler
*/
public function forward()
{
return $this->requestFromRequest($this->history->forward(), 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) {
throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects));
}
}
$request = $this->internalRequest;
if (in_array($this->internalResponse->getStatus(), array(302, 303))) {
$method = 'GET';
$files = array();
$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 = array();
} 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;
}
/**
* 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.
*
* @param string $uri A URI
*
* @return string An absolute URI
*/
protected function getAbsoluteUri($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' : '',
isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost'
);
}
// protocol relative URL
if (0 === strpos($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 Request $request A Request instance
* @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, $changeHistory = true)
{
return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory);
}
private function updateServerFromUri($server, $uri)
{
$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($uri)
{
$host = parse_url($uri, PHP_URL_HOST);
if ($port = parse_url($uri, PHP_URL_PORT)) {
return $host.':'.$port;
}
return $host;
}
}

View file

@ -0,0 +1,309 @@
<?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.
*
* @var array
*/
private static $dateFormats = array(
'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;
/**
* Sets a cookie.
*
* @param string $name The cookie name
* @param string $value The value of the cookie
* @param string $expires The time the cookie expires
* @param string $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
*/
public function __construct($name, $value, $expires = null, $path = null, $domain = '', $secure = false, $httponly = true, $encodedValue = false)
{
if ($encodedValue) {
$this->value = urldecode($value);
$this->rawValue = $value;
} else {
$this->value = $value;
$this->rawValue = urlencode($value);
}
$this->name = $name;
$this->path = empty($path) ? '/' : $path;
$this->domain = $domain;
$this->secure = (bool) $secure;
$this->httponly = (bool) $httponly;
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 The HTTP representation of the Cookie
*
* @throws \UnexpectedValueException
*/
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::$dateFormats[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';
}
return $cookie;
}
/**
* Creates a Cookie instance from a Set-Cookie header value.
*
* @param string $cookie A Set-Cookie header value
* @param string $url The base URL
*
* @return static
*
* @throws \InvalidArgumentException
*/
public static function fromString($cookie, $url = null)
{
$parts = explode(';', $cookie);
if (false === strpos($parts[0], '=')) {
throw new \InvalidArgumentException(sprintf('The cookie string "%s" is not valid.', $parts[0]));
}
list($name, $value) = explode('=', array_shift($parts), 2);
$values = array(
'name' => trim($name),
'value' => trim($value),
'expires' => null,
'path' => '/',
'domain' => '',
'secure' => false,
'httponly' => false,
'passedRawValue' => true,
);
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']
);
}
private static function parseDate($dateValue)
{
// trim single quotes around date if present
if (($length = strlen($dateValue)) > 1 && "'" === $dateValue[0] && "'" === $dateValue[$length - 1]) {
$dateValue = substr($dateValue, 1, -1);
}
foreach (self::$dateFormats 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');
}
throw new \InvalidArgumentException(sprintf('Could not parse date "%s".', $dateValue));
}
/**
* Gets the name of the cookie.
*
* @return string The cookie name
*/
public function getName()
{
return $this->name;
}
/**
* Gets the value of the cookie.
*
* @return string The cookie value
*/
public function getValue()
{
return $this->value;
}
/**
* Gets the raw value of the cookie.
*
* @return string The cookie value
*/
public function getRawValue()
{
return $this->rawValue;
}
/**
* Gets the expires time of the cookie.
*
* @return string The cookie expires time
*/
public function getExpiresTime()
{
return $this->expires;
}
/**
* Gets the path of the cookie.
*
* @return string The cookie path
*/
public function getPath()
{
return $this->path;
}
/**
* Gets the domain of the cookie.
*
* @return string The cookie domain
*/
public function getDomain()
{
return $this->domain;
}
/**
* Returns the secure flag of the cookie.
*
* @return bool The cookie secure flag
*/
public function isSecure()
{
return $this->secure;
}
/**
* Returns the httponly flag of the cookie.
*
* @return bool The cookie httponly flag
*/
public function isHttpOnly()
{
return $this->httponly;
}
/**
* Returns true if the cookie has expired.
*
* @return bool true if the cookie has expired, false otherwise
*/
public function isExpired()
{
return null !== $this->expires && 0 != $this->expires && $this->expires < time();
}
}

View file

@ -0,0 +1,255 @@
<?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 = array();
/**
* Sets a cookie.
*
* @param Cookie $cookie A Cookie instance
*/
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).
*
* @param string $name The cookie name
* @param string $path The cookie path
* @param string $domain The cookie domain
*
* @return Cookie|null A Cookie instance or null if the cookie does not exist
*/
public function get($name, $path = '/', $domain = null)
{
$this->flushExpiredCookies();
if (!empty($domain)) {
foreach ($this->cookieJar as $cookieDomain => $pathCookies) {
if ($cookieDomain) {
$cookieDomain = '.'.ltrim($cookieDomain, '.');
if ($cookieDomain != substr('.'.$domain, -strlen($cookieDomain))) {
continue;
}
}
foreach ($pathCookies as $cookiePath => $namedCookies) {
if ($cookiePath != substr($path, 0, strlen($cookiePath))) {
continue;
}
if (isset($namedCookies[$name])) {
return $namedCookies[$name];
}
}
}
return;
}
// avoid relying on this behavior that is mainly here for BC reasons
foreach ($this->cookieJar as $cookies) {
if (isset($cookies[$path][$name])) {
return $cookies[$path][$name];
}
}
}
/**
* 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).
*
* @param string $name The cookie name
* @param string $path The cookie path
* @param string $domain The cookie domain
*/
public function expire($name, $path = '/', $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 = array($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 = array();
}
/**
* Updates the cookie jar from a response Set-Cookie headers.
*
* @param array $setCookies Set-Cookie headers from an HTTP response
* @param string $uri The base URL
*/
public function updateFromSetCookie(array $setCookies, $uri = null)
{
$cookies = array();
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.
*
* @param Response $response A Response object
* @param string $uri The base URL
*/
public function updateFromResponse(Response $response, $uri = null)
{
$this->updateFromSetCookie($response->getHeader('Set-Cookie', false), $uri);
}
/**
* Returns not yet expired cookies.
*
* @return Cookie[] An array of cookies
*/
public function all()
{
$this->flushExpiredCookies();
$flattenedCookies = array();
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.
*
* @param string $uri A URI
* @param bool $returnsRawValue Returns raw value or urldecoded value
*
* @return array An array of cookie values
*/
public function allValues($uri, $returnsRawValue = false)
{
$this->flushExpiredCookies();
$parts = array_replace(array('path' => '/'), parse_url($uri));
$cookies = array();
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.
*
* @param string $uri A URI
*
* @return array An array of cookie values
*/
public function allRawValues($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]);
}
}
}
}
}
}

View 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\BrowserKit;
/**
* History.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class History
{
protected $stack = array();
protected $position = -1;
/**
* Clears the history.
*/
public function clear()
{
$this->stack = array();
$this->position = -1;
}
/**
* Adds a Request to the history.
*
* @param Request $request A Request instance
*/
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 true if the history is empty, false otherwise
*/
public function isEmpty()
{
return count($this->stack) == 0;
}
/**
* Goes back in the history.
*
* @return Request A Request instance
*
* @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 A Request instance
*
* @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 A Request instance
*
* @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];
}
}

19
web/vendor/symfony/browser-kit/LICENSE vendored Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2004-2017 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.

View file

@ -0,0 +1,14 @@
BrowserKit Component
====================
The BrowserKit component simulates the behavior of a web browser, allowing you
to make requests, click on links and submit forms programmatically.
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)

View file

@ -0,0 +1,120 @@
<?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;
/**
* Request object.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Request
{
protected $uri;
protected $method;
protected $parameters;
protected $files;
protected $cookies;
protected $server;
protected $content;
/**
* Constructor.
*
* @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($uri, $method, array $parameters = array(), array $files = array(), array $cookies = array(), array $server = array(), $content = null)
{
$this->uri = $uri;
$this->method = $method;
$this->parameters = $parameters;
$this->files = $files;
$this->cookies = $cookies;
$this->server = $server;
$this->content = $content;
}
/**
* Gets the request URI.
*
* @return string The request URI
*/
public function getUri()
{
return $this->uri;
}
/**
* Gets the request HTTP method.
*
* @return string The request HTTP method
*/
public function getMethod()
{
return $this->method;
}
/**
* Gets the request parameters.
*
* @return array The request parameters
*/
public function getParameters()
{
return $this->parameters;
}
/**
* Gets the request server files.
*
* @return array The request files
*/
public function getFiles()
{
return $this->files;
}
/**
* Gets the request cookies.
*
* @return array The request cookies
*/
public function getCookies()
{
return $this->cookies;
}
/**
* Gets the request server parameters.
*
* @return array The request server parameters
*/
public function getServer()
{
return $this->server;
}
/**
* Gets the request raw body data.
*
* @return string The request raw body data
*/
public function getContent()
{
return $this->content;
}
}

View file

@ -0,0 +1,129 @@
<?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;
/**
* Response object.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Response
{
protected $content;
protected $status;
protected $headers;
/**
* Constructor.
*
* 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($content = '', $status = 200, array $headers = array())
{
$this->content = $content;
$this->status = $status;
$this->headers = $headers;
}
/**
* Converts the response object to string containing all headers and the response content.
*
* @return string The response with headers and content
*/
public function __toString()
{
$headers = '';
foreach ($this->headers as $name => $value) {
if (is_string($value)) {
$headers .= $this->buildHeader($name, $value);
} else {
foreach ($value as $headerValue) {
$headers .= $this->buildHeader($name, $headerValue);
}
}
}
return $headers."\n".$this->content;
}
/**
* Returns the build header line.
*
* @param string $name The header name
* @param string $value The header value
*
* @return string The built header line
*/
protected function buildHeader($name, $value)
{
return sprintf("%s: %s\n", $name, $value);
}
/**
* Gets the response content.
*
* @return string The response content
*/
public function getContent()
{
return $this->content;
}
/**
* Gets the response status code.
*
* @return int The response status code
*/
public function getStatus()
{
return $this->status;
}
/**
* Gets the response headers.
*
* @return array The response headers
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Gets a response header.
*
* @param string $header The header name
* @param bool $first Whether to return the first value or all header values
*
* @return string|array The first header value if $first is true, an array of values otherwise
*/
public function getHeader($header, $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 : array($value);
}
}
return $first ? null : array();
}
}

View file

@ -0,0 +1,41 @@
{
"name": "symfony/browser-kit",
"type": "library",
"description": "Symfony BrowserKit Component",
"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": ">=5.3.9",
"symfony/dom-crawler": "~2.1|~3.0.0"
},
"require-dev": {
"symfony/process": "~2.3.34|~2.7,>=2.7.6|~3.0.0",
"symfony/css-selector": "~2.0,>=2.0.5|~3.0.0"
},
"suggest": {
"symfony/process": ""
},
"autoload": {
"psr-4": { "Symfony\\Component\\BrowserKit\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "2.8-dev"
}
}
}

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Symfony BrowserKit Component Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>