Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
81.58% covered (warning)
81.58%
31 / 38
CRAP
95.58% covered (success)
95.58%
173 / 181
AbstractBrowser
0.00% covered (danger)
0.00%
0 / 1
81.58% covered (warning)
81.58%
31 / 38
101
95.58% covered (success)
95.58%
173 / 181
 __construct
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
4 / 4
 followRedirects
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 followMetaRefresh
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 isFollowingRedirects
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 setMaxRedirects
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 getMaxRedirects
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 insulate
0.00% covered (danger)
0.00%
0 / 1
3.14
75.00% covered (warning)
75.00%
3 / 4
 setServerParameters
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 setServerParameter
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 getServerParameter
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
1 / 1
 xmlHttpRequest
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getHistory
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getCookieJar
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 getCrawler
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 getInternalResponse
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 getResponse
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 getInternalRequest
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 getRequest
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 click
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 clickLink
0.00% covered (danger)
0.00%
0 / 1
2.15
66.67% covered (warning)
66.67%
2 / 3
 submit
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 submitForm
0.00% covered (danger)
0.00%
0 / 1
2.03
80.00% covered (warning)
80.00%
4 / 5
 request
100.00% covered (success)
100.00%
1 / 1
18
100.00% covered (success)
100.00%
37 / 37
 doRequestInProcess
0.00% covered (danger)
0.00%
0 / 1
7.01
93.33% covered (success)
93.33%
14 / 15
 doRequest
n/a
0 / 0
1
n/a
0 / 0
 getScript
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 filterRequest
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 filterResponse
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 createCrawlerFromContent
0.00% covered (danger)
0.00%
0 / 1
2.03
80.00% covered (warning)
80.00%
4 / 5
 back
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 forward
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
3 / 3
 reload
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 followRedirect
0.00% covered (danger)
0.00%
0 / 1
6.02
91.30% covered (success)
91.30%
21 / 23
 getMetaRefreshUrl
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
5 / 5
 restart
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 getAbsoluteUri
100.00% covered (success)
100.00%
1 / 1
12
100.00% covered (success)
100.00%
17 / 17
 requestFromRequest
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 updateServerFromUri
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
 extractHost
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
4 / 4
<?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.
     *
     * @param bool $insulated Whether to insulate the requests or not
     *
     * @throws \RuntimeException When Symfony Process Component is not installed
     */
    public function insulate(bool $insulated = true)
    {
        if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) {
            throw new \LogicException('Unable to isolate requests as the Symfony Process Component is not installed.');
        }
        $this->insulated = $insulated;
    }
    /**
     * Sets server parameters.
     *
     * @param array $server An array of 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 A value of the parameter
     */
    public function getServerParameter(string $key, $default = '')
    {
        return isset($this->server[$key]) ? $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']);
        }
    }
    /**
     * 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 A Crawler instance
     */
    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 A BrowserKit Response instance
     */
    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 A response instance
     *
     * @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 A BrowserKit Request instance
     */
    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 A Request instance
     *
     * @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 a 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 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(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);
        }