WebTestCase::createUserToken()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 11
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 18
ccs 0
cts 12
cp 0
crap 6
rs 9.9
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Liip/FunctionalTestBundle
7
 *
8
 * (c) Lukas Kahwe Smith <[email protected]>
9
 *
10
 * This source file is subject to the MIT license that is bundled
11
 * with this source code in the file LICENSE.
12
 */
13
14
namespace Liip\FunctionalTestBundle\Test;
15
16
use Liip\FunctionalTestBundle\Utils\HttpAssertions;
17
use PHPUnit\Framework\MockObject\MockBuilder;
18
use Symfony\Bundle\FrameworkBundle\Client;
19
use Symfony\Bundle\FrameworkBundle\Console\Application;
20
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
21
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase;
22
use Symfony\Component\BrowserKit\Cookie;
23
use Symfony\Component\Console\Tester\CommandTester;
24
use Symfony\Component\DependencyInjection\ContainerInterface;
25
use Symfony\Component\DependencyInjection\ResettableContainerInterface;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Depend...tableContainerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
use Symfony\Component\DomCrawler\Crawler;
27
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
28
use Symfony\Component\HttpFoundation\Request;
29
use Symfony\Component\HttpFoundation\RequestStack;
30
use Symfony\Component\HttpFoundation\Response;
31
use Symfony\Component\HttpFoundation\Session\Session;
32
use Symfony\Component\HttpFoundation\Session\SessionInterface;
33
use Symfony\Component\HttpKernel\KernelInterface;
34
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
35
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
36
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
37
use Symfony\Component\Security\Core\User\UserInterface;
38
39
if (!class_exists(Client::class)) {
40
    class_alias(KernelBrowser::class, Client::class);
41
}
42
43
/**
44
 * @author Lea Haensenberger
45
 * @author Lukas Kahwe Smith <[email protected]>
46
 * @author Benjamin Eberlei <[email protected]>
47
 *
48
 * @method ContainerInterface getContainer()
49
 *
50
 * @property string $environment;
51
 */
