Skip to content

Commit 671350c

Browse files
committed
Performance Optimizations and Code Audit
1 parent 6cfa3e4 commit 671350c

24 files changed

+754
-781
lines changed

.github/workflows/main.yml

Lines changed: 33 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -6,132 +6,54 @@ jobs:
66
runs-on: ubuntu-latest
77
strategy:
88
matrix:
9-
php: [8.2, 8.3, 8.4, 8.5]
10-
symfony: ["5.4.*", "6.4.*", "6.4wApi", "7.3.*", "7.4.*"]
11-
env:
12-
only_sf_latest: &only_sf_latest ${{ matrix.symfony == '7.4.*' }}
9+
php: ["8.2", "8.5"]
10+
symfony: ["5.4", "6.4", "7.4", "8.0"]
11+
exclude:
12+
- php: "8.2"
13+
symfony: "8.0"
1314

1415
steps:
15-
- name: Checkout code
16-
uses: actions/checkout@v6
16+
- uses: actions/checkout@v6
1717

18-
- name: Setup PHP != 8.5
19-
if: ${{ matrix.php != '8.5' }}
20-
uses: shivammathur/setup-php@v2
18+
- uses: shivammathur/setup-php@v2
2119
with:
2220
php-version: ${{ matrix.php }}
23-
tools: composer:v2
24-
extensions: ctype, iconv, intl, json, mbstring, pdo, pdo_sqlite
25-
coverage: none
21+
coverage: pcov
2622

27-
- name: Setup PHP 8.5
28-
if: ${{ matrix.php == '8.5' }}
29-
uses: shivammathur/setup-php@v2
23+
- uses: actions/cache@v5
3024
with:
31-
php-version: ${{ matrix.php }}
32-
tools: composer:v2
33-
extensions: ctype, iconv, intl, json, mbstring, pdo, pdo_sqlite
34-
coverage: none
35-
# this ini directive seems to be off by default in PHP 8.5
36-
# see https://github.com/php/php-src/issues/20279
37-
# enable it because codeception relies on it.
38-
ini-values: register_argc_argv=1
39-
40-
- name: Set Symfony version reference
41-
env:
42-
MATRIX_SYMFONY: ${{ matrix.symfony }}
43-
run: |
44-
if [[ "$MATRIX_SYMFONY" == *'*' ]]; then
45-
echo "SF_REF=${MATRIX_SYMFONY%.*}" >> "$GITHUB_ENV"
46-
else
47-
echo "SF_REF=$MATRIX_SYMFONY" >> "$GITHUB_ENV"
48-
fi
25+
path: ~/.cache/composer
26+
key: php-${{ matrix.php }}-sf-${{ matrix.symfony }}-${{ hashFiles('**/composer.json') }}
27+
restore-keys: php-${{ matrix.php }}-sf-${{ matrix.symfony }}-
4928

50-
- name: Set Composer Symfony constraint
51-
env:
52-
MATRIX_SYMFONY: ${{ matrix.symfony }}
53-
run: |
54-
if [[ "$MATRIX_SYMFONY" == "6.4wApi" ]]; then
55-
echo "COMP_SYMFONY=6.4.*" >> "$GITHUB_ENV"
56-
else
57-
echo "COMP_SYMFONY=$MATRIX_SYMFONY" >> "$GITHUB_ENV"
58-
fi
59-
60-
- name: Checkout Symfony ${{ env.SF_REF }} sample
61-
uses: actions/checkout@v4
29+
- uses: actions/checkout@v6
30+
name: Checkout Symfony ${{ matrix.symfony }} app
6231
with:
6332
repository: Codeception/symfony-module-tests
6433
path: framework-tests
65-
ref: ${{ env.SF_REF }}
66-
67-
- name: Get composer cache directory
68-
id: composer-cache
69-
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
70-
71-
- name: Cache Composer dependencies
72-
uses: actions/cache@v3
73-
with:
74-
path: ${{ steps.composer-cache.outputs.dir }}
75-
key: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ hashFiles('**/composer.{json,lock}') }}
76-
restore-keys: ${{ runner.os }}-php-${{ matrix.php }}-composer-
77-
78-
- name: Install PHPUnit 10
79-
run: composer require --dev --no-update phpunit/phpunit:^10.0
34+
ref: ${{ matrix.symfony }}
8035

