All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- GitHub Spec Kit:
.specify/tooling, Cursor Agent skills (.cursor/skills/speckit-*), and baseline spec inspecs/001-baseline/(100%src/inventory). See SPEC-KIT.md and SPEC-DRIVEN-DEVELOPMENT.md. - CodeRabbit: review configuration (
.coderabbit.yaml) and GitHub Actions workflow for automated PR review.
- Documentation: SPEC-KIT.md (install, structure, Cursor usage); README link under Documentation.
- MysqlAes: PHPDoc
@deprecated since 2.1.0— not recommended for new production deployments (AES-128-ECB); prefer Halite or Defuse. No runtime change. - Twig
|decryptfilter: removedisSafe: ['html']so decrypted output is auto-escaped by Twig (security fix). Use|rawonly when the decrypted value is trusted HTML. - composer.lock: dependency sync for the bundle root and demo apps (
symfony7,symfony8).
No configuration changes required. See UPGRADING.md.
- Functional tests (CI): enable Doctrine ORM native lazy objects on PHP 8.4+ when running the test suite with Symfony 8.x. Symfony 8
var-exporterno longer ships LazyGhost helpers; fixes 14 functional test errors on the PHP 8.4 × Symfony 8.0 / 8.1 matrix cells.
No application or configuration changes. See UPGRADING.md.
- composer.lock:
platform.phpandcontent-hashsynced withcomposer.json(>=8.2); fixescomposer validate --strictin CI. - CI: Symfony 8.0 / 8.1 matrix cells use matching
symfony/var-exporter(^8.0/^8.1) instead of^7.4, which blocked dependency resolution.
No application changes required. See UPGRADING.md.
- GenerateSecretKeyCommandTest: assertions updated for MysqlAes in supported-encryptor messages (2.1.0 gap).
- Requirements (Nowo bundle specs): minimum PHP 8.2+;
symfony/*constraints ^7.0 || ^8.0 (Symfony 6 removed fromcomposer.json, aligned with 2.0 release notes). CI matrix tests Symfony 7.4, 8.0, and 8.1 on PHP 8.2–8.5. - Documentation: README badges and requirements, INSTALLATION.md, ROADMAP.md — PHP 8.2+ and Symfony 7.4 | 8.0 | 8.1+.
- Symfony 6 from declared
symfony/*Composer constraints (was still present incomposer.jsonafter 2.0).
Breaking: PHP 8.2+ is required; Symfony 6 is no longer installable with current constraints. See UPGRADING.md.
MysqlAesencryptor — AES-128-ECB compatible with MySQLAES_ENCRYPT()/AES_DECRYPT()(defaultblock_encryption_mode). Configure withencryptor_class: MysqlAesand a passphrase (env or key file). See MYSQL_AES.md.doctrine:encrypt:generate-secret-key— generates a passphrase forMysqlAesconfigs.- Documentation: MYSQL_AES.md (native SQL, LIKE demos, preset fixtures); PERFORMANCE.md (encryptors and query patterns compared).
- Demos (Symfony 7 & 8):
/mysql-aes-note— Doctrine + repository SQL paths, LIKE filters, preset fixtures, DB values view (encrypted vs decrypted). Secret messages:/secret-message/db-valueswith the same raw/decrypted tabs. Demo Docker: optional MySQL 8 service,pdo_mysql, Bootstrap 5 form theme.
- Documentation: CONFIGURATION.md, USAGE.md, DEMO.md, ROADMAP.md, README — MysqlAes, performance notes, demo troubleshooting (Composer advisories, Symfony 7.4).
- Demos: Symfony 7 demo targets 7.4.*;
composer.locksynced (e.g. twig-inspector-bundle); entrypoint avoids restart loop on failedcomposer install.
No breaking changes for existing Halite/Defuse configs. See UPGRADING.md.
- Symfony 7.1+:
DoctrineEncryptExtensionnow extendsSymfony\Component\DependencyInjection\Extension\Extensioninstead ofSymfony\Component\HttpKernel\DependencyInjection\Extension. The latter is considered internal since Symfony 7.1 and will be deprecated in 8.1; this change removes the deprecation notice.
- Documentation: COMMANDS.md — batchSize default for encrypt/decrypt database is documented as 5 (from config); link to CONFIGURATION added.
No upgrade steps required from 2.0.9. See UPGRADING.md.
- Code style: PHP-CS-Fixer configuration updated and applied across the codebase (src, tests, demos).
- Internal: Refactors in commands, encryptors, subscriber, mapping, Twig extensions and utilities; tests aligned with updated style and structure.
- Demos: Symfony 7 and 8 demo apps updated (config, controllers, entities, forms, fixtures, bootstrap); added
demo/Makefilefor running demos. - CI & tooling: GitHub Actions workflow, root and demo Makefiles, and docker-compose adjustments.
- PHP 8.1+:
DefuseEncryptorTest::testGenerateKeyandHaliteEncryptorTest::testGenerateKeyno longer pass an int tomd5(); usemd5((string) time())so the test suite passes with strict types.
No upgrade steps required from 2.0.8. See UPGRADING.md.
- Symfony Console 7.0 / 8.0:
RotateKeysCommandTestno longer usesApplication::add()(removed in Symfony Console 8.0). Tests now register commands viaFactoryCommandLoaderandsetCommandLoader(), and set the application on the rotate command withsetApplication()so subcommands resolve correctly. This fixes the 8 failing unit tests when running on PHP 8.4 with Symfony 7.0 (e.g. in CI).
No upgrade steps required from 2.0.7. See UPGRADING.md.
- doctrine:encrypt:status: For each entity, lists which properties are encrypted and their config (e.g.
email (config: personal_data)). At the end, shows configured encryptor configs (config name, encryptor class Halite/Defuse, and which is default). - doctrine:encrypt:rotate-keys: New command to run the full key rotation: optional backup of key files (
--backup), full decrypt, key change (generate new key files or prompt for .env update), then re-encrypt. Each step asks for confirmation unless--no-interactionis used. See COMMANDS.md and KEY_ROTATION.md. - doctrine:encrypt:generate-secret-key: Option
--forceto overwrite existing key file(s) without asking. - doctrine:decrypt:database and doctrine:encrypt:database: Option
--forceto skip the confirmation prompt (useful when calling from scripts or fromrotate-keyswith--no-interaction). - Configuration:
nowo_doctrine_encrypt.batch_size(default5) for the default batch size of encrypt/decrypt database commands; overridable per run via thebatchSizeargument. - Documentation: INSTALLATION.md — new section FrankenPHP (runtime and worker mode): compatibility with FrankenPHP in both modes, no extra config; recommendations (e.g. limit requests per worker). README and DEMO.md reference FrankenPHP compatibility; demo Caddyfiles include a short comment.
- doctrine:decrypt:database and doctrine:encrypt:database: Use raw SQL for encrypt/decrypt (no Doctrine lifecycle events); default batch size is 5 (configurable via
nowo_doctrine_encrypt.batch_size). - Documentation: COMMANDS.md documents status output (per-entity properties and configs, configured configs), rotate-keys command, and
--forceon generate-secret-key, decrypt and encrypt. KEY_ROTATION.md and SECURITY.md reference the new rotate-keys command. README and INSTALLATION next steps mention rotate keys. ROADMAP updated. DEMO.md has a FrankenPHP subsection; demo Caddyfiles reference INSTALLATION § FrankenPHP.
- RotateKeysCommandTest: Test for “no configs” now builds the command with
DoctrineEncryptSubscriber(null)andregistry = nullso that an empty registry is never passed to the subscriber (which would callgetDefault()and throw).
- Coverage: Unit tests added for
RotateKeysCommand(full rotation, backup with SQLite, backup-db-cmd, env configs, paused step, no configs), forAbstractCommand(getEncryptedTableInfo,getColumnNameFromMetadata,getRowValue), for decrypt/encrypt database commands (executeStatement path, row with null id, decrypt throw fallback), forDoctrineEncryptStatusCommand(registry null, configured configs, none), and forGenerateSecretKeyCommand(config argument with unsupported encryptor). Code coverage for lines is ≥ 95%.
No upgrade steps required from 2.0.6. See UPGRADING.md.
- Tests: Unit test for
HaliteEncryptorwhen the key path is a directory (coversnormalizeKeyFile()early return). Unit test forGenerateSecretKeyCommandwhen the key file is created in a non-existent directory (coverscreateKey()withmkdir(..., true)). Improves code coverage (lines ≥ 95%).
- PHPUnit: The unit test that uses a directory as key path no longer triggers a PHP Notice (Halite emits a notice when reading a path that is a directory); the test suppresses it with
@on the call that triggers it so the test suite runs without notices.
No upgrade steps required from 2.0.5. See UPGRADING.md.
- doctrine:encrypt:generate-secret-key: Optional argument
configto generate the key for a single config; when the key file already exists, the command asks for confirmation before overwriting. Without argument, the command now checks all configs (allsecret_directory_pathentries) and creates a missing key file for each Halite or Defuse config; configs that already have a key are skipped with a message. - Makefile: Targets
validate(runscomposer validate --strict) andupdate(runscomposer update);make qanow runsvalidatebefore cs-check and test.
- Test directory: The test directory has been renamed from
Teststotests(lowercase). PHP namespaces remainNowo\DoctrineEncryptBundle\Tests\; only the folder and references inphpunit.xml.dist,composer.json(autoload-dev), and.php-cs-fixer.dist.phpchanged. - symfony/var-exporter: Kept in
require(not only in require-dev) so that Doctrine ORM 3.x LazyGhost is available in all environments (CI and production when using lazy loading). This fixes functional test failures on PHP 8.4/8.5 with Symfony 8.0 ("Symfony LazyGhost is not available"). - CI: Install step pins
symfony/var-exporter:^7.4so Doctrine ORM accepts the installed version for LazyGhost; coverage job also requiressymfony/var-exporter. - Documentation: COMMANDS.md updated: encrypt/decrypt database commands use argument
config(config alias); generate-secret-key documents the new behaviour (all configs vs single config with confirmation).
No upgrade steps required from 2.0.4. See UPGRADING.md.
- PHPUnit: Unit tests for
EncryptUtilandDecryptExtension(Twig) no longer mock the final classEncryptorRegistry; they use realEncryptorRegistryinstances with mockedEncryptorInterfacewhere needed. This fixesClassIsFinalException: Class ... is declared "final" and cannot be doubledwhen runningmake test-coverageorcomposer test-coveragewith PHPUnit 10/11. - Symfony Console: Commands
doctrine:encrypt:databaseanddoctrine:decrypt:databasenow register arguments in an idempotent way:configandbatchSizeare only added when not already present. This avoidsLogicException: An argument with name "config" already existswhen the Console component builds the command definition more than once (e.g. with certain Symfony Console 7/8 setups or when tests callgetDefinition()).
No upgrade steps required from 2.0.3. See UPGRADING.md.
- paragonie/sodium_compat: Constraint relaxed from
^1.20to^1.20 || ^2.0so the bundle installs in projects that requiresodium_compat2.x (e.g. 2.5.0).
No upgrade steps required from 2.0.2. See UPGRADING.md.
- Symfony Console 8.0: Command unit tests (
DoctrineEncryptDatabaseCommandTest,DoctrineDecryptDatabaseCommandTest) now use$command->setApplication($application)instead of$application->add($command), sinceApplication::add()was removed in Symfony Console 8.0. - CI: Ensure
symfony/var-exporteris installed so Doctrine ORM 3.x LazyGhost works in functional tests; CI install step now runs a fullcomposer updateso the package is resolved and installed on all matrix jobs.
No upgrade steps required from 2.0.1. See UPGRADING.md.
- require-dev:
symfony/var-exporter^7.0 || ^8.0 for Doctrine ORM 3.x LazyGhost support in functional tests.
- Documentation: README, INSTALLATION, and ROADMAP now state explicitly that the bundle is compatible with Symfony 7 and 8; Requirements and ROADMAP focus updated to 2.x.
- Unit tests for
DefuseEncryptorandHaliteEncryptorno longer depend on fixture key files orsys_get_temp_dir(); they create key files under the testfixturesdirectory so CI (e.g. GitHub Actions) runs reliably.
No upgrade steps required from 2.0.0. See UPGRADING.md.
- Symfony 6 support. The bundle now requires Symfony ^7.0 || ^8.0. If you are on Symfony 6.x, stay on
nowo-tech/doctrine-encrypt-bundle^1.0 or upgrade your application to Symfony 7 or 8. - Demo for Symfony 6. The
demo/symfony6project has been removed. Demos for Symfony 7 and 8 remain indemo/symfony7anddemo/symfony8.
- Composer requirements: all
symfony/*constraints updated from^6.0 || ^7.0 || ^8.0to^7.0 || ^8.0. - CI (GitHub Actions) and documentation now target only Symfony 7.0 and 8.0.
For upgrade steps, see UPGRADING.md.
First release under nowo-tech. Fork from hec-franco/doctrine-encrypt-bundle with Halite and Defuse encryptors, Doctrine ORM 2.x/3.x, and attribute/annotation support.
- nowo-tech fork: Bundle in namespace
Nowo\DoctrineEncryptBundleand packagenowo-tech/doctrine-encrypt-bundle. - Support for Symfony 7 and 8; PHP 8.1+.
- Multiple encryptors: Config key
configs(e.g.personal_data,financial_data) withdefault_config; each config has its own encryptor and key file (.{Encryptor}.{alias}.key, e.g..Halite.personal_data.key). - Attribute
#[Encrypted('alias')]to assign a field to a named config; use"default"for the default config. - Documentation in
docs/(CHANGELOG, UPGRADING, RELEASE, CONFIGURATION, INSTALLATION, USAGE, COMMANDS, DEMO, custom_encryptor, etc.). - Makefile and Docker setup for development (
test,cs-check,cs-fix,qa). - Demo applications for Symfony 7 and 8 (entity
SensitiveRecord, CRUD, fixtures, Halite + Defuse). - Symfony Flex recipe (config with
configsonly); demo apps excluded from the distributed package (archive.exclude,export-ignore).
- Configuration alias:
ambta_doctrine_encrypt→nowo_doctrine_encrypt. See UPGRADING.md when migrating from the old bundle. - Bundle class:
AmbtaDoctrineEncryptBundle→NowoDoctrineEncryptBundle; registerNowo\DoctrineEncryptBundle\NowoDoctrineEncryptBundle::classinconfig/bundles.php. - Single-encryptor config (legacy) still supported; recipe and docs recommend
configsfor new setups.
- (None in this release.)
For upgrade steps from ambta/doctrine-encrypt-bundle or hec-franco/doctrine-encrypt-bundle, see UPGRADING.md.