52
abstract class WebTestCase extends BaseWebTestCase
53
{
54
    protected static $env = 'test';
55
56
    protected $containers;
57
58
    // 5 * 1024 * 1024 KB
59
    protected $maxMemory = 5242880;
60
61
    // RUN COMMAND
62
    protected $verbosityLevel;
63
64
    protected $decorated;
65
66
    /**
67
     * @var array|null
68
     */
69
    private $inputs = null;
70
71
    /**
72
     * @var array
73
     */
74
    private $firewallLogins = [];
75
76
    /**
77
     * Creates a mock object of a service identified by its id.
78
     */
79
    protected function getServiceMockBuilder(string $id): MockBuilder
80
    {
81
        $service = $this->getContainer()->get($id);
82
        $class = \get_class($service);
0 ignored issues
show
Bug introduced by
It seems like $service can also be of type null; however, parameter $object of get_class() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

82
        $class = \get_class(/** @scrutinizer ignore-type */ $service);
Loading history...
83
84
        return $this->getMockBuilder($class)->disableOriginalConstructor();
85
    }
86
87
    /**
88
     * Builds up the environment to run the given command.
89
     */
90 12
    protected function runCommand(string $name, array $params = [], bool $reuseKernel = false): CommandTester
91
    {
92 12
        if (!$reuseKernel) {
93 12
            if (null !== static::$kernel) {
94 1
                static::ensureKernelShutdown();
95
            }
96
97 12
            $kernel = static::bootKernel(['environment' => static::$env]);
98 12
            $kernel->boot();
99
        } else {
100 1
            $kernel = $this->getContainer()->get('kernel');
101
        }
102
103 12
        $application = new Application($kernel);
0 ignored issues
show
Bug introduced by
It seems like $kernel can also be of type null; however, parameter $kernel of Symfony\Bundle\Framework...lication::__construct() does only seem to accept Symfony\Component\HttpKernel\KernelInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

103
        $application = new Application(/** @scrutinizer ignore-type */ $kernel);
Loading history...
104
105
        $options = [
106 12
            'interactive' => false,
107 12
            'decorated' => $this->getDecorated(),
108 12
            'verbosity' => $this->getVerbosityLevel(),
109
        ];
110
111 11
        $command = $application->find($name);
112 11
        $commandTester = new CommandTester($command);
113
114 11
        if (null !== $inputs = $this->getInputs()) {
115 1
            $commandTester->setInputs($inputs);
116 1
            $options['interactive'] = true;
117 1
            $this->inputs = null;
118
        }
119
120 11
        $commandTester->execute(
121 11
            array_merge(['command' => $command->getName()], $params),
122 11
            $options
123
        );
124
125 11
        return $commandTester;
126
    }
127
128
    /**
129
     * Retrieves the output verbosity level.
130
     *
131
     * @see \Symfony\Component\Console\Output\OutputInterface for available levels
132
     *
133
     * @throws \OutOfBoundsException If the set value isn't accepted
134
     */
135 12
    protected function getVerbosityLevel(): int
136
    {
137
        // If `null`, is not yet set
138 12
        if (null === $this->verbosityLevel) {
139
            // Set the global verbosity level that is set as NORMAL by the TreeBuilder in Configuration
140 6
            $level = strtoupper($this->getContainer()->getParameter('liip_functional_test.command_verbosity'));
141 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
142
143 6
            $this->verbosityLevel = \constant($verbosity);
144
        }
145
146
        // If string, it is set by the developer, so check that the value is an accepted one
147 12
        if (\is_string($this->verbosityLevel)) {
148 6
            $level = strtoupper($this->verbosityLevel);
149 6
            $verbosity = '\Symfony\Component\Console\Output\StreamOutput::VERBOSITY_'.$level;
150
151 6
            if (!\defined($verbosity)) {
152 1
                throw new \OutOfBoundsException(sprintf('The set value "%s" for verbosityLevel is not valid. Accepted are: "quiet", "normal", "verbose", "very_verbose" and "debug".', $level));
153
            }
154
155 5
            $this->verbosityLevel = \constant($verbosity);
156
        }
157
158 11
        return $this->verbosityLevel;
159
    }
160
161 6
    public function setVerbosityLevel($level): void
162
    {
163 6
        $this->verbosityLevel = $level;
164 6
    }
165
166 1
    protected function setInputs(array $inputs): void
167
    {
168 1
        $this->inputs = $inputs;
169 1
    }
170
171 11
    protected function getInputs(): ?array
172
    {
173 11
        return $this->inputs;
174
    }
175
176
    /**
177
     * Set verbosity for Symfony 3.4+.
178
     *
179
     * @see https://github.com/symfony/symfony/pull/24425
180
     *
181
     * @param $level
182
     */
183
    private function setVerbosityLevelEnv($level): void
184
    {
185
        putenv('SHELL_VERBOSITY='.$level);
186
    }
187
188
    /**
189
     * Retrieves the flag indicating if the output should be decorated or not.
190
     */
191 12
    protected function getDecorated(): bool
192
    {
193 12
        if (null === $this->decorated) {
194
            // Set the global decoration flag that is set to `true` by the TreeBuilder in Configuration
195 6
            $this->decorated = $this->getContainer()->getParameter('liip_functional_test.command_decoration');
196
        }
197
198
        // Check the local decorated flag
199 12
        if (false === \is_bool($this->decorated)) {
200
            throw new \OutOfBoundsException(sprintf('`WebTestCase::decorated` has to be `bool`. "%s" given.', \gettype($this->decorated)));
201
        }
202
203 12
        return $this->decorated;
204
    }
205
206 6
    public function isDecorated(bool $decorated): void
207
    {
208 6
        $this->decorated = $decorated;
209 6
    }
210
211
    /**
212
     * Get an instance of the dependency injection container.
213
     * (this creates a kernel *without* parameters).
214
     */
215 10
    private function getDependencyInjectionContainer(): ContainerInterface
216
    {
217 10
        $cacheKey = static::$env;
218 10
        if (empty($this->containers[$cacheKey])) {
219 10
            $kernel = static::createKernel([
220 10
                'environment' => static::$env,
221
            ]);
222 10
            $kernel->boot();
223
224 10
            $container = $kernel->getContainer();
225 10
            if ($container->has('test.service_container')) {
226 10
                $this->containers[$cacheKey] = $container->get('test.service_container');
227
            } else {
228
                $this->containers[$cacheKey] = $container;
229
            }
230
        }
231
232 10
        return $this->containers[$cacheKey];
233
    }
234
235 26
    protected static function createKernel(array $options = []): KernelInterface
236
    {
237 26
        if (!isset($options['environment'])) {
238
            $options['environment'] = static::$env;
239
        }
240
241 26
        return parent::createKernel($options);
242
    }
243
244
    /**
245
     * Keep support of Symfony < 5.3.
246
     */
247 10
    public function __call(string $name, $arguments)
248
    {
249 10
        if ('getContainer' === $name) {
250 10
            return $this->getDependencyInjectionContainer();
251
        }
252
253
        throw new \Exception("Method {$name} is not supported.");
254
    }
255
256
    /**
257
     * @deprecated
258
     */
259 1
    public function __set($name, $value): void
260
    {
261 1
        if ('environment' !== $name) {
262
            throw new \Exception(sprintf('There is no property with name "%s"', $name));
263
        }
264
265 1
        @trigger_error('Setting "environment" property is deprecated, please use static::$env.', \E_USER_DEPRECATED);
266
267 1
        static::$env = $value;
268 1
    }
269
270
    /**
271
     * @deprecated
272
     */
273
    public function __isset($name)
274
    {
275
        if ('environment' !== $name) {
276
            throw new \Exception(sprintf('There is no property with name "%s"', $name));
277
        }
278
279
        @trigger_error('Checking "environment" property is deprecated, please use static::$env.', \E_USER_DEPRECATED);
280
281
        return true;
282
    }
283
284
    /**
285
     * @deprecated
286
     */
287
    public function __get($name)
288
    {
289
        if ('environment' !== $name) {
290
            throw new \Exception(sprintf('There is no property with name "%s"', $name));
291
        }
292
293
        @trigger_error('Getting "environment" property is deprecated, please use static::$env.', \E_USER_DEPRECATED);
294
295
        return static::$env;
296
    }
297
298
    /**
299
     * Creates an instance of a lightweight Http client.
300
     *
301
     * $params can be used to pass headers to the client, note that they have
302
     * to follow the naming format used in $_SERVER.
303
     * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With'
304
     *
305
     * @deprecated
306
     */
307 12
    protected function makeClient(array $params = []): Client
308
    {
309 12
        return $this->createClientWithParams($params);
0 ignored issues
show
Deprecated Code introduced by
The function Liip\FunctionalTestBundl...reateClientWithParams() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

309
        return /** @scrutinizer ignore-deprecated */ $this->createClientWithParams($params);
Loading history...
310
    }
311
312
    /**
313
     * Creates an instance of a lightweight Http client.
314
     *
315
     * $params can be used to pass headers to the client, note that they have
316
     * to follow the naming format used in $_SERVER.
317
     * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With'
318
     *
319
     * @deprecated
320
     */
321
    protected function makeAuthenticatedClient(array $params = []): Client
322
    {
323
        $username = $this->getContainer()
324
            ->getParameter('liip_functional_test.authentication.username');
325
        $password = $this->getContainer()
326
            ->getParameter('liip_functional_test.authentication.password');
327
328
        return $this->createClientWithParams($params, $username, $password);
0 ignored issues
show
Deprecated Code introduced by
The function Liip\FunctionalTestBundl...reateClientWithParams() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

328
        return /** @scrutinizer ignore-deprecated */ $this->createClientWithParams($params, $username, $password);
Loading history...
329
    }
330
331
    /**
332
     * Creates an instance of a lightweight Http client and log in user with
333
     * username and password params.
334
     *
335
     * $params can be used to pass headers to the client, note that they have
336
     * to follow the naming format used in $_SERVER.
337
     * Example: 'HTTP_X_REQUESTED_WITH' instead of 'X-Requested-With'
338
     *
339
     * @deprecated
340
     */
341
    protected function makeClientWithCredentials(string $username, string $password, array $params = []): Client
342
    {
343
        return $this->createClientWithParams($params, $username, $password);
0 ignored issues
show
Deprecated Code introduced by
The function Liip\FunctionalTestBundl...reateClientWithParams() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

343
        return /** @scrutinizer ignore-deprecated */ $this->createClientWithParams($params, $username, $password);
Loading history...
344
    }
345
346
    /**
347
     * Create User Token.
348
     *
349
     * Factory method for creating a User Token object for the firewall based on
350
     * the user object provided. By default it will be a Username/Password
351
     * Token based on the user's credentials, but may be overridden for custom
352
     * tokens in your applications.
353
     *
354
     * @param UserInterface $user         The user object to base the token off of
355
     * @param string        $firewallName name of the firewall provider to use
356
     *
357
     * @return TokenInterface The token to be used in the security context
358
     */
359
    protected function createUserToken(UserInterface $user, string $firewallName): TokenInterface
360
    {
361
        // Since Symfony 6.0, UsernamePasswordToken only has 3 arguments
362
        $usernamePasswordTokenClass = new \ReflectionClass(UsernamePasswordToken::class);
363
364
        if (3 === $usernamePasswordTokenClass->getConstructor()->getNumberOfParameters()) {
365
            return new UsernamePasswordToken(
366
                $user,
367
                $firewallName,
368
                $user->getRoles()
369
            );
370
        }
371
372
        return new UsernamePasswordToken(
373
            $user,
374
            null,
375
            $firewallName,
376
            $user->getRoles()
377
        );
378
    }
379
380
    /**
381
     * Extracts the location from the given route.
382
     *
383
     * @param string $route  The name of the route
384
     * @param array  $params Set of parameters
385
     */
386 1
    protected function getUrl(string $route, array $params = [], int $absolute = UrlGeneratorInterface::ABSOLUTE_PATH): string
387
    {
388 1
        return $this->getContainer()->get('router')->generate($route, $params, $absolute);
389
    }
390
391
    /**
392
     * Checks the success state of a response.
393
     *
394
     * @param Response $response Response object
395
     * @param bool     $success  to define whether the response is expected to be successful
396
     * @param string   $type
397
     */
398 1
    public function isSuccessful(Response $response, $success = true, $type = 'text/html'): void
399
    {
400 1
        HttpAssertions::isSuccessful($response, $success, $type);
401
    }
402
403
    /**
404
     * Executes a request on the given url and returns the response contents.
405
     *
406
     * This method also asserts the request was successful.
407
     *
408
     * @param string $path           path of the requested page
409
     * @param string $method         The HTTP method to use, defaults to GET
410
     * @param bool   $authentication Whether to use authentication, defaults to false
411
     * @param bool   $success        to define whether the response is expected to be successful
412
     */
413
    public function fetchContent(string $path, string $method = 'GET', bool $authentication = false, bool $success = true): string
414
    {
415
        $client = ($authentication) ? $this->makeAuthenticatedClient() : $this->makeClient();
0 ignored issues
show
Deprecated Code introduced by
The function Liip\FunctionalTestBundl...bTestCase::makeClient() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

415
        $client = ($authentication) ? $this->makeAuthenticatedClient() : /** @scrutinizer ignore-deprecated */ $this->makeClient();
Loading history...
Deprecated Code introduced by
The function Liip\FunctionalTestBundl...keAuthenticatedClient() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

415
        $client = ($authentication) ? /** @scrutinizer ignore-deprecated */ $this->makeAuthenticatedClient() : $this->makeClient();
Loading history...
416
417
        $client->request($method, $path);
418
419
        $content = $client->getResponse()->getContent();
420
        $this->isSuccessful($client->getResponse(), $success);
421
422
        return $content;
423
    }
424
425
    /**
426
     * Executes a request on the given url and returns a Crawler object.
427
     *
428
     * This method also asserts the request was successful.
429
     *
430
     * @param string $path           path of the requested page
431
     * @param string $method         The HTTP method to use, defaults to GET
432
     * @param bool   $authentication Whether to use authentication, defaults to false
433
     * @param bool   $success        Whether the response is expected to be successful
434
     */
435
    public function fetchCrawler(string $path, string $method = 'GET', bool $authentication = false, bool $success = true): Crawler
436
    {
437
        $client = ($authentication) ? $this->makeAuthenticatedClient() : $this->makeClient();
0 ignored issues
show
Deprecated Code introduced by
The function Liip\FunctionalTestBundl...keAuthenticatedClient() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

437
        $client = ($authentication) ? /** @scrutinizer ignore-deprecated */ $this->makeAuthenticatedClient() : $this->makeClient();
Loading history...
Deprecated Code introduced by
The function Liip\FunctionalTestBundl...bTestCase::makeClient() has been deprecated. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

437
        $client = ($authentication) ? $this->makeAuthenticatedClient() : /** @scrutinizer ignore-deprecated */ $this->makeClient();
Loading history...
438
439
        $crawler = $client->request($method, $path);
440
441
        $this->isSuccessful($client->getResponse(), $success);
442
443
        return $crawler;
444
    }
445
446
    /**
447
     * @deprecated
448
     *
449
     * @return WebTestCase
450
     */
451
    public function loginAs(UserInterface $user, string $firewallName): self
452
    {
453
        @trigger_error(sprintf('"%s()" is deprecated, use loginClient() after creating a client.', __METHOD__), \E_USER_DEPRECATED);
454
455
        $this->firewallLogins[$firewallName] = $user;
456
457
        return $this;
458
    }
459
460
    /**
461
     * @deprecated
462
     */
463
    public function loginClient(KernelBrowser $client, UserInterface $user, string $firewallName): void
464
    {
465
        // Available since Symfony 5.1
466
        if (method_exists($client, 'loginUser')) {
467
            @trigger_error(
468
                sprintf(
469
                    '"%s()" is deprecated, use loginUser() from Symfony 5.1+ instead %s',
470
                    __METHOD__,
471
                    'https://symfony.com/doc/5.4/testing.html#logging-in-users-authentication'
472
                ),
473
                \E_USER_DEPRECATED
474
            );
475
476
            $client->loginUser($user);
477
478
            return;
479
        }
480
481
        // has to be set otherwise "hasPreviousSession" in Request returns false.
482
        $options = $client->getContainer()->getParameter('session.storage.options');
483
484
        if (!$options || !isset($options['name'])) {
485
            throw new \InvalidArgumentException('Missing session.storage.options#name');
486
        }
487
488
        $session = $this->getSession($client);
489
490
        $client->getCookieJar()->set(new Cookie($options['name'], $session->getId()));
491
492
        $token = $this->createUserToken($user, $firewallName);
493
494
        $tokenStorage = $client->getContainer()->get('security.token_storage');
495
496
        $tokenStorage->setToken($token);
497
        $session->set('_security_'.$firewallName, serialize($token));
498
499
        $session->save();
500
    }
501
502
    /**
503
     * Asserts that the HTTP response code of the last request performed by
504
     * $client matches the expected code. If not, raises an error with more
505
     * information.
506
     */
507
    public static function assertStatusCode(int $expectedStatusCode, Client $client): void
508
    {
509
        HttpAssertions::assertStatusCode($expectedStatusCode, $client);
510
    }
511
512
    /**
513
     * Assert that the last validation errors within $container match the
514
     * expected keys.
515
     *
516
     * @param array $expected A flat array of field names
517
     */
518 1
    public static function assertValidationErrors(array $expected, ContainerInterface $container): void
519
    {
520 1
        HttpAssertions::assertValidationErrors($expected, $container);
521
    }
522
523 27
    protected function tearDown(): void
524
    {
525 27
        if (null !== $this->containers) {
526 10
            foreach ($this->containers as $container) {
527 10
                if ($container instanceof ResettableContainerInterface) {
528 10
                    $container->reset();
529
                }
530
            }
531
        }
532
533 27
        $this->containers = null;
534
535 27
        parent::tearDown();
536 27
    }
537
538
    /**
539
     * @deprecated
540
     */
541 12
    protected function createClientWithParams(array $params, ?string $username = null, ?string $password = null): Client
542
    {
543 12
        if ($username && $password) {
544
            $params = array_merge($params, [
545
                'PHP_AUTH_USER' => $username,
546
                'PHP_AUTH_PW' => $password,
547
            ]);
548
        }
549
550 12
        if (static::$booted) {
551
            static::ensureKernelShutdown();
552
        }
553
554 12
        $client = static::createClient(['environment' => static::$env], $params);
555
556 12
        if ($this->firewallLogins) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->firewallLogins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
557
            // has to be set otherwise "hasPreviousSession" in Request returns false.
558
            $options = $client->getContainer()->getParameter('session.storage.options');
559
560
            if (!$options || !isset($options['name'])) {
561
                throw new \InvalidArgumentException('Missing session.storage.options#name');
562
            }
563
564
            $session = $this->getSession($client);
565
566
            $client->getCookieJar()->set(new Cookie($options['name'], $session->getId()));
567
568
            /** @var $user UserInterface */
569
            foreach ($this->firewallLogins as $firewallName => $user) {
570
                $token = $this->createUserToken($user, $firewallName);
571
572
                // Available since Symfony 5.1
573
                if (method_exists($client, 'loginUser')) {
574
                    @trigger_error(
575
                        sprintf(
576
                            '"%s()" is deprecated, use loginUser() from Symfony 5.1+ instead %s',
577
                            __METHOD__,
578
                            'https://symfony.com/doc/5.4/testing.html#logging-in-users-authentication'
579
                        ),
580
                        \E_USER_DEPRECATED
581
                    );
582
583
                    $client->loginUser($user);
584
585
                    continue;
586
                }
587
588
                $tokenStorage = $client->getContainer()->get('security.token_storage');
589
590
                $tokenStorage->setToken($token);
591
                $session->set('_security_'.$firewallName, serialize($token));
592
            }
593
594
            $session->save();
595
        }
596
597 12
        return $client;
598
    }
599
600
    /**
601
     * Compatibility layer.
602
     */
603
    private function getSession(KernelBrowser $client): SessionInterface
604
    {
605
        $container = $client->getContainer();
606
607
        // Preferred since Symfony 5.4
608
        if ($container->has(RequestStack::class)) {
609
            /** @var RequestStack $requestStack */
610
            $requestStack = $container->get(RequestStack::class);
611
612
            // see https://github.com/symfony/symfony-docs/pull/14898/files#diff-29ab32502d95be802fed1001850b76c9624912cf4c299101d3ecaf17b0352562R37
613
            try {
614
                $session = $requestStack->getSession();
615
            } catch (SessionNotFoundException $e) {
616
                $requestStack->push(new Request());
617
                $session = new Session();
618
                $session->start();
619
            }
620
621
            return $session;
622
        }
623
624
        /** @var SessionInterface $session */
625
        $session = $container->get(SessionInterface::class);
626
627
        $session->start();
628
629
        return $session;
630
    }
631
}
632