8136
- name: Install dependencies
82-
env:
83-
MATRIX_SYMFONY: ${{ matrix.symfony }}
8437
run: |
85-
composer require --no-update \
86-
symfony/{finder,yaml,console,event-dispatcher,css-selector,dom-crawler,browser-kit}:${{ env.COMP_SYMFONY }} \
87-
vlucas/phpdotenv \
88-
codeception/module-asserts:"3.*" \
89-
codeception/module-doctrine:"3.*"
90-
91-
if [[ "$MATRIX_SYMFONY" == "6.4wApi" ]]; then
92-
composer require codeception/module-rest="3.*" --no-update
93-
fi
94-
95-
composer update --prefer-dist --no-progress
38+
sed -i "s/\^5.4 | \^6.4 | \^7.4 | \^8.0/${{ matrix.symfony }}.*/g" composer.json
39+
composer install --prefer-dist --no-progress
9640
97-
- name: Run PHPStan (max)
98-
if: *only_sf_latest
99-
run: composer phpstan
41+
- name: Quality checks
42+
if: matrix.symfony == '8.0'
43+
run: composer validate --strict && composer audit && composer cs-check && composer phpstan
10044

101-
- name: Run PHP-CS-Fixer
102-
if: *only_sf_latest
103-
run: composer cs-check
104-
105-
- name: Run Composer Audit
106-
if: *only_sf_latest
107-
run: composer audit
108-
109-
- name: Validate Composer files
110-
run: composer validate --strict
111-
working-directory: framework-tests
112-
113-
- name: Install PHPUnit in framework-tests
114-
run: composer require --dev --no-update phpunit/phpunit:^10.0
115-
working-directory: framework-tests
116-
117-
- name: Prepare Symfony sample
118-
run: |
119-
composer remove codeception/codeception codeception/module-asserts codeception/module-doctrine codeception/lib-innerbrowser codeception/module-symfony --dev --no-update
120-
composer update --no-progress
121-
working-directory: framework-tests
122-
123-
- name: Setup Database
45+
- name: Prepare Symfony app & run tests
12446
run: |
125-
php bin/console doctrine:schema:update --force
126-
php bin/console doctrine:fixtures:load --quiet
127-
working-directory: framework-tests
128-
129-
- name: Generate JWT keypair
130-
if: ${{ matrix.symfony == '6.4wApi' }}
131-
run: php bin/console lexik:jwt:generate-keypair --skip-if-exists
132-
working-directory: framework-tests
133-
134-
- name: Run tests
135-
run: |
136-
php vendor/bin/codecept build -c framework-tests
47+
composer -d framework-tests remove codeception/module-symfony --dev --no-update
48+
composer -d framework-tests install --no-progress
49+
php framework-tests/bin/console doctrine:schema:update --force
50+
php framework-tests/bin/console doctrine:fixtures:load --quiet
13751
php vendor/bin/codecept run Functional -c framework-tests
52+
53+
if [ "${{ matrix.symfony }}" = "7.4" ]; then
54+
composer require codeception/module-rest --dev
55+
git -C framework-tests apply resetFormatsAfterRequest_issue_test.patch
56+
composer -d framework-tests install --no-progress
57+
php framework-tests/bin/console lexik:jwt:generate-keypair --skip-if-exists
58+
php vendor/bin/codecept run Functional -c framework-tests
59+
fi

.php-cs-fixer.dist.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use PhpCsFixer\Config;
6+
use PhpCsFixer\Finder;
7+
use PhpCsFixer\Runner\Parallel\ParallelConfigFactory;
8+
9+
return (new Config())
10+
->setParallelConfig(ParallelConfigFactory::detect())
11+
->setRiskyAllowed(true)
12+
->setRules([
13+
'@PER-CS' => true,
14+
'@PHP82Migration' => true,
15+
'array_syntax' => ['syntax' => 'short'],
16+
'strict_param' => true,
17+
'declare_strict_types' => true,
18+
])
19+
->setFinder(
20+
(new Finder())
21+
->in(__DIR__)
22+
->exclude(['var', 'vendor'])
23+
->ignoreDotFiles(true)
24+
->ignoreVCS(true)
25+
)
26+
;

