Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Add first-class PHP configuration format - #6946

#6946
Open
TavoNiievez wants to merge 1 commit into
mainCodeception/Codeception:mainfrom
php_configCodeception/Codeception:php_configCopy head branch name to clipboard
Open

Add first-class PHP configuration format#6946
TavoNiievez wants to merge 1 commit into
mainCodeception/Codeception:mainfrom
php_configCodeception/Codeception:php_configCopy head branch name to clipboard

Conversation

@TavoNiievez

Copy link
Copy Markdown
Member

Summary

Adds a PHP configuration format as a first-class alternative to YAML, designed to become the canonical way to configure Codeception in future versions. A codeception.php (or *.suite.php / env file) returns a fluent builder — or a raw array — instead of YAML. This unlocks IDE autocomplete, PHPStan-checkable config, ::class constants, native getenv(), and real PHP expressions (E_ALL & ~E_DEPRECATED), while keeping a 1:1 mapping to every existing YAML key. YAML is untouched and remains the default — the PHP format is fully opt-in.

<?php
use Codeception\Config\GlobalConfig;
use Codeception\Config\SuiteConfig;

return GlobalConfig::create()
    ->namespace('App\Tests')
    ->paths(tests: 'tests', output: 'tests/_output', data: 'tests/_data', support: 'tests/Support')
    ->settings(colors: true, memoryLimit: '1024M')
    ->suite('Functional', SuiteConfig::create()
        ->actor('FunctionalTester')
        ->module('Asserts')
        ->module('Db', ['dsn' => getenv('DB_DSN')]));

What's new

Builder API (src/Codeception/Config/, namespace Codeception\Config\):

  • GlobalConfig, SuiteConfig (both final), ConfigInterface (forever-contract = toArray() only), AbstractConfigBuilder (@internal), Params.
  • Uniform pattern: singular appenders (module(), moduleConfig(), extension(), suite(), env(), commands()) + one raw escape hatch merge(). settings() uses explicit named nullable params (camelCase → snake_case) for autocomplete.

Loader (Configuration.php): per role (global / suite / env), if any .php variant exists, PHP wins and YAML is ignored — one rule everywhere. Config files are loaded exactly once, inside an isolated closure wrapped in output buffering (stray echo can't corrupt --xml/--json output), with concrete-type guards (a SuiteConfig returned in global position throws a clear error naming the file and expected type). Cross-format extends works both directions.

Params: %param% stays YAML-only; PHP configs use getenv() in the global file and Params::get() in suite/env files (with a self-explaining exception if Params::get() is called too early during global load).

Tooling & scaffolding:

  • config:validate now reports the loaded config file path.
  • config:to-php — new command migrating existing YAML projects to builder-style PHP (--dry-run prints without writing; renderer Lib/Generator/PhpConfigFile.php).
  • bootstrap, init, g:suite, g:env gain PHP output; g:suite/g:env detect the project format and no longer risk generating a silently-shadowed file when the other extension already exists.

Design notes

  • New Codeception\Config\ namespace deliberately avoids colliding with the existing Codeception\Configuration class.
  • A reflection drift-guard test fails if any core $defaultConfig / $defaultSuiteSettings key gains no builder method — the canonical API can't silently lag core.
  • Mutable builders (matching Rector / PHP-CS-Fixer); merge() renamed from with() since with* implies immutable clone in PHP.
  • Considered and rejected: closure-style config, attributes, immutable builders, a separate lib-config package, var_export array generation. (Rationale available on request.)

Backward compatibility

Non-breaking and additive. YAML behavior is unchanged; YAML remains the default scaffold. Flipping the default to PHP, and any future YAML deprecation, is left as a maintainer decision — not smuggled in here.

Tests

  • 30 unit tests (tests/unit/Codeception/Config/): every builder method → expected key/value, snake_case mapping, guards, merge() order, drift-guards, Params::get() timing.
  • 13 cli tests (tests/cli/ConfigPhpFormatCest.php): discovery, php-wins, env-wins, cross-format extends, type/echo/invalid-return errors, inline-suite shadowing, config:to-php migrate → build → run.
  • Verified against a CI-faithful Linux run (build + web server + run cli,unit,coverage), plus phpstan analyse src and composer cs-tests clean.

Unrelated changes (disclosed)

This commit also bumps CI actions: actions/checkout@v6 → v7 and actions/cache@v3 → v6 in .github/workflows/build.yml. Not history-rewritten; called out here for review.

This comment was marked as resolved.

@TavoNiievez
TavoNiievez force-pushed the php_config branch 2 times, most recently from 8ddfeaf to 17c7e25 Compare July 7, 2026 23:42
- Introduced support for PHP configuration files alongside YAML.
- Updated Api, Unit, and Bootstrap templates to generate PHP config files.
- Added methods to handle PHP config generation and validation.
- Enhanced test cases to validate the new PHP configuration format.
- Migrated existing YAML configurations to PHP format in tests.
- Improved error handling and output for configuration validation.
- Added new test cases to ensure compatibility and correctness of the new configuration system.
@TavoNiievez

Copy link
Copy Markdown
Member Author

I added this example PR to demonstrate the functionality and the new API: Codeception/symfony-module-tests#66

@TavoNiievez
TavoNiievez requested review from SamMousa and W0rma July 8, 2026 02:20
@TavoNiievez

Copy link
Copy Markdown
Member Author

cc. @ThomasLandauer , @burned42 & @samsonasik

@ThomasLandauer

Copy link
Copy Markdown
Member

Moving away from YAML is an excellent idea IMO!

Symfony just moved away from PHP methods to PHP arrays (e.g. ['namespace' => 'App\Tests'] instead of ->namespace('App\Tests')). I'm sure you know that. Do you know the reason why?

@ThomasLandauer

Copy link
Copy Markdown
Member

Detail while looking at Codeception/symfony-module-tests#66 (comment)
Would it be possible to pass the modules as FQCN too? So something like ->module(Asserts::class) instead of ->module('Asserts')?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Morty Proxy This is a proxified and sanitized view of the page, visit original site.