composer.json

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,47 +24,58 @@
2424
"require": {
2525
"php": "^8.2",
2626
"ext-json": "*",
27-
"codeception/codeception": "^5.3",
28-
"codeception/lib-innerbrowser": "^3.1 | ^4.0"
27+
"codeception/lib-innerbrowser": "^3.1 | ^4.1"
2928
},
3029
"require-dev": {
31-
"codeception/module-asserts": "^3.0",
32-
"codeception/module-doctrine": "^3.1",
33-
"doctrine/orm": "^3.5",
34-
"friendsofphp/php-cs-fixer": "^3.85",
30+
"codeception/module-asserts": "^3.3",
31+
"codeception/module-doctrine": "^3.3",
32+
"doctrine/orm": "^3.6",
33+
"friendsofphp/php-cs-fixer": "^3.94",
3534
"phpstan/phpstan": "^2.1",
36-
"phpunit/phpunit": "^10.0",
37-
"symfony/browser-kit": "^5.4 | ^6.4 | ^7.3 | ^8.0",
38-
"symfony/cache": "^5.4 | ^6.4 | ^7.3 | ^8.0",
39-
"symfony/config": "^5.4 | ^6.4 | ^7.3 | ^8.0",
40-
"symfony/dependency-injection": "^5.4 | ^6.4 | ^7.3 | ^8.0",
41-
"symfony/dom-crawler": "^5.4 | ^6.4 | ^7.3 | ^8.0",
42-
"symfony/dotenv": "^5.4 | ^6.4 | ^7.3 | ^8.0",
43-
"symfony/error-handler": "^5.4 | ^6.4 | ^7.3 | ^8.0",
44-
"symfony/filesystem": "^5.4 | ^6.4 | ^7.3 | ^8.0",
45-
"symfony/form": "^5.4 | ^6.4 | ^7.3 | ^8.0",
46-
"symfony/framework-bundle": "^5.4 | ^6.4 | ^7.3 | ^8.0",
47-
"symfony/http-client": "^5.4 | ^6.4 | ^7.3 | ^8.0",
48-
"symfony/http-foundation": "^5.4 | ^6.4 | ^7.3 | ^8.0",
49-
"symfony/http-kernel": "^5.4 | ^6.4 | ^7.3 | ^8.0",
50-
"symfony/mailer": "^5.4 | ^6.4 | ^7.3 | ^8.0",
51-
"symfony/mime": "^5.4 | ^6.4 | ^7.3 | ^8.0",
52-
"symfony/notifier": "^5.4 | ^6.4 | ^7.3 | ^8.0",
53-
"symfony/options-resolver": "^5.4 | ^6.4 | ^7.3 | ^8.0",
54-
"symfony/property-access": "^5.4 | ^6.4 | ^7.3 | ^8.0",
55-
"symfony/property-info": "^5.4 | ^6.4 | ^7.3 | ^8.0",
56-
"symfony/routing": "^5.4 | ^6.4 | ^7.3 | ^8.0",
57-
"symfony/security-bundle": "^5.4 | ^6.4 | ^7.3 | ^8.0",
58-
"symfony/security-core": "^5.4 | ^6.4 | ^7.3 | ^8.0",
59-
"symfony/security-csrf": "^5.4 | ^6.4 | ^7.3 | ^8.0",
60-
"symfony/security-http": "^5.4 | ^6.4 | ^7.3 | ^8.0",
61-
"symfony/translation": "^5.4 | ^6.4 | ^7.3 | ^8.0",
62-
"symfony/twig-bundle": "^5.4 | ^6.4 | ^7.3 | ^8.0",
63-
"symfony/validator": "^5.4 | ^6.4 | ^7.3 | ^8.0",
64-
"symfony/var-exporter": "^5.4 | ^6.4 | ^7.3 | ^8.0",
65-
"vlucas/phpdotenv": "^4.2 | ^5.4"
35+
"phpunit/phpunit": "^11.0 | ^12.0",
36+
"symfony/browser-kit": "^5.4 | ^6.4 | ^7.4 | ^8.0",
37+
"symfony/cache": "^5.4 | ^6.4 | ^7.4 | ^8.0",
38+
"symfony/clock": "^6.4 | ^7.4 | ^8.0",
39+
"symfony/config": "^5.4 | ^6.4 | ^7.4 | ^8.0",
40+
"symfony/console": "^5.4 | ^6.4 | ^7.4 | ^8.0",
41+
"symfony/css-selector": "^5.4 | ^6.4 | ^7.4 | ^8.0",
42+
"symfony/dependency-injection": "^5.4 | ^6.4 | ^7.4 | ^8.0",
43+
"symfony/dom-crawler": "^5.4 | ^6.4 | ^7.4 | ^8.0",
44+
"symfony/dotenv": "^5.4 | ^6.4 | ^7.4 | ^8.0",
45+
"symfony/error-handler": "^5.4 | ^6.4 | ^7.4 | ^8.0",
46+
"symfony/event-dispatcher": "^5.4 | ^6.4 | ^7.4 | ^8.0",
47+
"symfony/filesystem": "^5.4 | ^6.4 | ^7.4 | ^8.0",
48+
"symfony/finder": "^5.4 | ^6.4 | ^7.4 | ^8.0",
49+
"symfony/form": "^5.4 | ^6.4 | ^7.4 | ^8.0",
50+
"symfony/framework-bundle": "^5.4 | ^6.4 | ^7.4 | ^8.0",
51+
"symfony/http-client": "^5.4 | ^6.4 | ^7.4 | ^8.0",
52+
"symfony/http-foundation": "^5.4 | ^6.4 | ^7.4 | ^8.0",
53+
"symfony/http-kernel": "^5.4 | ^6.4 | ^7.4 | ^8.0",
54+
"symfony/mailer": "^5.4 | ^6.4 | ^7.4 | ^8.0",
55+
"symfony/mime": "^5.4 | ^6.4 | ^7.4 | ^8.0",
56+
"symfony/notifier": "^5.4 | ^6.4 | ^7.4 | ^8.0",
57+
"symfony/options-resolver": "^5.4 | ^6.4 | ^7.4 | ^8.0",
58+
"symfony/password-hasher": "^5.4 | ^6.4 | ^7.4 | ^8.0",
59+
"symfony/process": "^5.4 | ^6.4 | ^7.4 | ^8.0",
60+
"symfony/property-access": "^5.4 | ^6.4 | ^7.4 | ^8.0",
61+
"symfony/property-info": "^5.4 | ^6.4 | ^7.4 | ^8.0",
62+
"symfony/routing": "^5.4 | ^6.4 | ^7.4 | ^8.0",
63+
"symfony/security-bundle": "^5.4 | ^6.4 | ^7.4 | ^8.0",
64+
"symfony/security-core": "^5.4 | ^6.4 | ^7.4 | ^8.0",
65+
"symfony/security-csrf": "^5.4 | ^6.4 | ^7.4 | ^8.0",
66+
"symfony/security-http": "^5.4 | ^6.4 | ^7.4 | ^8.0",
67+
"symfony/stopwatch": "^5.4 | ^6.4 | ^7.4 | ^8.0",
68+
"symfony/string": "^5.4 | ^6.4 | ^7.4 | ^8.0",
69+
"symfony/translation": "^5.4 | ^6.4 | ^7.4 | ^8.0",
70+
"symfony/twig-bundle": "^5.4 | ^6.4 | ^7.4 | ^8.0",
71+
"symfony/validator": "^5.4 | ^6.4 | ^7.4 | ^8.0",
72+
"symfony/var-dumper": "^5.4 | ^6.4 | ^7.4 | ^8.0",
73+
"symfony/var-exporter": "^5.4 | ^6.4 | ^7.4",
74+
"symfony/yaml": "^5.4 | ^6.4 | ^7.4 | ^8.0",
75+
"vlucas/phpdotenv": "^4.2 | ^5.6"
6676
},
6777
"suggest": {
78+
"codeception/codeception": "Install Codeception to run tests using this module",
6879
"codeception/module-asserts": "Include traditional PHPUnit assertions in your tests",
6980
"symfony/web-profiler-bundle": "Tool that gives information about the execution of requests"
7081
},

src/Codeception/Lib/Connector/Symfony.php

Lines changed: 18 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@
55
namespace Codeception\Lib\Connector;
66

77
use InvalidArgumentException;
8-
use LogicException;
9-
use ReflectionMethod;
10-
use ReflectionProperty;
11-
use Symfony\Bundle\FrameworkBundle\Test\TestContainer;
128
use Symfony\Component\DependencyInjection\ContainerInterface;
139
use Symfony\Component\HttpFoundation\Response;
1410
use Symfony\Component\HttpKernel\HttpKernelBrowser;
@@ -17,7 +13,7 @@
1713
use Symfony\Component\HttpKernel\KernelInterface;
1814
use Symfony\Component\HttpKernel\Profiler\Profiler;
1915

20-
use function codecept_debug;
16+
use function function_exists;
2117

2218
/**
2319
* @property KernelInterface $kernel
@@ -57,23 +53,28 @@ protected function doRequest(object $request): Response
5753
*/
5854
public function rebootKernel(): void
5955
{
60-
foreach (array_keys($this->persistentServices) as $service) {
61-
if ($this->container->has($service)) {
62-
$this->persistentServices[$service] = $this->container->get($service);
56+
foreach ($this->persistentServices as $name => $_) {
57+
if ($this->container->has($name)) {
58+
$this->persistentServices[$name] = $this->container->get($name);
6359
}
6460
}
6561

6662
$this->persistDoctrineConnections();
63+
6764
if ($this->kernel instanceof Kernel) {
6865
$this->ensureKernelShutdown();
6966
$this->kernel->boot();
7067
}
68+
7169
$this->container = $this->resolveContainer();
70+
7271
foreach ($this->persistentServices as $name => $service) {
7372
try {
7473
$this->container->set($name, $service);
7574
} catch (InvalidArgumentException $e) {
76-
codecept_debug("[Symfony] Can't set persistent service {$name}: {$e->getMessage()}");
75+
if (function_exists('codecept_debug')) {
76+
codecept_debug("[Symfony] Can't set persistent service {$name}: {$e->getMessage()}");
77+
}
7778
}
7879
}
7980

@@ -90,15 +91,10 @@ private function resolveContainer(): ContainerInterface
9091
{
9192
$container = $this->kernel->getContainer();
9293

93-
if ($container->has('test.service_container')) {
94-
$testContainer = $container->get('test.service_container');
95-
if (!$testContainer instanceof ContainerInterface) {
96-
throw new LogicException('Service "test.service_container" must implement ' . ContainerInterface::class);
97-
}
98-
$container = $testContainer;
99-
}
94+
/** @var ContainerInterface $testContainer */
95+
$testContainer = $container->has('test.service_container') ? $container->get('test.service_container') : $container;
10096

101-
return $container;
97+
return $testContainer;
10298
}
10399

104100
private function getProfiler(): ?Profiler
@@ -114,32 +110,10 @@ private function getProfiler(): ?Profiler
114110

115111
private function persistDoctrineConnections(): void
116112
{
117-
if (!$this->container->hasParameter('doctrine.connections')) {
118-
return;
119-
}
120-
121-
if ($this->container instanceof TestContainer) {
122-
$method = new ReflectionMethod($this->container, 'getPublicContainer');
123-
$publicContainer = $method->invoke($this->container);
124-
} else {
125-
$publicContainer = $this->container;
126-
}
127-
128-
if (!is_object($publicContainer) || !method_exists($publicContainer, 'getParameterBag')) {
129-
return;
130-
}
131-
132-
$target = property_exists($publicContainer, 'parameters')
133-
? $publicContainer
134-
: $publicContainer->getParameterBag();
135-
136-
if (!is_object($target) || !property_exists($target, 'parameters')) {
137-
return;
138-
}
139-
$prop = new ReflectionProperty($target, 'parameters');
140-
141-
$params = (array) $prop->getValue($target);
142-
unset($params['doctrine.connections']);
143-
$prop->setValue($target, $params);
113+
(function (): void {
114+
if (property_exists($this, 'parameters') && is_array($this->parameters)) {
115+
unset($this->parameters['doctrine.connections']);
116+
}
117+
})->call($this->kernel->getContainer());
144118
}
145119
}

0 commit comments

Comments
 (0)