diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..14846f83
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,10 @@
+# See https://php.watch/articles/composer-gitattributes
+/.github export-ignore
+/demo export-ignore
+/tests export-ignore
+
+/.gitignore export-ignore
+/.gitattributes export-ignore
+/.travis.yml export-ignore
+/phpstan.neon.dist export-ignore
+/phpunit.xml.dist export-ignore
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..ae5cfc73
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,113 @@
+# This file was heavily based on the ci-file from SymfonyCasts/verify-email-bundle
+#
+# See https://github.com/SymfonyCasts/verify-email-bundle
+# https://github.com/SymfonyCasts/verify-email-bundle/blob/main/.github/workflows/ci.yml
+
+name: CI
+on:
+ push:
+ branches: ['main','master']
+ pull_request:
+
+jobs:
+ static-analysis:
+ name: Static Analysis
+ runs-on: ubuntu-18.04
+
+ steps:
+ - name: "Checkout code"
+ uses: "actions/checkout@v2"
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "8.0"
+
+ - name: "Validate composer.json"
+ run: "composer validate --strict --no-check-lock"
+
+ - name: "Validate php-files"
+ run: "php -l src && php -l tests"
+
+ - name: "Composer install"
+ uses: "ramsey/composer-install@v2"
+ with:
+ composer-options: "--prefer-stable"
+ dependency-versions: 'highest'
+
+ - name: "PHPStan"
+ run: "vendor/bin/phpstan analyze"
+
+ - name: "PHPCompatibility"
+ run: "vendor/bin/phpcs src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 7.2-8.1"
+
+ tests:
+ name: "Tests ${{ matrix.php-version }} ${{ matrix.dependency-versions }}"
+ runs-on: ubuntu-18.04
+ needs: static-analysis
+
+ strategy:
+ fail-fast: false
+ matrix:
+ # normal, highest, non-dev installs
+ php-version: ['7.2', '7.3', '7.4', '8.0', '8.1']
+ composer-options: ['--prefer-stable']
+ dependency-versions: ['highest']
+ include:
+ # testing lowest PHP version with lowest dependencies
+ - php-version: '7.2.5'
+ dependency-versions: 'lowest'
+ composer-options: '--prefer-lowest'
+
+ steps:
+ - name: "Checkout code"
+ uses: "actions/checkout@v2"
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "${{ matrix.php-version }}"
+
+# - name: Install Global Dependencies
+# run: |
+# composer global require --no-progress --no-scripts --no-plugins symfony/flex >=1.x
+
+ - name: "Composer install"
+ uses: "ramsey/composer-install@v2"
+ with:
+ dependency-versions: "${{ matrix.dependency-versions }}"
+ composer-options: "--prefer-dist --no-progress"
+
+ - name: Unit Tests
+ run: vendor/bin/phpunit
+
+ - name: Install symlinks for demo's
+ uses: "ramsey/composer-install@v2"
+ with:
+ dependency-versions: "${{ matrix.dependency-versions }}"
+ composer-options: "--prefer-dist --no-progress"
+ working-directory: "demo"
+
+ - name: Demo symfony4.4 - Install dependencies
+ uses: "ramsey/composer-install@v2"
+ with:
+ dependency-versions: "${{ matrix.dependency-versions }}"
+ composer-options: "--prefer-dist --no-progress"
+ working-directory: "demo/symfony4.4"
+
+ - name: Demo symfony4.4 - Unit Tests
+ run: demo/symfony4.4/vendor/bin/simple-phpunit -c demo/symfony4.4/phpunit.xml.dist
+
+ - name: Demo symfony6.x - Install dependencies
+ uses: "ramsey/composer-install@v2"
+ with:
+ dependency-versions: "${{ matrix.dependency-versions }}"
+ composer-options: "--prefer-dist --no-progress"
+ working-directory: "demo/symfony6.x"
+ if: ${{ startsWith(matrix.php-version , '8.') }}
+
+ - name: Demo symfony6.x - Unit Tests
+ run: demo/symfony6.x/vendor/bin/simple-phpunit -c demo/symfony6.x/phpunit.xml.dist
+ if: ${{ startsWith(matrix.php-version , '8.') }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index bd757d68..74552b2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
.idea/
/composer.lock
/vendor
+.phpunit.result.cache
+.phpstan-cache/
\ No newline at end of file
diff --git a/.phpunit.result.cache b/.phpunit.result.cache
deleted file mode 100644
index 2ea5639a..00000000
--- a/.phpunit.result.cache
+++ /dev/null
@@ -1 +0,0 @@
-{"version":1,"defects":{"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryDefuseTest::testPersistEntity":4,"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryDefuseTest::testNoUpdateOnReadEncrypted":4,"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryDefuseTest::testStoredDataIsEncrypted":4,"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryHaliteTest::testPersistEntity":1,"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryHaliteTest::testNoUpdateOnReadEncrypted":1,"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryHaliteTest::testStoredDataIsEncrypted":1,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\DependencyInjection\\DoctrineEncryptExtensionTest::testConfigLoadCustom":1,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Encryptors\\HaliteEncryptorTest::testEncryptWithoutExtensionThrowsException":1,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Encryptors\\DefuseEncryptorTest::testEncrypt":4,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Encryptors\\DefuseEncryptorTest::testGenerateKey":4,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Encryptors\\HaliteEncryptorTest::testEncryptExtension":1,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Encryptors\\HaliteEncryptorTest::testGenerateKey":1,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsNoEncryptor":4},"times":{"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryDefuseTest::testPersistEntity":0.456,"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryDefuseTest::testNoUpdateOnReadEncrypted":1.046,"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryDefuseTest::testStoredDataIsEncrypted":0.654,"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryHaliteTest::testPersistEntity":0.106,"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryHaliteTest::testNoUpdateOnReadEncrypted":0.142,"Ambta\\DoctrineEncryptBundle\\Tests\\Functional\\BasicQueryTest\\BasicQueryHaliteTest::testStoredDataIsEncrypted":0.126,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\DependencyInjection\\DoctrineEncryptExtensionTest::testConfigLoadHalite":0.031,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\DependencyInjection\\DoctrineEncryptExtensionTest::testConfigLoadDefuse":0.012,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\DependencyInjection\\DoctrineEncryptExtensionTest::testConfigLoadCustom":0.013,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Encryptors\\DefuseEncryptorTest::testEncrypt":0.252,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Encryptors\\DefuseEncryptorTest::testGenerateKey":0.132,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Encryptors\\HaliteEncryptorTest::testEncryptExtension":0.005,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Encryptors\\HaliteEncryptorTest::testGenerateKey":0.008,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Encryptors\\HaliteEncryptorTest::testEncryptWithoutExtensionThrowsException":0.001,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testSetRestorEncryptor":0.01,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsEncrypt":0.003,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsEncryptExtend":0.003,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsEncryptEmbedded":0.004,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsEncryptNull":0.002,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsNoEncryptor":0.001,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsDecrypt":0.002,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsDecryptExtended":0.003,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsDecryptEmbedded":0.003,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsDecryptNull":0.002,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testProcessFieldsDecryptNonEncrypted":0.002,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testOnFlush":0.052,"Ambta\\DoctrineEncryptBundle\\Tests\\Unit\\Subscribers\\DoctrineEncryptSubscriberTest::testPostFlush":0.006}}
\ No newline at end of file
diff --git a/README.md b/README.md
index 5e7d22d0..c3ca35f8 100644
--- a/README.md
+++ b/README.md
@@ -62,3 +62,8 @@ Filename example: `.DefuseEncryptor.key` or `.HaliteEncryptor.key`
* [Usage](src/Resources/doc/usage.md)
* [Console commands](src/Resources/doc/commands.md)
* [Custom encryption class](src/Resources/doc/custom_encryptor.md)
+
+### Demo
+
+Two demo-installations, one using symfony 4.4 and one using symfony 6.x, can be found in this repository in [`demo`](demo). This demonstrates how to use
+the application using both annotations and, when using php > 8.0, attributes.
diff --git a/composer.json b/composer.json
index fc3a8a89..699dd29c 100644
--- a/composer.json
+++ b/composer.json
@@ -5,19 +5,25 @@
"license": "MIT",
"description": "Encrypted symfony entity's by verified and standardized libraries",
"require": {
- "php": "^8.0",
+ "php": "^7.2|^8.0",
"paragonie/halite": "^4.6",
"paragonie/sodium_compat": "^1.5",
"doctrine/orm": "^2.5",
+ "doctrine/doctrine-bundle": "^2.0",
"symfony/property-access": "^4.1|^5.0|^6.0",
"symfony/dependency-injection": "^4.1|^5.0|^6.0",
"symfony/yaml": "^4.1|^5.0|^6.0",
"symfony/http-kernel": "^4.1|^5.0|^6.0",
- "symfony/config": "^4.1|^5.0|^6.0"
+ "symfony/config": "^4.1|^5.0|^6.0",
+ "doctrine/annotations": "^1.13"
},
"require-dev": {
"phpunit/phpunit": "^8.0|^9.0",
- "defuse/php-encryption": "^2.1"
+ "defuse/php-encryption": "^2.1",
+ "doctrine/cache": "^1.11",
+ "phpstan/phpstan": "^1.4",
+ "jetbrains/phpstorm-attributes": "^1.0",
+ "phpcompatibility/php-compatibility": "^9.3"
},
"suggest": {
"defuse/php-encryption": "Alternative for halite for use with older php-versions",
@@ -28,9 +34,14 @@
"Ambta\\DoctrineEncryptBundle\\": "src/"
}
},
+ "scripts": {
+ "post-install-cmd": "\"vendor/bin/phpcs\" --config-set installed_paths vendor/phpcompatibility/php-compatibility",
+ "post-update-cmd" : "\"vendor/bin/phpcs\" --config-set installed_paths vendor/phpcompatibility/php-compatibility",
+ "phpcs-compatibility-test" : "vendor/bin/phpcs src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 7.2-8.1"
+ },
"autoload-dev": {
"psr-4": {
- "Ambta\\DoctrineEncryptBundle\\Tests\\": "Tests/"
+ "Ambta\\DoctrineEncryptBundle\\Tests\\": "tests/"
}
},
"config": {
diff --git a/demo/.gitignore b/demo/.gitignore
new file mode 100644
index 00000000..74552b2c
--- /dev/null
+++ b/demo/.gitignore
@@ -0,0 +1,5 @@
+.idea/
+/composer.lock
+/vendor
+.phpunit.result.cache
+.phpstan-cache/
\ No newline at end of file
diff --git a/demo/README.md b/demo/README.md
new file mode 100644
index 00000000..6bb37caf
--- /dev/null
+++ b/demo/README.md
@@ -0,0 +1,3 @@
+Run `composer.install` in this directory to create symlinks for the shared files to the demo-projects
+
+If you prefer a copy, use `composer install --no-dev`.
diff --git a/demo/composer.json b/demo/composer.json
new file mode 100644
index 00000000..a7844a22
--- /dev/null
+++ b/demo/composer.json
@@ -0,0 +1,26 @@
+{
+ "name": "absolute-quantum/doctrine-encrypt-bundle-demo",
+ "description": "Symlinks shared directories",
+ "type": "metapackage",
+ "require": {
+ "kporras07/composer-symlinks": "^1.1"
+ },
+ "scripts": {
+ "post-install-cmd": [
+ "Kporras07\\ComposerSymlinks\\ScriptHandler::createSymlinks"
+ ],
+ "post-update-cmd": [
+ "Kporras07\\ComposerSymlinks\\ScriptHandler::createSymlinks"
+ ]
+ },
+ "extra": {
+ "symlinks": {
+ "shared/templates": "symfony4.4/templates",
+ "./shared/templates": "symfony6.x/templates",
+ "shared/var/data.db": "symfony4.4/var/data.db",
+ "./shared/var/data.db": "symfony6.x/var/data.db",
+ "shared/.Halite.key": "symfony4.4/.Halite.key",
+ "./shared/.Halite.key": "symfony6.x/.Halite.key"
+ }
+ }
+}
diff --git a/demo/shared/.Halite.key b/demo/shared/.Halite.key
new file mode 100644
index 00000000..352aa20d
--- /dev/null
+++ b/demo/shared/.Halite.key
@@ -0,0 +1 @@
+31400400ce550006e6a149298ed2a9d78d94d9a5aaac68e4b2d86ee0174c4e25ad03983e03d6d958d8f51636cbe221dd21ae6c186058af6b998001a7a04954a14ffc675595ccbdb04602b76dabe84b33d023974e8b8e689a1d03a9affe5abadad18a8861
\ No newline at end of file
diff --git a/demo/shared/templates/index.html.twig b/demo/shared/templates/index.html.twig
new file mode 100644
index 00000000..65279859
--- /dev/null
+++ b/demo/shared/templates/index.html.twig
@@ -0,0 +1,51 @@
+
+
+
+
+ {% block title %}Welcome!{% endblock %}
+
+
+
+ Hello World!
+ Symfony {{ appVersion }} using php{{ constant('PHP_MAJOR_VERSION')~'.'~constant('PHP_MINOR_VERSION') }}
+
+
+ | Type |
+ Name |
+ Secret |
+ Raw Secret (as stored in DB) |
+
+ {% for secret in secrets %}
+
+ | {{ secret.type }} |
+ {{ secret.name }} |
+ {{ secret.secret }} |
+ {{ secret.rawSecret }} |
+
+ {% endfor %}
+
+
+
diff --git a/demo/shared/var/data.db b/demo/shared/var/data.db
new file mode 100644
index 00000000..760eeee8
Binary files /dev/null and b/demo/shared/var/data.db differ
diff --git a/demo/symfony4.4/.env b/demo/symfony4.4/.env
new file mode 100644
index 00000000..4f0cd85f
--- /dev/null
+++ b/demo/symfony4.4/.env
@@ -0,0 +1,31 @@
+# In all environments, the following files are loaded if they exist,
+# the latter taking precedence over the former:
+#
+# * .env contains default values for the environment variables needed by the app
+# * .env.local uncommitted file with local overrides
+# * .env.$APP_ENV committed environment-specific defaults
+# * .env.$APP_ENV.local uncommitted environment-specific overrides
+#
+# Real environment variables win over .env files.
+#
+# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
+#
+# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
+# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
+
+###> symfony/framework-bundle ###
+APP_ENV=dev
+APP_SECRET=50710977b1177353fb0c574b9ce67051
+#TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
+#TRUSTED_HOSTS='^(localhost|example\.com)$'
+###< symfony/framework-bundle ###
+
+###> doctrine/doctrine-bundle ###
+# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
+# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
+#
+# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
+# DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7"
+DATABASE_PATH="var/data.db"
+DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
+###< doctrine/doctrine-bundle ###
diff --git a/demo/symfony4.4/.gitignore b/demo/symfony4.4/.gitignore
new file mode 100644
index 00000000..36eed7b0
--- /dev/null
+++ b/demo/symfony4.4/.gitignore
@@ -0,0 +1,16 @@
+# Ignore symlinks
+/templates
+/var/data.db
+/.Halite.key
+
+# Ignore locked versions of composer
+composer.lock
+###> symfony/framework-bundle ###
+/.env.local
+/.env.local.php
+/.env.*.local
+/config/secrets/prod/prod.decrypt.private.php
+/public/bundles/
+/var/
+/vendor/
+###< symfony/framework-bundle ###
diff --git a/demo/symfony4.4/.phpversion b/demo/symfony4.4/.phpversion
new file mode 100644
index 00000000..ea70ce01
--- /dev/null
+++ b/demo/symfony4.4/.phpversion
@@ -0,0 +1 @@
+72
diff --git a/demo/symfony4.4/README.md b/demo/symfony4.4/README.md
new file mode 100644
index 00000000..ba4ace44
--- /dev/null
+++ b/demo/symfony4.4/README.md
@@ -0,0 +1,26 @@
+This demo-installation demonstrates a simple symfony 4.4-application using both annotations and, when using php > 8.0, attributes.
+
+# How to use
+```shell
+# run `composer install` in parent-directory to create symlinks for shared files
+cd ../
+composer install
+cd -
+
+# Install packages
+composer install
+
+# Serve the application
+## Using symfony-cli
+symfony serve
+## Using php-builtin-server ()
+php -S localhost:8000 -t public
+## Other possibility
+### Use a webserver like apache or nginx
+
+
+# Run unittests
+vendor/bin/simple-phpunit
+```
+
+
diff --git a/demo/symfony4.4/bin/console b/demo/symfony4.4/bin/console
new file mode 100755
index 00000000..5de0e1c5
--- /dev/null
+++ b/demo/symfony4.4/bin/console
@@ -0,0 +1,42 @@
+#!/usr/bin/env php
+getParameterOption(['--env', '-e'], null, true)) {
+ putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env);
+}
+
+if ($input->hasParameterOption('--no-debug', true)) {
+ putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0');
+}
+
+require dirname(__DIR__).'/config/bootstrap.php';
+
+if ($_SERVER['APP_DEBUG']) {
+ umask(0000);
+
+ if (class_exists(Debug::class)) {
+ Debug::enable();
+ }
+}
+
+$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
+$application = new Application($kernel);
+$application->run($input);
diff --git a/demo/symfony4.4/composer.json b/demo/symfony4.4/composer.json
new file mode 100644
index 00000000..88824244
--- /dev/null
+++ b/demo/symfony4.4/composer.json
@@ -0,0 +1,72 @@
+{
+ "type": "project",
+ "license": "proprietary",
+ "require": {
+ "php": ">=7.1.3",
+ "ext-ctype": "*",
+ "ext-iconv": "*",
+ "absolute-quantum/doctrine-encrypt-bundle": "@dev",
+ "doctrine/persistence": "^1.3|^2.0",
+ "symfony/console": "4.4.*",
+ "symfony/dotenv": "4.4.*",
+ "symfony/flex": "^1.3.1",
+ "symfony/framework-bundle": "4.4.*",
+ "symfony/twig-bundle": "4.4.*",
+ "symfony/yaml": "4.4.*"
+ },
+ "require-dev": {
+ "symfony/phpunit-bridge": "^6.0"
+ },
+ "config": {
+ "allow-plugins": {
+ "composer/package-versions-deprecated": true,
+ "symfony/flex": true,
+ "ajgl/composer-symlinker": true
+ },
+ "preferred-install": {
+ "*": "dist"
+ },
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": {
+ "App\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "App\\Tests\\": "tests/"
+ }
+ },
+ "replace": {
+ "paragonie/random_compat": "2.*",
+ "symfony/polyfill-ctype": "*",
+ "symfony/polyfill-iconv": "*",
+ "symfony/polyfill-php71": "*",
+ "symfony/polyfill-php70": "*",
+ "symfony/polyfill-php56": "*"
+ },
+ "scripts": {
+ "post-install-cmd": [
+ "bin/console cache:clear"
+ ],
+ "post-update-cmd": [
+ "bin/console cache:clear"
+ ]
+ },
+ "conflict": {
+ "symfony/symfony": "*"
+ },
+ "extra": {
+ "symfony": {
+ "allow-contrib": false,
+ "require": "4.4.*"
+ }
+ },
+ "repositories": [
+ {
+ "type": "path",
+ "url": "../../"
+ }
+ ]
+}
diff --git a/demo/symfony4.4/config/bootstrap.php b/demo/symfony4.4/config/bootstrap.php
new file mode 100644
index 00000000..55560fb8
--- /dev/null
+++ b/demo/symfony4.4/config/bootstrap.php
@@ -0,0 +1,23 @@
+=1.2)
+if (is_array($env = @include dirname(__DIR__).'/.env.local.php') && (!isset($env['APP_ENV']) || ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? $env['APP_ENV']) === $env['APP_ENV'])) {
+ (new Dotenv(false))->populate($env);
+} else {
+ // load all the .env files
+ (new Dotenv(false))->loadEnv(dirname(__DIR__).'/.env');
+}
+
+$_SERVER += $_ENV;
+$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = ($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) ?: 'dev';
+$_SERVER['APP_DEBUG'] = $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? 'prod' !== $_SERVER['APP_ENV'];
+$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = (int) $_SERVER['APP_DEBUG'] || filter_var($_SERVER['APP_DEBUG'], FILTER_VALIDATE_BOOLEAN) ? '1' : '0';
diff --git a/demo/symfony4.4/config/bundles.php b/demo/symfony4.4/config/bundles.php
new file mode 100644
index 00000000..05746c3a
--- /dev/null
+++ b/demo/symfony4.4/config/bundles.php
@@ -0,0 +1,8 @@
+ ['all' => true],
+ Ambta\DoctrineEncryptBundle\AmbtaDoctrineEncryptBundle::class => ['all' => true],
+ Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
+ Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
+];
diff --git a/demo/symfony4.4/config/packages/cache.yaml b/demo/symfony4.4/config/packages/cache.yaml
new file mode 100644
index 00000000..6899b720
--- /dev/null
+++ b/demo/symfony4.4/config/packages/cache.yaml
@@ -0,0 +1,19 @@
+framework:
+ cache:
+ # Unique name of your app: used to compute stable namespaces for cache keys.
+ #prefix_seed: your_vendor_name/app_name
+
+ # The "app" cache stores to the filesystem by default.
+ # The data in this cache should persist between deploys.
+ # Other options include:
+
+ # Redis
+ #app: cache.adapter.redis
+ #default_redis_provider: redis://localhost
+
+ # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
+ #app: cache.adapter.apcu
+
+ # Namespaced pools use the above "app" backend by default
+ #pools:
+ #my.dedicated.cache: null
diff --git a/demo/symfony4.4/config/packages/doctrine.yaml b/demo/symfony4.4/config/packages/doctrine.yaml
new file mode 100644
index 00000000..3ddaa9f6
--- /dev/null
+++ b/demo/symfony4.4/config/packages/doctrine.yaml
@@ -0,0 +1,18 @@
+doctrine:
+ dbal:
+ url: '%env(resolve:DATABASE_URL)%'
+
+ # IMPORTANT: You MUST configure your server version,
+ # either here or in the DATABASE_URL env var (see .env file)
+ #server_version: '13'
+ orm:
+ auto_generate_proxy_classes: true
+ naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
+ auto_mapping: true
+ mappings:
+ Annotation:
+ type: 'annotation'
+ is_bundle: false
+ dir: '%kernel.project_dir%/src/Entity/Annotation'
+ prefix: 'App\Entity\Annotation'
+ alias: App
diff --git a/demo/symfony4.4/config/packages/framework.yaml b/demo/symfony4.4/config/packages/framework.yaml
new file mode 100644
index 00000000..cad7f780
--- /dev/null
+++ b/demo/symfony4.4/config/packages/framework.yaml
@@ -0,0 +1,17 @@
+# see https://symfony.com/doc/current/reference/configuration/framework.html
+framework:
+ secret: '%env(APP_SECRET)%'
+ #csrf_protection: true
+ #http_method_override: true
+
+ # Enables session support. Note that the session will ONLY be started if you read or write from it.
+ # Remove or comment this section to explicitly disable session support.
+ session:
+ handler_id: null
+ cookie_secure: auto
+ cookie_samesite: lax
+
+ #esi: true
+ #fragments: true
+ php_errors:
+ log: true
diff --git a/demo/symfony4.4/config/packages/php8/doctrine.yaml b/demo/symfony4.4/config/packages/php8/doctrine.yaml
new file mode 100644
index 00000000..371d4934
--- /dev/null
+++ b/demo/symfony4.4/config/packages/php8/doctrine.yaml
@@ -0,0 +1,15 @@
+doctrine:
+ orm:
+ mappings:
+ Annotation:
+ type: 'annotation'
+ is_bundle: false
+ dir: '%kernel.project_dir%/src/Entity/Annotation'
+ prefix: 'App\Entity\Annotation'
+ alias: App
+ Attributes:
+ type: 'attribute'
+ is_bundle: false
+ dir: '%kernel.project_dir%/src/Entity/Attribute'
+ prefix: 'App\Entity\Attribute'
+ alias: App
diff --git a/demo/symfony4.4/config/packages/prod/doctrine.yaml b/demo/symfony4.4/config/packages/prod/doctrine.yaml
new file mode 100644
index 00000000..17299e28
--- /dev/null
+++ b/demo/symfony4.4/config/packages/prod/doctrine.yaml
@@ -0,0 +1,17 @@
+doctrine:
+ orm:
+ auto_generate_proxy_classes: false
+ query_cache_driver:
+ type: pool
+ pool: doctrine.system_cache_pool
+ result_cache_driver:
+ type: pool
+ pool: doctrine.result_cache_pool
+
+framework:
+ cache:
+ pools:
+ doctrine.result_cache_pool:
+ adapter: cache.app
+ doctrine.system_cache_pool:
+ adapter: cache.system
diff --git a/demo/symfony4.4/config/packages/prod/routing.yaml b/demo/symfony4.4/config/packages/prod/routing.yaml
new file mode 100644
index 00000000..b3e6a0af
--- /dev/null
+++ b/demo/symfony4.4/config/packages/prod/routing.yaml
@@ -0,0 +1,3 @@
+framework:
+ router:
+ strict_requirements: null
diff --git a/demo/symfony4.4/config/packages/routing.yaml b/demo/symfony4.4/config/packages/routing.yaml
new file mode 100644
index 00000000..7e977620
--- /dev/null
+++ b/demo/symfony4.4/config/packages/routing.yaml
@@ -0,0 +1,3 @@
+framework:
+ router:
+ utf8: true
diff --git a/demo/symfony4.4/config/packages/test/doctrine.yaml b/demo/symfony4.4/config/packages/test/doctrine.yaml
new file mode 100644
index 00000000..e00e38f5
--- /dev/null
+++ b/demo/symfony4.4/config/packages/test/doctrine.yaml
@@ -0,0 +1,4 @@
+doctrine:
+ dbal:
+ # "TEST_TOKEN" is typically set by ParaTest
+ #dbname_suffix: '_test%env(default::TEST_TOKEN)%'
diff --git a/demo/symfony4.4/config/packages/test/framework.yaml b/demo/symfony4.4/config/packages/test/framework.yaml
new file mode 100644
index 00000000..d051c840
--- /dev/null
+++ b/demo/symfony4.4/config/packages/test/framework.yaml
@@ -0,0 +1,4 @@
+framework:
+ test: true
+ session:
+ storage_id: session.storage.mock_file
diff --git a/demo/symfony4.4/config/packages/test/twig.yaml b/demo/symfony4.4/config/packages/test/twig.yaml
new file mode 100644
index 00000000..8c6e0b40
--- /dev/null
+++ b/demo/symfony4.4/config/packages/test/twig.yaml
@@ -0,0 +1,2 @@
+twig:
+ strict_variables: true
diff --git a/demo/symfony4.4/config/packages/twig.yaml b/demo/symfony4.4/config/packages/twig.yaml
new file mode 100644
index 00000000..773160cf
--- /dev/null
+++ b/demo/symfony4.4/config/packages/twig.yaml
@@ -0,0 +1,7 @@
+twig:
+ default_path: '%kernel.project_dir%/templates'
+ debug: '%kernel.debug%'
+ strict_variables: '%kernel.debug%'
+ exception_controller: null
+ globals:
+ appVersion: "4.4"
diff --git a/demo/symfony4.4/config/preload.php b/demo/symfony4.4/config/preload.php
new file mode 100644
index 00000000..064bdcd6
--- /dev/null
+++ b/demo/symfony4.4/config/preload.php
@@ -0,0 +1,9 @@
+ doctrine/doctrine-bundle ###
+ database:
+ ports:
+ - "5432"
+###< doctrine/doctrine-bundle ###
diff --git a/demo/symfony4.4/docker-compose.yml b/demo/symfony4.4/docker-compose.yml
new file mode 100644
index 00000000..a80a8cf9
--- /dev/null
+++ b/demo/symfony4.4/docker-compose.yml
@@ -0,0 +1,21 @@
+version: '3'
+
+services:
+###> doctrine/doctrine-bundle ###
+ database:
+ image: postgres:${POSTGRES_VERSION:-13}-alpine
+ environment:
+ POSTGRES_DB: ${POSTGRES_DB:-app}
+ # You should definitely change the password in production
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-ChangeMe}
+ POSTGRES_USER: ${POSTGRES_USER:-symfony}
+ volumes:
+ - db-data:/var/lib/postgresql/data:rw
+ # You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data!
+ # - ./docker/db/data:/var/lib/postgresql/data:rw
+###< doctrine/doctrine-bundle ###
+
+volumes:
+###> doctrine/doctrine-bundle ###
+ db-data:
+###< doctrine/doctrine-bundle ###
diff --git a/demo/symfony4.4/phpunit.xml.dist b/demo/symfony4.4/phpunit.xml.dist
new file mode 100644
index 00000000..ddc7f24a
--- /dev/null
+++ b/demo/symfony4.4/phpunit.xml.dist
@@ -0,0 +1,29 @@
+
+
+
+
+ tests
+
+
+
+
+
+ src
+
+
+
+
+
+
+
+
+
diff --git a/demo/symfony4.4/public/index.php b/demo/symfony4.4/public/index.php
new file mode 100644
index 00000000..d0b6e020
--- /dev/null
+++ b/demo/symfony4.4/public/index.php
@@ -0,0 +1,27 @@
+handle($request);
+$response->send();
+$kernel->terminate($request, $response);
diff --git a/demo/symfony4.4/src/Controller/.gitignore b/demo/symfony4.4/src/Controller/.gitignore
new file mode 100644
index 00000000..e69de29b
diff --git a/demo/symfony4.4/src/Controller/DefaultController.php b/demo/symfony4.4/src/Controller/DefaultController.php
new file mode 100644
index 00000000..0d4b0c5d
--- /dev/null
+++ b/demo/symfony4.4/src/Controller/DefaultController.php
@@ -0,0 +1,55 @@
+findAll(),
+ $secretUsingAttributesRepository->findAll()
+ );
+
+ return $this->render('index.html.twig',['secrets' => $secrets]);
+ }
+
+ /**
+ * @Route(name="create", path="/create")
+ */
+ public function create(Request $request, EntityManagerInterface $em): Response
+ {
+ if (!$request->query->has('name') || !$request->query->has('secret') || !$request->query->has('type')) {
+ return new Response('Please specify name, secret and type in url-query');
+ }
+
+ $type = $request->query->get('type');
+ if ($type === 'annotation') {
+ $secret = new \App\Entity\Annotation\Secret();
+ } elseif($type === 'attribute') {
+ $secret = new \App\Entity\Attribute\Secret();
+ } else {
+ return new Response('Type is only allowed to be "annotation" or "attribute"');
+ }
+
+ $secret
+ ->setName($request->query->getAlnum('name'))
+ ->setSecret($request->query->getAlnum('secret'));
+
+ $em->persist($secret);
+ $em->flush();
+
+ return new Response(sprintf('OK - secret %s stored',$secret->getName()));
+ }
+}
\ No newline at end of file
diff --git a/demo/symfony4.4/src/Entity/.gitignore b/demo/symfony4.4/src/Entity/.gitignore
new file mode 100644
index 00000000..e69de29b
diff --git a/demo/symfony4.4/src/Entity/Annotation/Secret.php b/demo/symfony4.4/src/Entity/Annotation/Secret.php
new file mode 100644
index 00000000..49dce58a
--- /dev/null
+++ b/demo/symfony4.4/src/Entity/Annotation/Secret.php
@@ -0,0 +1,98 @@
+name;
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function setName($name): self
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSecret()
+ {
+ return $this->secret;
+ }
+
+ /**
+ * @param string $secret
+ *
+ * @return $this
+ */
+ public function setSecret($secret): self
+ {
+ $this->secret = $secret;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRawSecret()
+ {
+ return $this->rawSecret;
+ }
+
+ /**
+ * @param mixed $rawSecret
+ *
+ * @return $this
+ */
+ public function setRawSecret($rawSecret)
+ {
+ $this->rawSecret = $rawSecret;
+
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/demo/symfony4.4/src/Entity/Attribute/Secret.php b/demo/symfony4.4/src/Entity/Attribute/Secret.php
new file mode 100644
index 00000000..c76a80db
--- /dev/null
+++ b/demo/symfony4.4/src/Entity/Attribute/Secret.php
@@ -0,0 +1,94 @@
+name;
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function setName($name): self
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSecret()
+ {
+ return $this->secret;
+ }
+
+ /**
+ * @param string $secret
+ *
+ * @return $this
+ */
+ public function setSecret($secret): self
+ {
+ $this->secret = $secret;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRawSecret()
+ {
+ return $this->rawSecret;
+ }
+
+ /**
+ * @param mixed $rawSecret
+ *
+ * @return $this
+ */
+ public function setRawSecret($rawSecret)
+ {
+ $this->rawSecret = $rawSecret;
+
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/demo/symfony4.4/src/Entity/SecretInterface.php b/demo/symfony4.4/src/Entity/SecretInterface.php
new file mode 100644
index 00000000..cd234732
--- /dev/null
+++ b/demo/symfony4.4/src/Entity/SecretInterface.php
@@ -0,0 +1,11 @@
+getProjectDir().'/config/bundles.php';
+ foreach ($contents as $class => $envs) {
+ if ($envs[$this->environment] ?? $envs['all'] ?? false) {
+ yield new $class();
+ }
+ }
+ }
+
+ public function getProjectDir(): string
+ {
+ return \dirname(__DIR__);
+ }
+
+ protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
+ {
+ $container->addResource(new FileResource($this->getProjectDir().'/config/bundles.php'));
+ $container->setParameter('container.dumper.inline_class_loader', \PHP_VERSION_ID < 70400 || $this->debug);
+ $container->setParameter('container.dumper.inline_factories', true);
+ $confDir = $this->getProjectDir().'/config';
+
+ $loader->load($confDir.'/{packages}/*'.self::CONFIG_EXTS, 'glob');
+ $loader->load($confDir.'/{packages}/'.$this->environment.'/*'.self::CONFIG_EXTS, 'glob');
+ $loader->load($confDir.'/{services}'.self::CONFIG_EXTS, 'glob');
+ $loader->load($confDir.'/{services}_'.$this->environment.self::CONFIG_EXTS, 'glob');
+
+ if (PHP_VERSION_ID >= 80000) {
+ $loader->load($confDir.'/{packages}/php8/*'.self::CONFIG_EXTS, 'glob');
+ }
+ }
+
+ protected function configureRoutes(RouteCollectionBuilder $routes): void
+ {
+ $confDir = $this->getProjectDir().'/config';
+
+ $routes->import($confDir.'/{routes}/'.$this->environment.'/*'.self::CONFIG_EXTS, '/', 'glob');
+ $routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob');
+ $routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob');
+ }
+}
diff --git a/demo/symfony4.4/src/Repository/.gitignore b/demo/symfony4.4/src/Repository/.gitignore
new file mode 100644
index 00000000..e69de29b
diff --git a/demo/symfony4.4/src/Repository/AbstractSecretRepository.php b/demo/symfony4.4/src/Repository/AbstractSecretRepository.php
new file mode 100644
index 00000000..13510ec2
--- /dev/null
+++ b/demo/symfony4.4/src/Repository/AbstractSecretRepository.php
@@ -0,0 +1,26 @@
+createQueryBuilder('s');
+ $qb->select('s')
+ ->addSelect('(s.secret) as rawSecret')
+ ->orderBy('s.name','ASC');
+ $rawResult = $qb->getQuery()->getResult();
+
+ $result = [];
+ foreach ($rawResult as $row) {
+ $secret = $row[0];
+ $secret->setRawSecret($row['rawSecret']);
+ $result[] = $secret;
+ }
+
+ return $result;
+ }
+}
\ No newline at end of file
diff --git a/demo/symfony4.4/src/Repository/Annotation/SecretRepository.php b/demo/symfony4.4/src/Repository/Annotation/SecretRepository.php
new file mode 100644
index 00000000..fc1427a5
--- /dev/null
+++ b/demo/symfony4.4/src/Repository/Annotation/SecretRepository.php
@@ -0,0 +1,27 @@
+= 80000) {
+ /**
+ * @method Secret|null find($id, $lockMode = null, $lockVersion = null)
+ * @method Secret|null findOneBy(array $criteria, array $orderBy = null)
+ * @method Secret[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
+ */
+ class SecretRepository extends AbstractSecretRepository
+ {
+ public function __construct(\Doctrine\Common\Persistence\ManagerRegistry $registry)
+ {
+ parent::__construct($registry, Secret::class);
+ }
+ }
+} else {
+ /**
+ * Dummy-repository for php < 8.0
+ */
+ class SecretRepository
+ {
+ public function findAll()
+ {
+ return [];
+ }
+
+ public function find($id, $lockMode = null, $lockVersion = null)
+ {
+ return null;
+ }
+
+ public function findOneBy(array $criteria, array $orderBy = null)
+ {
+ return null;
+ }
+
+ public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
+ {
+ return [];
+ }
+ }
+}
\ No newline at end of file
diff --git a/demo/symfony4.4/symfony.lock b/demo/symfony4.4/symfony.lock
new file mode 100644
index 00000000..2a2aaeab
--- /dev/null
+++ b/demo/symfony4.4/symfony.lock
@@ -0,0 +1,266 @@
+{
+ "absolute-quantum/doctrine-encrypt-bundle": {
+ "version": "dev-master-fixes"
+ },
+ "doctrine/annotations": {
+ "version": "1.13",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "1.0",
+ "ref": "a2759dd6123694c8d901d0ec80006e044c2e6457"
+ },
+ "files": [
+ "config/routes/annotations.yaml"
+ ]
+ },
+ "doctrine/cache": {
+ "version": "2.1.1"
+ },
+ "doctrine/collections": {
+ "version": "1.6.8"
+ },
+ "doctrine/common": {
+ "version": "3.2.2"
+ },
+ "doctrine/dbal": {
+ "version": "3.3.2"
+ },
+ "doctrine/deprecations": {
+ "version": "v0.5.3"
+ },
+ "doctrine/doctrine-bundle": {
+ "version": "2.5",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "2.4",
+ "ref": "f98f1affe028f8153a459d15f220ada3826b5aa2"
+ },
+ "files": [
+ "config/packages/doctrine.yaml",
+ "config/packages/prod/doctrine.yaml",
+ "config/packages/test/doctrine.yaml",
+ "src/Entity/.gitignore",
+ "src/Repository/.gitignore"
+ ]
+ },
+ "doctrine/event-manager": {
+ "version": "1.1.1"
+ },
+ "doctrine/inflector": {
+ "version": "2.0.4"
+ },
+ "doctrine/instantiator": {
+ "version": "1.4.0"
+ },
+ "doctrine/lexer": {
+ "version": "1.2.2"
+ },
+ "doctrine/orm": {
+ "version": "2.11.1"
+ },
+ "doctrine/persistence": {
+ "version": "2.3.0"
+ },
+ "doctrine/sql-formatter": {
+ "version": "1.1.2"
+ },
+ "kporras07/composer-symlinks": {
+ "version": "v1.1"
+ },
+ "paragonie/constant_time_encoding": {
+ "version": "v2.5.0"
+ },
+ "paragonie/halite": {
+ "version": "v4.8.0"
+ },
+ "paragonie/hidden-string": {
+ "version": "v2.0.0"
+ },
+ "paragonie/sodium_compat": {
+ "version": "v1.17.0"
+ },
+ "psr/cache": {
+ "version": "2.0.0"
+ },
+ "psr/container": {
+ "version": "1.1.2"
+ },
+ "psr/log": {
+ "version": "2.0.0"
+ },
+ "symfony/cache": {
+ "version": "v4.4.37"
+ },
+ "symfony/cache-contracts": {
+ "version": "v2.5.0"
+ },
+ "symfony/config": {
+ "version": "v4.4.37"
+ },
+ "symfony/console": {
+ "version": "4.4",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "4.4",
+ "ref": "fd5340d07d4c90504843b53da41525cf42e31f5c"
+ },
+ "files": [
+ "bin/console",
+ "config/bootstrap.php"
+ ]
+ },
+ "symfony/debug": {
+ "version": "v4.4.37"
+ },
+ "symfony/dependency-injection": {
+ "version": "v4.4.37"
+ },
+ "symfony/deprecation-contracts": {
+ "version": "v2.5.0"
+ },
+ "symfony/doctrine-bridge": {
+ "version": "v4.4.37"
+ },
+ "symfony/dotenv": {
+ "version": "v4.4.37"
+ },
+ "symfony/error-handler": {
+ "version": "v4.4.37"
+ },
+ "symfony/event-dispatcher": {
+ "version": "v4.4.37"
+ },
+ "symfony/event-dispatcher-contracts": {
+ "version": "v1.1.11"
+ },
+ "symfony/filesystem": {
+ "version": "v4.4.37"
+ },
+ "symfony/finder": {
+ "version": "v4.4.37"
+ },
+ "symfony/flex": {
+ "version": "1.18",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "1.0",
+ "ref": "c0eeb50665f0f77226616b6038a9b06c03752d8e"
+ },
+ "files": [
+ ".env"
+ ]
+ },
+ "symfony/framework-bundle": {
+ "version": "4.4",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "4.4",
+ "ref": "24eb45d1355810154890460e6a05c0ca27318fe7"
+ },
+ "files": [
+ "config/bootstrap.php",
+ "config/packages/cache.yaml",
+ "config/packages/framework.yaml",
+ "config/packages/test/framework.yaml",
+ "config/preload.php",
+ "config/routes/dev/framework.yaml",
+ "config/services.yaml",
+ "public/index.php",
+ "src/Controller/.gitignore",
+ "src/Kernel.php"
+ ]
+ },
+ "symfony/http-client-contracts": {
+ "version": "v2.5.0"
+ },
+ "symfony/http-foundation": {
+ "version": "v4.4.37"
+ },
+ "symfony/http-kernel": {
+ "version": "v4.4.37"
+ },
+ "symfony/inflector": {
+ "version": "v4.4.37"
+ },
+ "symfony/mime": {
+ "version": "v4.4.37"
+ },
+ "symfony/polyfill-intl-idn": {
+ "version": "v1.24.0"
+ },
+ "symfony/polyfill-intl-normalizer": {
+ "version": "v1.24.0"
+ },
+ "symfony/polyfill-mbstring": {
+ "version": "v1.24.0"
+ },
+ "symfony/polyfill-php72": {
+ "version": "v1.24.0"
+ },
+ "symfony/polyfill-php73": {
+ "version": "v1.24.0"
+ },
+ "symfony/polyfill-php80": {
+ "version": "v1.24.0"
+ },
+ "symfony/polyfill-php81": {
+ "version": "v1.24.0"
+ },
+ "symfony/property-access": {
+ "version": "v4.4.37"
+ },
+ "symfony/routing": {
+ "version": "4.4",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "4.2",
+ "ref": "683dcb08707ba8d41b7e34adb0344bfd68d248a7"
+ },
+ "files": [
+ "config/packages/prod/routing.yaml",
+ "config/packages/routing.yaml",
+ "config/routes.yaml"
+ ]
+ },
+ "symfony/service-contracts": {
+ "version": "v2.5.0"
+ },
+ "symfony/translation-contracts": {
+ "version": "v2.5.0"
+ },
+ "symfony/twig-bridge": {
+ "version": "v4.4.37"
+ },
+ "symfony/twig-bundle": {
+ "version": "4.4",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "4.4",
+ "ref": "73baff3f7b3cea12a73812a7cfd2c0924a9e250f"
+ },
+ "files": [
+ "config/packages/test/twig.yaml",
+ "config/packages/twig.yaml",
+ "templates/base.html.twig"
+ ]
+ },
+ "symfony/var-dumper": {
+ "version": "v4.4.37"
+ },
+ "symfony/var-exporter": {
+ "version": "v4.4.37"
+ },
+ "symfony/yaml": {
+ "version": "v4.4.37"
+ },
+ "twig/twig": {
+ "version": "v3.3.8"
+ }
+}
diff --git a/demo/symfony4.4/templates b/demo/symfony4.4/templates
new file mode 120000
index 00000000..e6b9ddfe
--- /dev/null
+++ b/demo/symfony4.4/templates
@@ -0,0 +1 @@
+../shared/templates
\ No newline at end of file
diff --git a/demo/symfony4.4/tests/SecretTest.php b/demo/symfony4.4/tests/SecretTest.php
new file mode 100644
index 00000000..d66ffacf
--- /dev/null
+++ b/demo/symfony4.4/tests/SecretTest.php
@@ -0,0 +1,78 @@
+get('doctrine.orm.entity_manager');
+
+ // Make sure we do not store testdata
+ $entityManager->beginTransaction();
+
+ $name = 'test123';
+ $secretString = 'i am a secret string';
+
+ // Create entity to test with
+ $newSecretObject = (new $className)
+ ->setName($name)
+ ->setSecret($secretString);
+
+ $entityManager->persist($newSecretObject);
+ $entityManager->flush();
+
+ // Fetch the actual data
+ $secretRepository = $entityManager->getRepository($className);
+ $qb = $secretRepository->createQueryBuilder('s');
+ $qb->select('s')
+ ->addSelect('(s.secret) as rawSecret')
+ ->where('s.name = :name')
+ ->setParameter('name',$name)
+ ->orderBy('s.name','ASC');
+ $result = $qb->getQuery()->getSingleResult();
+
+ $actualSecretObject = $result[0];
+ $actualRawSecret = $result['rawSecret'];
+
+ self::assertInstanceOf($className,$actualSecretObject);
+ self::assertEquals($newSecretObject->getSecret(), $actualSecretObject->getSecret());
+ self::assertEquals($newSecretObject->getName(), $actualSecretObject->getName());
+ // Make sure it is encrypted
+ self::assertNotEquals($newSecretObject->getSecret(),$actualRawSecret);
+ self::assertStringEndsWith(DoctrineEncryptSubscriber::ENCRYPTION_MARKER,$actualRawSecret);
+ }
+
+ /**
+ * @covers Entity\Annotation\Secret::getSecret
+ * @covers Entity\Annotation\Secret::getName
+ */
+ public function testAnnotationSecretsAreEncryptedInDatabase(): void
+ {
+ $this->testSecretsAreEncryptedInDatabase(Entity\Annotation\Secret::class);
+ }
+
+ /**
+ * @covers Entity\Attribute\Secret::getSecret
+ * @covers Entity\Attribute\Secret::getName
+ * @requires PHP 8.0
+ */
+ public function testAttributeSecretsAreEncryptedInDatabase(): void
+ {
+ $this->testSecretsAreEncryptedInDatabase(Entity\Attribute\Secret::class);
+ }
+}
diff --git a/demo/symfony6.x/.env b/demo/symfony6.x/.env
new file mode 100644
index 00000000..54ba0e01
--- /dev/null
+++ b/demo/symfony6.x/.env
@@ -0,0 +1,28 @@
+# In all environments, the following files are loaded if they exist,
+# the latter taking precedence over the former:
+#
+# * .env contains default values for the environment variables needed by the app
+# * .env.local uncommitted file with local overrides
+# * .env.$APP_ENV committed environment-specific defaults
+# * .env.$APP_ENV.local uncommitted environment-specific overrides
+#
+# Real environment variables win over .env files.
+#
+# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
+#
+# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
+# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
+
+###> symfony/framework-bundle ###
+APP_ENV=dev
+APP_SECRET=6962c9529b154a9ca68b705e88c4d677
+###< symfony/framework-bundle ###
+###> doctrine/doctrine-bundle ###
+# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
+# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
+#
+# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
+# DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7"
+DATABASE_PATH="var/data.db"
+DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
+###< doctrine/doctrine-bundle ###
diff --git a/demo/symfony6.x/.env.test b/demo/symfony6.x/.env.test
new file mode 100644
index 00000000..9e7162f0
--- /dev/null
+++ b/demo/symfony6.x/.env.test
@@ -0,0 +1,6 @@
+# define your env variables for the test env here
+KERNEL_CLASS='App\Kernel'
+APP_SECRET='$ecretf0rt3st'
+SYMFONY_DEPRECATIONS_HELPER=999999
+PANTHER_APP_ENV=panther
+PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots
diff --git a/demo/symfony6.x/.gitignore b/demo/symfony6.x/.gitignore
new file mode 100644
index 00000000..9daab39b
--- /dev/null
+++ b/demo/symfony6.x/.gitignore
@@ -0,0 +1,27 @@
+# Ignore symlinks
+/templates
+/var/data.db
+/.Halite.key
+
+# Ignore locked versions of composer
+composer.lock
+
+###> symfony/framework-bundle ###
+/.env.local
+/.env.local.php
+/.env.*.local
+/config/secrets/prod/prod.decrypt.private.php
+/public/bundles/
+/var/
+/vendor/
+###< symfony/framework-bundle ###
+
+###> symfony/phpunit-bridge ###
+.phpunit.result.cache
+/phpunit.xml
+###< symfony/phpunit-bridge ###
+
+###> phpunit/phpunit ###
+/phpunit.xml
+.phpunit.result.cache
+###< phpunit/phpunit ###
diff --git a/demo/symfony6.x/README.md b/demo/symfony6.x/README.md
new file mode 100644
index 00000000..ba4ace44
--- /dev/null
+++ b/demo/symfony6.x/README.md
@@ -0,0 +1,26 @@
+This demo-installation demonstrates a simple symfony 4.4-application using both annotations and, when using php > 8.0, attributes.
+
+# How to use
+```shell
+# run `composer install` in parent-directory to create symlinks for shared files
+cd ../
+composer install
+cd -
+
+# Install packages
+composer install
+
+# Serve the application
+## Using symfony-cli
+symfony serve
+## Using php-builtin-server ()
+php -S localhost:8000 -t public
+## Other possibility
+### Use a webserver like apache or nginx
+
+
+# Run unittests
+vendor/bin/simple-phpunit
+```
+
+
diff --git a/demo/symfony6.x/bin/console b/demo/symfony6.x/bin/console
new file mode 100755
index 00000000..c933dc53
--- /dev/null
+++ b/demo/symfony6.x/bin/console
@@ -0,0 +1,17 @@
+#!/usr/bin/env php
+=8.0.2",
+ "ext-ctype": "*",
+ "ext-iconv": "*",
+ "absolute-quantum/doctrine-encrypt-bundle": "@dev",
+ "symfony/console": "6.0.*",
+ "symfony/dotenv": "6.0.*",
+ "symfony/flex": "^2",
+ "symfony/framework-bundle": "6.0.*",
+ "symfony/runtime": "6.0.*",
+ "symfony/twig-bundle": "6.0.*"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^9.5",
+ "symfony/maker-bundle": "^1.0",
+ "symfony/phpunit-bridge": "^6.0"
+ },
+ "config": {
+ "allow-plugins": {
+ "composer/package-versions-deprecated": true,
+ "symfony/flex": true,
+ "symfony/runtime": true
+ },
+ "optimize-autoloader": true,
+ "preferred-install": {
+ "*": "dist"
+ },
+ "sort-packages": true
+ },
+ "autoload": {
+ "psr-4": {
+ "App\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "App\\Tests\\": "tests/"
+ }
+ },
+ "replace": {
+ "symfony/polyfill-ctype": "*",
+ "symfony/polyfill-iconv": "*",
+ "symfony/polyfill-php72": "*",
+ "symfony/polyfill-php73": "*",
+ "symfony/polyfill-php74": "*",
+ "symfony/polyfill-php80": "*"
+ },
+ "scripts": {
+ "auto-scripts": {
+ "cache:clear": "symfony-cmd",
+ "assets:install %PUBLIC_DIR%": "symfony-cmd"
+ },
+ "post-install-cmd": [
+ "@auto-scripts"
+ ],
+ "post-update-cmd": [
+ "@auto-scripts"
+ ]
+ },
+ "conflict": {
+ "symfony/symfony": "*"
+ },
+ "extra": {
+ "symfony": {
+ "allow-contrib": false,
+ "require": "6.0.*"
+ }
+ },
+ "repositories": [
+ {
+ "type": "path",
+ "url": "../../"
+ }
+ ]
+}
diff --git a/demo/symfony6.x/config/bundles.php b/demo/symfony6.x/config/bundles.php
new file mode 100644
index 00000000..b7e7b92c
--- /dev/null
+++ b/demo/symfony6.x/config/bundles.php
@@ -0,0 +1,9 @@
+ ['all' => true],
+ Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
+ Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
+ Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
+ Ambta\DoctrineEncryptBundle\AmbtaDoctrineEncryptBundle::class => ['all' => true],
+];
diff --git a/demo/symfony6.x/config/packages/cache.yaml b/demo/symfony6.x/config/packages/cache.yaml
new file mode 100644
index 00000000..6899b720
--- /dev/null
+++ b/demo/symfony6.x/config/packages/cache.yaml
@@ -0,0 +1,19 @@
+framework:
+ cache:
+ # Unique name of your app: used to compute stable namespaces for cache keys.
+ #prefix_seed: your_vendor_name/app_name
+
+ # The "app" cache stores to the filesystem by default.
+ # The data in this cache should persist between deploys.
+ # Other options include:
+
+ # Redis
+ #app: cache.adapter.redis
+ #default_redis_provider: redis://localhost
+
+ # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues)
+ #app: cache.adapter.apcu
+
+ # Namespaced pools use the above "app" backend by default
+ #pools:
+ #my.dedicated.cache: null
diff --git a/demo/symfony6.x/config/packages/doctrine.yaml b/demo/symfony6.x/config/packages/doctrine.yaml
new file mode 100644
index 00000000..bff6abbf
--- /dev/null
+++ b/demo/symfony6.x/config/packages/doctrine.yaml
@@ -0,0 +1,49 @@
+doctrine:
+ dbal:
+ url: '%env(resolve:DATABASE_URL)%'
+
+ # IMPORTANT: You MUST configure your server version,
+ # either here or in the DATABASE_URL env var (see .env file)
+ #server_version: '13'
+ orm:
+ auto_generate_proxy_classes: true
+ naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
+ auto_mapping: true
+ mappings:
+ Annotation:
+ type: 'annotation'
+ is_bundle: false
+ dir: '%kernel.project_dir%/src/Entity/Annotation'
+ prefix: 'App\Entity\Annotation'
+ alias: App
+ Attributes:
+ type: 'attribute'
+ is_bundle: false
+ dir: '%kernel.project_dir%/src/Entity/Attribute'
+ prefix: 'App\Entity\Attribute'
+ alias: App
+
+when@test:
+ doctrine:
+ dbal:
+ # "TEST_TOKEN" is typically set by ParaTest
+ dbname_suffix: '_test%env(default::TEST_TOKEN)%'
+
+when@prod:
+ doctrine:
+ orm:
+ auto_generate_proxy_classes: false
+ query_cache_driver:
+ type: pool
+ pool: doctrine.system_cache_pool
+ result_cache_driver:
+ type: pool
+ pool: doctrine.result_cache_pool
+
+ framework:
+ cache:
+ pools:
+ doctrine.result_cache_pool:
+ adapter: cache.app
+ doctrine.system_cache_pool:
+ adapter: cache.system
diff --git a/demo/symfony6.x/config/packages/framework.yaml b/demo/symfony6.x/config/packages/framework.yaml
new file mode 100644
index 00000000..7853e9ed
--- /dev/null
+++ b/demo/symfony6.x/config/packages/framework.yaml
@@ -0,0 +1,24 @@
+# see https://symfony.com/doc/current/reference/configuration/framework.html
+framework:
+ secret: '%env(APP_SECRET)%'
+ #csrf_protection: true
+ http_method_override: false
+
+ # Enables session support. Note that the session will ONLY be started if you read or write from it.
+ # Remove or comment this section to explicitly disable session support.
+ session:
+ handler_id: null
+ cookie_secure: auto
+ cookie_samesite: lax
+ storage_factory_id: session.storage.factory.native
+
+ #esi: true
+ #fragments: true
+ php_errors:
+ log: true
+
+when@test:
+ framework:
+ test: true
+ session:
+ storage_factory_id: session.storage.factory.mock_file
diff --git a/demo/symfony6.x/config/packages/routing.yaml b/demo/symfony6.x/config/packages/routing.yaml
new file mode 100644
index 00000000..4b766ce5
--- /dev/null
+++ b/demo/symfony6.x/config/packages/routing.yaml
@@ -0,0 +1,12 @@
+framework:
+ router:
+ utf8: true
+
+ # Configure how to generate URLs in non-HTTP contexts, such as CLI commands.
+ # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands
+ #default_uri: http://localhost
+
+when@prod:
+ framework:
+ router:
+ strict_requirements: null
diff --git a/demo/symfony6.x/config/packages/twig.yaml b/demo/symfony6.x/config/packages/twig.yaml
new file mode 100644
index 00000000..d863e8ec
--- /dev/null
+++ b/demo/symfony6.x/config/packages/twig.yaml
@@ -0,0 +1,8 @@
+twig:
+ default_path: '%kernel.project_dir%/templates'
+ globals:
+ appVersion: "6.x"
+
+when@test:
+ twig:
+ strict_variables: true
diff --git a/demo/symfony6.x/config/preload.php b/demo/symfony6.x/config/preload.php
new file mode 100644
index 00000000..5ebcdb21
--- /dev/null
+++ b/demo/symfony6.x/config/preload.php
@@ -0,0 +1,5 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tests
+
+
+
+
+
+ src
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/symfony6.x/public/index.php b/demo/symfony6.x/public/index.php
new file mode 100644
index 00000000..9982c218
--- /dev/null
+++ b/demo/symfony6.x/public/index.php
@@ -0,0 +1,9 @@
+findAll(),
+ $secretUsingAttributesRepository->findAll()
+ );
+
+ return $this->render('index.html.twig',['secrets' => $secrets]);
+ }
+
+ /**
+ * @Route(name="create", path="/create")
+ */
+ public function create(Request $request, EntityManagerInterface $em): Response
+ {
+ if (!$request->query->has('name') || !$request->query->has('secret') || !$request->query->has('type')) {
+ return new Response('Please specify name, secret and type in url-query');
+ }
+
+ $type = $request->query->get('type');
+ if ($type === 'annotation') {
+ $secret = new \App\Entity\Annotation\Secret();
+ } elseif($type === 'attribute') {
+ $secret = new \App\Entity\Attribute\Secret();
+ } else {
+ return new Response('Type is only allowed to be "annotation" or "attribute"');
+ }
+
+ $secret
+ ->setName($request->query->getAlnum('name'))
+ ->setSecret($request->query->getAlnum('secret'));
+
+ $em->persist($secret);
+ $em->flush();
+
+ return new Response(sprintf('OK - secret %s stored',$secret->getName()));
+ }
+}
\ No newline at end of file
diff --git a/demo/symfony6.x/src/Entity/.gitignore b/demo/symfony6.x/src/Entity/.gitignore
new file mode 100644
index 00000000..e69de29b
diff --git a/demo/symfony6.x/src/Entity/Annotation/Secret.php b/demo/symfony6.x/src/Entity/Annotation/Secret.php
new file mode 100644
index 00000000..49dce58a
--- /dev/null
+++ b/demo/symfony6.x/src/Entity/Annotation/Secret.php
@@ -0,0 +1,98 @@
+name;
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function setName($name): self
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSecret()
+ {
+ return $this->secret;
+ }
+
+ /**
+ * @param string $secret
+ *
+ * @return $this
+ */
+ public function setSecret($secret): self
+ {
+ $this->secret = $secret;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRawSecret()
+ {
+ return $this->rawSecret;
+ }
+
+ /**
+ * @param mixed $rawSecret
+ *
+ * @return $this
+ */
+ public function setRawSecret($rawSecret)
+ {
+ $this->rawSecret = $rawSecret;
+
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/demo/symfony6.x/src/Entity/Attribute/Secret.php b/demo/symfony6.x/src/Entity/Attribute/Secret.php
new file mode 100644
index 00000000..c76a80db
--- /dev/null
+++ b/demo/symfony6.x/src/Entity/Attribute/Secret.php
@@ -0,0 +1,94 @@
+name;
+ }
+
+ /**
+ * @param string $name
+ *
+ * @return $this
+ */
+ public function setName($name): self
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSecret()
+ {
+ return $this->secret;
+ }
+
+ /**
+ * @param string $secret
+ *
+ * @return $this
+ */
+ public function setSecret($secret): self
+ {
+ $this->secret = $secret;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getRawSecret()
+ {
+ return $this->rawSecret;
+ }
+
+ /**
+ * @param mixed $rawSecret
+ *
+ * @return $this
+ */
+ public function setRawSecret($rawSecret)
+ {
+ $this->rawSecret = $rawSecret;
+
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/demo/symfony6.x/src/Entity/SecretInterface.php b/demo/symfony6.x/src/Entity/SecretInterface.php
new file mode 100644
index 00000000..cd234732
--- /dev/null
+++ b/demo/symfony6.x/src/Entity/SecretInterface.php
@@ -0,0 +1,11 @@
+ The objects.
+ */
+ public function findAll()
+ {
+ $qb = $this->createQueryBuilder('s');
+ $qb->select('s')
+ ->addSelect('(s.secret) as rawSecret')
+ ->orderBy('s.name','ASC');
+ $rawResult = $qb->getQuery()->getResult();
+
+ $result = [];
+ foreach ($rawResult as $row) {
+ $secret = $row[0];
+ $secret->setRawSecret($row['rawSecret']);
+ $result[] = $secret;
+ }
+
+ return $result;
+ }
+}
\ No newline at end of file
diff --git a/demo/symfony6.x/src/Repository/Annotation/SecretRepository.php b/demo/symfony6.x/src/Repository/Annotation/SecretRepository.php
new file mode 100644
index 00000000..fc1427a5
--- /dev/null
+++ b/demo/symfony6.x/src/Repository/Annotation/SecretRepository.php
@@ -0,0 +1,27 @@
+= 80000) {
+ /**
+ * @method Secret|null find($id, $lockMode = null, $lockVersion = null)
+ * @method Secret|null findOneBy(array $criteria, array $orderBy = null)
+ * @method Secret[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
+ */
+ class SecretRepository extends AbstractSecretRepository
+ {
+ public function __construct(\Doctrine\Common\Persistence\ManagerRegistry $registry)
+ {
+ parent::__construct($registry, Secret::class);
+ }
+ }
+} else {
+ /**
+ * Dummy-repository for php < 8.0
+ */
+ class SecretRepository
+ {
+ public function findAll()
+ {
+ return [];
+ }
+
+ public function find($id, $lockMode = null, $lockVersion = null)
+ {
+ return null;
+ }
+
+ public function findOneBy(array $criteria, array $orderBy = null)
+ {
+ return null;
+ }
+
+ public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
+ {
+ return [];
+ }
+ }
+}
\ No newline at end of file
diff --git a/demo/symfony6.x/symfony.lock b/demo/symfony6.x/symfony.lock
new file mode 100644
index 00000000..aaf4fff0
--- /dev/null
+++ b/demo/symfony6.x/symfony.lock
@@ -0,0 +1,381 @@
+{
+ "absolute-quantum/doctrine-encrypt-bundle": {
+ "version": "dev-master-fixes"
+ },
+ "doctrine/annotations": {
+ "version": "1.13",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "1.0",
+ "ref": "a2759dd6123694c8d901d0ec80006e044c2e6457"
+ },
+ "files": [
+ "config/routes/annotations.yaml"
+ ]
+ },
+ "doctrine/cache": {
+ "version": "2.1.1"
+ },
+ "doctrine/collections": {
+ "version": "1.6.8"
+ },
+ "doctrine/common": {
+ "version": "3.2.2"
+ },
+ "doctrine/dbal": {
+ "version": "3.3.2"
+ },
+ "doctrine/deprecations": {
+ "version": "v0.5.3"
+ },
+ "doctrine/doctrine-bundle": {
+ "version": "2.5",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "2.4",
+ "ref": "06a5dfe6c1b12da89276b72ed281be757d24c8a0"
+ },
+ "files": [
+ "config/packages/doctrine.yaml",
+ "src/Entity/.gitignore",
+ "src/Repository/.gitignore"
+ ]
+ },
+ "doctrine/event-manager": {
+ "version": "1.1.1"
+ },
+ "doctrine/inflector": {
+ "version": "2.0.4"
+ },
+ "doctrine/instantiator": {
+ "version": "1.4.0"
+ },
+ "doctrine/lexer": {
+ "version": "1.2.2"
+ },
+ "doctrine/orm": {
+ "version": "2.11.1"
+ },
+ "doctrine/persistence": {
+ "version": "2.3.0"
+ },
+ "doctrine/sql-formatter": {
+ "version": "1.1.2"
+ },
+ "myclabs/deep-copy": {
+ "version": "1.10.2"
+ },
+ "nikic/php-parser": {
+ "version": "v4.13.2"
+ },
+ "paragonie/constant_time_encoding": {
+ "version": "v2.5.0"
+ },
+ "paragonie/halite": {
+ "version": "v4.8.0"
+ },
+ "paragonie/hidden-string": {
+ "version": "v2.0.0"
+ },
+ "paragonie/random_compat": {
+ "version": "v9.99.100"
+ },
+ "paragonie/sodium_compat": {
+ "version": "v1.17.0"
+ },
+ "phar-io/manifest": {
+ "version": "2.0.3"
+ },
+ "phar-io/version": {
+ "version": "3.1.1"
+ },
+ "phpdocumentor/reflection-common": {
+ "version": "2.2.0"
+ },
+ "phpdocumentor/reflection-docblock": {
+ "version": "5.3.0"
+ },
+ "phpdocumentor/type-resolver": {
+ "version": "1.6.0"
+ },
+ "phpspec/prophecy": {
+ "version": "v1.15.0"
+ },
+ "phpunit/php-code-coverage": {
+ "version": "9.2.10"
+ },
+ "phpunit/php-file-iterator": {
+ "version": "3.0.6"
+ },
+ "phpunit/php-invoker": {
+ "version": "3.1.1"
+ },
+ "phpunit/php-text-template": {
+ "version": "2.0.4"
+ },
+ "phpunit/php-timer": {
+ "version": "5.0.3"
+ },
+ "phpunit/phpunit": {
+ "version": "9.5",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "9.3",
+ "ref": "a6249a6c4392e9169b87abf93225f7f9f59025e6"
+ },
+ "files": [
+ ".env.test",
+ "phpunit.xml.dist",
+ "tests/bootstrap.php"
+ ]
+ },
+ "psr/cache": {
+ "version": "3.0.0"
+ },
+ "psr/container": {
+ "version": "2.0.2"
+ },
+ "psr/event-dispatcher": {
+ "version": "1.0.0"
+ },
+ "psr/log": {
+ "version": "3.0.0"
+ },
+ "sebastian/cli-parser": {
+ "version": "1.0.1"
+ },
+ "sebastian/code-unit": {
+ "version": "1.0.8"
+ },
+ "sebastian/code-unit-reverse-lookup": {
+ "version": "2.0.3"
+ },
+ "sebastian/comparator": {
+ "version": "4.0.6"
+ },
+ "sebastian/complexity": {
+ "version": "2.0.2"
+ },
+ "sebastian/diff": {
+ "version": "4.0.4"
+ },
+ "sebastian/environment": {
+ "version": "5.1.3"
+ },
+ "sebastian/exporter": {
+ "version": "4.0.4"
+ },
+ "sebastian/global-state": {
+ "version": "5.0.4"
+ },
+ "sebastian/lines-of-code": {
+ "version": "1.0.3"
+ },
+ "sebastian/object-enumerator": {
+ "version": "4.0.4"
+ },
+ "sebastian/object-reflector": {
+ "version": "2.0.4"
+ },
+ "sebastian/recursion-context": {
+ "version": "4.0.4"
+ },
+ "sebastian/resource-operations": {
+ "version": "3.0.3"
+ },
+ "sebastian/type": {
+ "version": "2.3.4"
+ },
+ "sebastian/version": {
+ "version": "3.0.2"
+ },
+ "symfony/cache": {
+ "version": "v6.0.3"
+ },
+ "symfony/cache-contracts": {
+ "version": "v3.0.0"
+ },
+ "symfony/config": {
+ "version": "v6.0.3"
+ },
+ "symfony/console": {
+ "version": "6.0",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "5.3",
+ "ref": "da0c8be8157600ad34f10ff0c9cc91232522e047"
+ },
+ "files": [
+ "bin/console"
+ ]
+ },
+ "symfony/dependency-injection": {
+ "version": "v6.0.3"
+ },
+ "symfony/deprecation-contracts": {
+ "version": "v3.0.0"
+ },
+ "symfony/doctrine-bridge": {
+ "version": "v6.0.3"
+ },
+ "symfony/dotenv": {
+ "version": "v6.0.3"
+ },
+ "symfony/error-handler": {
+ "version": "v6.0.3"
+ },
+ "symfony/event-dispatcher": {
+ "version": "v6.0.3"
+ },
+ "symfony/event-dispatcher-contracts": {
+ "version": "v3.0.0"
+ },
+ "symfony/filesystem": {
+ "version": "v6.0.3"
+ },
+ "symfony/finder": {
+ "version": "v6.0.3"
+ },
+ "symfony/flex": {
+ "version": "2.1",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "1.0",
+ "ref": "c0eeb50665f0f77226616b6038a9b06c03752d8e"
+ },
+ "files": [
+ ".env"
+ ]
+ },
+ "symfony/framework-bundle": {
+ "version": "6.0",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "5.4",
+ "ref": "3cd216a4d007b78d8554d44a5b1c0a446dab24fb"
+ },
+ "files": [
+ "config/packages/cache.yaml",
+ "config/packages/framework.yaml",
+ "config/preload.php",
+ "config/routes/framework.yaml",
+ "config/services.yaml",
+ "public/index.php",
+ "src/Controller/.gitignore",
+ "src/Kernel.php"
+ ]
+ },
+ "symfony/http-foundation": {
+ "version": "v6.0.3"
+ },
+ "symfony/http-kernel": {
+ "version": "v6.0.4"
+ },
+ "symfony/maker-bundle": {
+ "version": "1.36",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "1.0",
+ "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
+ }
+ },
+ "symfony/phpunit-bridge": {
+ "version": "6.0",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "5.3",
+ "ref": "97cb3dc7b0f39c7cfc4b7553504c9d7b7795de96"
+ },
+ "files": [
+ ".env.test",
+ "bin/phpunit",
+ "phpunit.xml.dist",
+ "tests/bootstrap.php"
+ ]
+ },
+ "symfony/polyfill-intl-grapheme": {
+ "version": "v1.24.0"
+ },
+ "symfony/polyfill-intl-normalizer": {
+ "version": "v1.24.0"
+ },
+ "symfony/polyfill-mbstring": {
+ "version": "v1.24.0"
+ },
+ "symfony/polyfill-php81": {
+ "version": "v1.24.0"
+ },
+ "symfony/property-access": {
+ "version": "v6.0.3"
+ },
+ "symfony/property-info": {
+ "version": "v6.0.3"
+ },
+ "symfony/routing": {
+ "version": "6.0",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "6.0",
+ "ref": "ab9ad892b7bba7ac584f6dc2ccdb659d358c63c5"
+ },
+ "files": [
+ "config/packages/routing.yaml",
+ "config/routes.yaml"
+ ]
+ },
+ "symfony/runtime": {
+ "version": "v6.0.3"
+ },
+ "symfony/service-contracts": {
+ "version": "v3.0.0"
+ },
+ "symfony/string": {
+ "version": "v6.0.3"
+ },
+ "symfony/translation-contracts": {
+ "version": "v3.0.0"
+ },
+ "symfony/twig-bridge": {
+ "version": "v6.0.3"
+ },
+ "symfony/twig-bundle": {
+ "version": "6.0",
+ "recipe": {
+ "repo": "github.com/symfony/recipes",
+ "branch": "master",
+ "version": "5.4",
+ "ref": "bffbb8f1a849736e64006735afae730cb428b6ff"
+ },
+ "files": [
+ "config/packages/twig.yaml",
+ "templates/base.html.twig"
+ ]
+ },
+ "symfony/var-dumper": {
+ "version": "v6.0.3"
+ },
+ "symfony/var-exporter": {
+ "version": "v6.0.3"
+ },
+ "symfony/yaml": {
+ "version": "v6.0.3"
+ },
+ "theseer/tokenizer": {
+ "version": "1.2.1"
+ },
+ "twig/twig": {
+ "version": "v3.3.8"
+ },
+ "webmozart/assert": {
+ "version": "1.10.0"
+ }
+}
diff --git a/demo/symfony6.x/templates b/demo/symfony6.x/templates
new file mode 120000
index 00000000..e6b9ddfe
--- /dev/null
+++ b/demo/symfony6.x/templates
@@ -0,0 +1 @@
+../shared/templates
\ No newline at end of file
diff --git a/demo/symfony6.x/tests/SecretTest.php b/demo/symfony6.x/tests/SecretTest.php
new file mode 100644
index 00000000..e2783e66
--- /dev/null
+++ b/demo/symfony6.x/tests/SecretTest.php
@@ -0,0 +1,77 @@
+get('doctrine.orm.entity_manager');
+
+ // Make sure we do not store testdata
+ $entityManager->beginTransaction();
+
+ $name = 'test123';
+ $secretString = 'i am a secret string';
+
+ // Create entity to test with
+ $newSecretObject = (new $className)
+ ->setName($name)
+ ->setSecret($secretString);
+
+ $entityManager->persist($newSecretObject);
+ $entityManager->flush();
+
+ // Fetch the actual data
+ $secretRepository = $entityManager->getRepository($className);
+ $qb = $secretRepository->createQueryBuilder('s');
+ $qb->select('s')
+ ->addSelect('(s.secret) as rawSecret')
+ ->where('s.name = :name')
+ ->setParameter('name',$name)
+ ->orderBy('s.name','ASC');
+ $result = $qb->getQuery()->getSingleResult();
+
+ $actualSecretObject = $result[0];
+ $actualRawSecret = $result['rawSecret'];
+
+ self::assertInstanceOf($className,$actualSecretObject);
+ self::assertEquals($newSecretObject->getSecret(), $actualSecretObject->getSecret());
+ self::assertEquals($newSecretObject->getName(), $actualSecretObject->getName());
+ // Make sure it is encrypted
+ self::assertNotEquals($newSecretObject->getSecret(),$actualRawSecret);
+ self::assertStringEndsWith(DoctrineEncryptSubscriber::ENCRYPTION_MARKER,$actualRawSecret);
+ }
+
+ /**
+ * @covers Entity\Annotation\Secret::getSecret
+ * @covers Entity\Annotation\Secret::getName
+ */
+ public function testAnnotationSecretsAreEncryptedInDatabase()
+ {
+ $this->testSecretsAreEncryptedInDatabase(Entity\Annotation\Secret::class);
+ }
+
+ /**
+ * @covers Entity\Attribute\Secret::getSecret
+ * @covers Entity\Attribute\Secret::getName
+ * @requires PHP 8.0
+ */
+ public function testAttributeSecretsAreEncryptedInDatabase()
+ {
+ $this->testSecretsAreEncryptedInDatabase(Entity\Attribute\Secret::class);
+ }
+}
diff --git a/demo/symfony6.x/tests/bootstrap.php b/demo/symfony6.x/tests/bootstrap.php
new file mode 100644
index 00000000..469dccee
--- /dev/null
+++ b/demo/symfony6.x/tests/bootstrap.php
@@ -0,0 +1,11 @@
+bootEnv(dirname(__DIR__).'/.env');
+}
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
new file mode 100644
index 00000000..743101ea
--- /dev/null
+++ b/phpstan.neon.dist
@@ -0,0 +1,6 @@
+parameters:
+ level: 0
+ paths:
+ - src
+ - tests
+ tmpDir: .phpstan-cache
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 80037be4..e0767699 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,6 +1,6 @@
-
- ./Tests/Unit
+ ./tests/Unit
- ./Tests/Functional
+ ./tests/Functional
@@ -26,7 +26,8 @@
-
+
+
diff --git a/src/Command/AbstractCommand.php b/src/Command/AbstractCommand.php
index 489875a3..a59263cb 100644
--- a/src/Command/AbstractCommand.php
+++ b/src/Command/AbstractCommand.php
@@ -16,19 +16,19 @@
abstract class AbstractCommand extends Command
{
/**
- * @var EntityManagerInterface
+ * @var EntityManagerInterface|EntityManager
*/
- protected EntityManagerInterface|EntityManager $entityManager;
+ protected $entityManager;
/**
* @var DoctrineEncryptSubscriber
*/
- protected DoctrineEncryptSubscriber $subscriber;
+ protected $subscriber;
/**
* @var Reader
*/
- protected Reader $annotationReader;
+ protected $annotationReader;
/**
* AbstractCommand constructor.
diff --git a/src/Command/DoctrineDecryptDatabaseCommand.php b/src/Command/DoctrineDecryptDatabaseCommand.php
index 52fd45dd..e0bd9226 100644
--- a/src/Command/DoctrineDecryptDatabaseCommand.php
+++ b/src/Command/DoctrineDecryptDatabaseCommand.php
@@ -146,6 +146,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
$output->writeln('' . PHP_EOL . 'Decryption finished values found: ' . $valueCounter . ', decrypted: ' . $this->subscriber->decryptCounter . '.' . PHP_EOL . 'All values are now decrypted.');
- return 1;
+
+ return 0;
}
}
diff --git a/src/Command/DoctrineEncryptDatabaseCommand.php b/src/Command/DoctrineEncryptDatabaseCommand.php
index 7bb03502..114fef33 100644
--- a/src/Command/DoctrineEncryptDatabaseCommand.php
+++ b/src/Command/DoctrineEncryptDatabaseCommand.php
@@ -101,7 +101,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
// Say it is finished
$output->writeln('Encryption finished. Values encrypted: ' . $this->subscriber->encryptCounter . ' values.' . PHP_EOL . 'All values are now encrypted.');
- return 1;
+
+ return 0;
}
diff --git a/src/Command/DoctrineEncryptStatusCommand.php b/src/Command/DoctrineEncryptStatusCommand.php
index 4338c5bf..998060f2 100644
--- a/src/Command/DoctrineEncryptStatusCommand.php
+++ b/src/Command/DoctrineEncryptStatusCommand.php
@@ -33,7 +33,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$totalCount = 0;
foreach ($metaDataArray as $metaData) {
- if ($metaData instanceof ClassMetadataInfo and $metaData->isMappedSuperclass) {
+ if ($metaData instanceof ClassMetadataInfo && $metaData->isMappedSuperclass) {
continue;
}
@@ -53,6 +53,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$output->writeln('');
$output->writeln(sprintf('%d entities found which are containing %d encrypted properties.', count($metaDataArray), $totalCount));
- return 1;
+
+ return 0;
}
}
diff --git a/src/DependencyInjection/DoctrineEncryptExtension.php b/src/DependencyInjection/DoctrineEncryptExtension.php
index 4914ea40..d12be8e9 100644
--- a/src/DependencyInjection/DoctrineEncryptExtension.php
+++ b/src/DependencyInjection/DoctrineEncryptExtension.php
@@ -31,7 +31,7 @@ public function load(array $configs, ContainerBuilder $container)
$config = $this->processConfiguration($configuration, $configs);
// If empty encryptor class, use Halite encryptor
- if (in_array($config['encryptor_class'], array_keys(self::SupportedEncryptorClasses))) {
+ if (array_key_exists($config['encryptor_class'], self::SupportedEncryptorClasses)) {
$config['encryptor_class_full'] = self::SupportedEncryptorClasses[$config['encryptor_class']];
} else {
$config['encryptor_class_full'] = $config['encryptor_class'];
@@ -44,6 +44,11 @@ public function load(array $configs, ContainerBuilder $container)
// Load service file
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
$loader->load('services.yml');
+
+ // Remove usage of AttributeAnnotationReader when using php < 8.0
+ if (PHP_VERSION_ID < 80000) {
+ $container->setAlias('ambta_doctrine_annotation_reader','annotations.reader');
+ }
}
/**
diff --git a/src/Encryptors/DefuseEncryptor.php b/src/Encryptors/DefuseEncryptor.php
index 0079aba8..56a08350 100644
--- a/src/Encryptors/DefuseEncryptor.php
+++ b/src/Encryptors/DefuseEncryptor.php
@@ -12,9 +12,12 @@
class DefuseEncryptor implements EncryptorInterface
{
- private Filesystem $fs;
- private ?string $encryptionKey = null;
- private string $keyFile;
+ /** @var Filesystem */
+ private $fs;
+ /** @var string|null */
+ private $encryptionKey = null;
+ /** @var string */
+ private $keyFile;
/**
* {@inheritdoc}
diff --git a/src/Encryptors/HaliteEncryptor.php b/src/Encryptors/HaliteEncryptor.php
index 07653093..2c3fb979 100644
--- a/src/Encryptors/HaliteEncryptor.php
+++ b/src/Encryptors/HaliteEncryptor.php
@@ -16,8 +16,10 @@
class HaliteEncryptor implements EncryptorInterface
{
- private ?EncryptionKey $encryptionKey = null;
- private string $keyFile;
+ /** @var EncryptionKey|null */
+ private $encryptionKey = null;
+ /** @var string */
+ private $keyFile;
/**
* {@inheritdoc}
@@ -40,14 +42,7 @@ public function encrypt(string $data): string
*/
public function decrypt(string $data): string
{
- $data = Crypto::decrypt($data, $this->getKey());
-
- if ($data instanceof HiddenString)
- {
- $data = $data->getString();
- }
-
- return $data;
+ return Crypto::decrypt($data, $this->getKey())->getString();
}
private function getKey(): EncryptionKey
diff --git a/src/Mapping/AttributeAnnotationReader.php b/src/Mapping/AttributeAnnotationReader.php
index 86309e45..8b9402fa 100644
--- a/src/Mapping/AttributeAnnotationReader.php
+++ b/src/Mapping/AttributeAnnotationReader.php
@@ -17,12 +17,12 @@ final class AttributeAnnotationReader implements Reader
/**
* @var Reader
*/
- private Reader $annotationReader;
+ private $annotationReader;
/**
* @var AttributeReader
*/
- private AttributeReader $attributeReader;
+ private $attributeReader;
public function __construct(AttributeReader $attributeReader, Reader $annotationReader)
{
diff --git a/src/Mapping/AttributeReader.php b/src/Mapping/AttributeReader.php
index d8d36470..2b600fd2 100644
--- a/src/Mapping/AttributeReader.php
+++ b/src/Mapping/AttributeReader.php
@@ -14,7 +14,7 @@
final class AttributeReader
{
/** @var array */
- private array $isRepeatableAttribute = [];
+ private $isRepeatableAttribute = [];
/**
* @param ReflectionClass $class
@@ -30,7 +30,7 @@ public function getClassAnnotations(ReflectionClass $class): array
*
* @return Annotation|Annotation[]|null
*/
- public function getClassAnnotation(ReflectionClass $class, string $annotationName): array|Annotation|null
+ public function getClassAnnotation(ReflectionClass $class, string $annotationName)
{
return $this->getClassAnnotations($class)[$annotationName] ?? null;
}
@@ -49,7 +49,7 @@ public function getPropertyAnnotations(\ReflectionProperty $property): array
*
* @return Annotation|Annotation[]|null
*/
- public function getPropertyAnnotation(\ReflectionProperty $property, string $annotationName): array|Annotation|null
+ public function getPropertyAnnotation(\ReflectionProperty $property, string $annotationName)
{
return $this->getPropertyAnnotations($property)[$annotationName] ?? null;
}
@@ -100,3 +100,5 @@ private function isRepeatable(string $attributeClassName): bool
return $this->isRepeatableAttribute[$attributeClassName] = ($attribute->flags & Attribute::IS_REPEATABLE) > 0;
}
}
+
+
diff --git a/src/Subscribers/DoctrineEncryptSubscriber.php b/src/Subscribers/DoctrineEncryptSubscriber.php
index 06f05df4..fe6c0404 100644
--- a/src/Subscribers/DoctrineEncryptSubscriber.php
+++ b/src/Subscribers/DoctrineEncryptSubscriber.php
@@ -2,6 +2,8 @@
namespace Ambta\DoctrineEncryptBundle\Subscribers;
+use Doctrine\ORM\EntityManagerInterface;
+use Doctrine\ORM\Event\OnClearEventArgs;
use Doctrine\ORM\Event\PreFlushEventArgs;
use ReflectionClass;
use Doctrine\ORM\Event\PostFlushEventArgs;
@@ -40,34 +42,34 @@ class DoctrineEncryptSubscriber implements EventSubscriber
* Encryptor
* @var EncryptorInterface|null
*/
- private ?EncryptorInterface $encryptor;
+ private $encryptor;
/**
* Annotation reader
* @var Reader
*/
- private Reader $annReader;
+ private $annReader;
/**
* Used for restoring the encryptor after changing it
* @var EncryptorInterface|string
*/
- private EncryptorInterface|string $restoreEncryptor;
+ private $restoreEncryptor;
/**
* Count amount of decrypted values in this service
* @var integer
*/
- public int $decryptCounter = 0;
+ public $decryptCounter = 0;
/**
* Count amount of encrypted values in this service
* @var integer
*/
- public int $encryptCounter = 0;
+ public $encryptCounter = 0;
/** @var array */
- private array $cachedDecryptions = [];
+ private $cachedDecryptions = [];
/**
* Initialization of subscriber
@@ -122,7 +124,7 @@ public function restoreEncryptor()
public function postUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
- $this->processFields($entity, false);
+ $this->processFields($entity, $args->getEntityManager(), false);
}
/**
@@ -134,7 +136,7 @@ public function postUpdate(LifecycleEventArgs $args)
public function preUpdate(PreUpdateEventArgs $args)
{
$entity = $args->getEntity();
- $this->processFields($entity);
+ $this->processFields($entity, $args->getEntityManager(), true);
}
/**
@@ -146,7 +148,7 @@ public function preUpdate(PreUpdateEventArgs $args)
public function postLoad(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
- $this->processFields($entity, false);
+ $this->processFields($entity, $args->getEntityManager(), false);
}
/**
@@ -161,7 +163,7 @@ public function preFlush(PreFlushEventArgs $preFlushEventArgs)
foreach ($unitOfWOrk->getIdentityMap() as $entityName => $entityArray) {
if (isset($this->cachedDecryptions[$entityName])) {
foreach ($entityArray as $entityId => $instance) {
- $this->processFields($instance);
+ $this->processFields($instance, $preFlushEventArgs->getEntityManager(), true);
}
}
}
@@ -179,7 +181,7 @@ public function onFlush(OnFlushEventArgs $onFlushEventArgs)
$unitOfWork = $onFlushEventArgs->getEntityManager()->getUnitOfWork();
foreach ($unitOfWork->getScheduledEntityInsertions() as $entity) {
$encryptCounterBefore = $this->encryptCounter;
- $this->processFields($entity);
+ $this->processFields($entity,$onFlushEventArgs->getEntityManager(),true);
if ($this->encryptCounter > $encryptCounterBefore ) {
$classMetadata = $onFlushEventArgs->getEntityManager()->getClassMetadata(get_class($entity));
$unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity);
@@ -198,11 +200,19 @@ public function postFlush(PostFlushEventArgs $postFlushEventArgs)
$unitOfWork = $postFlushEventArgs->getEntityManager()->getUnitOfWork();
foreach ($unitOfWork->getIdentityMap() as $entityMap) {
foreach ($entityMap as $entity) {
- $this->processFields($entity, false);
+ $classMetadata = $postFlushEventArgs->getEntityManager()->getClassMetadata(get_class($entity));
+ $this->processFields($entity,$postFlushEventArgs->getEntityManager(), false);
}
}
}
+ public function onClear(OnClearEventArgs $onClearEventArgs)
+ {
+ $this->cachedDecryptions = [];
+ $this->decryptCounter = 0;
+ $this->encryptCounter = 0;
+ }
+
/**
* Realization of EventSubscriber interface method.
*
@@ -217,6 +227,7 @@ public function getSubscribedEvents(): array
Events::onFlush,
Events::preFlush,
Events::postFlush,
+ Events::onClear,
);
}
@@ -224,13 +235,14 @@ public function getSubscribedEvents(): array
* Process (encrypt/decrypt) entities fields
*
* @param Object $entity doctrine entity
+ * @param EntityManagerInterface $entityManager
* @param Boolean $isEncryptOperation If true - encrypt, false - decrypt entity
*
* @return object|null
*@throws \RuntimeException
*
*/
- public function processFields(object $entity, bool $isEncryptOperation = true): ?object
+ public function processFields(object $entity, EntityManagerInterface $entityManager, bool $isEncryptOperation = true): ?object
{
if (!empty($this->encryptor)) {
// Check which operation to be used
@@ -244,7 +256,7 @@ public function processFields(object $entity, bool $isEncryptOperation = true):
// Foreach property in the reflection class
foreach ($properties as $refProperty) {
if ($this->annReader->getPropertyAnnotation($refProperty, 'Doctrine\ORM\Mapping\Embedded')) {
- $this->handleEmbeddedAnnotation($entity, $refProperty, $isEncryptOperation);
+ $this->handleEmbeddedAnnotation($entity, $entityManager, $refProperty, $isEncryptOperation);
continue;
}
@@ -252,21 +264,23 @@ public function processFields(object $entity, bool $isEncryptOperation = true):
* If property is an normal value and contains the Encrypt tag, lets encrypt/decrypt that property
*/
if ($this->annReader->getPropertyAnnotation($refProperty, self::ENCRYPTED_ANN_NAME)) {
+ $rootEntityName = $entityManager->getClassMetadata(get_class($entity))->rootEntityName;
+
$pac = PropertyAccess::createPropertyAccessor();
$value = $pac->getValue($entity, $refProperty->getName());
- if ($encryptorMethod == 'decrypt') {
+ if ($encryptorMethod === 'decrypt') {
if (!is_null($value) and !empty($value)) {
if (substr($value, -strlen(self::ENCRYPTION_MARKER)) == self::ENCRYPTION_MARKER) {
$this->decryptCounter++;
$currentPropValue = $this->encryptor->decrypt(substr($value, 0, -5));
$pac->setValue($entity, $refProperty->getName(), $currentPropValue);
- $this->cachedDecryptions[get_class($entity)][spl_object_id($entity)][$refProperty->getName()][$currentPropValue] = $value;
+ $this->cachedDecryptions[$rootEntityName][spl_object_id($entity)][$refProperty->getName()][$currentPropValue] = $value;
}
}
} else {
if (!is_null($value) and !empty($value)) {
- if (isset($this->cachedDecryptions[get_class($entity)][spl_object_id($entity)][$refProperty->getName()][$value])) {
- $pac->setValue($entity, $refProperty->getName(), $this->cachedDecryptions[get_class($entity)][spl_object_id($entity)][$refProperty->getName()][$value]);
+ if (isset($this->cachedDecryptions[$rootEntityName][spl_object_id($entity)][$refProperty->getName()][$value])) {
+ $pac->setValue($entity, $refProperty->getName(), $this->cachedDecryptions[$rootEntityName][spl_object_id($entity)][$refProperty->getName()][$value]);
} elseif (substr($value, -strlen(self::ENCRYPTION_MARKER)) != self::ENCRYPTION_MARKER) {
$this->encryptCounter++;
$currentPropValue = $this->encryptor->encrypt($value).self::ENCRYPTION_MARKER;
@@ -283,7 +297,7 @@ public function processFields(object $entity, bool $isEncryptOperation = true):
return $entity;
}
- private function handleEmbeddedAnnotation($entity, ReflectionProperty $embeddedProperty, bool $isEncryptOperation = true)
+ private function handleEmbeddedAnnotation($entity, EntityManagerInterface $entityManager, ReflectionProperty $embeddedProperty, bool $isEncryptOperation = true)
{
$propName = $embeddedProperty->getName();
@@ -292,7 +306,7 @@ private function handleEmbeddedAnnotation($entity, ReflectionProperty $embeddedP
$embeddedEntity = $pac->getValue($entity, $propName);
if ($embeddedEntity) {
- $this->processFields($embeddedEntity, $isEncryptOperation);
+ $this->processFields($embeddedEntity, $entityManager, $isEncryptOperation);
}
}
diff --git a/tests/DoctrineCompatibilityTrait.php b/tests/DoctrineCompatibilityTrait.php
new file mode 100644
index 00000000..071cce40
--- /dev/null
+++ b/tests/DoctrineCompatibilityTrait.php
@@ -0,0 +1,36 @@
+executeQuery()->fetchAllAssociative();
+ } else {
+ $statement->execute();
+ return $statement->fetchAll();
+ }
+ }
+
+ /**
+ * Execute statement and fetch singe row
+ *
+ * Helper-method since methods changed in different supported versions of Doctrine
+ */
+ private function executeStatementFetch(\Doctrine\DBAL\Statement $statement)
+ {
+ if (method_exists($statement,'executeQuery')) {
+ return $statement->executeQuery()->fetchAssociative();
+ } else {
+ $statement->execute();
+ return $statement->fetch();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tests/Functional/AbstractFunctionalTestCase.php b/tests/Functional/AbstractFunctionalTestCase.php
similarity index 89%
rename from Tests/Functional/AbstractFunctionalTestCase.php
rename to tests/Functional/AbstractFunctionalTestCase.php
index 97fc9384..d566a020 100644
--- a/Tests/Functional/AbstractFunctionalTestCase.php
+++ b/tests/Functional/AbstractFunctionalTestCase.php
@@ -90,11 +90,11 @@ protected function getLatestInsertQuery(): ?array
protected function getLatestUpdateQuery(): ?array
{
- $insertQueries = array_values(array_filter($this->sqlLoggerStack->queries,static function ($queryData) {
+ $updateQueries = array_values(array_filter($this->sqlLoggerStack->queries,static function ($queryData) {
return stripos($queryData['sql'], 'UPDATE ') === 0;
}));
- return current(array_reverse($insertQueries)) ?: null;
+ return current(array_reverse($updateQueries)) ?: null;
}
/**
@@ -105,6 +105,11 @@ protected function getCurrentQueryCount(): int
return count($this->sqlLoggerStack->queries);
}
+ protected function resetQueryStack(): void
+ {
+ $this->sqlLoggerStack->queries = [];
+ }
+
/**
* Asserts that a string starts with a given prefix.
*
@@ -114,17 +119,9 @@ protected function getCurrentQueryCount(): int
*/
public function assertStringDoesNotContain($needle, $string, $ignoreCase = false, $message = ''): void
{
- if (!\is_string($needle)) {
- throw InvalidArgumentHelper::factory(1, 'string');
- }
-
- if (!\is_string($string)) {
- throw InvalidArgumentHelper::factory(2, 'string');
- }
-
- if (!\is_bool($ignoreCase)) {
- throw InvalidArgumentHelper::factory(3, 'bool');
- }
+ $this->assertIsString($needle,$message);
+ $this->assertIsString($string,$message);
+ $this->assertIsBool($ignoreCase,$message);
$constraint = new LogicalNot(new StringContains(
$needle,
diff --git a/Tests/Functional/BasicQueryTest/AbstractBasicQueryTestCase.php b/tests/Functional/BasicQueryTest/AbstractBasicQueryTestCase.php
similarity index 78%
rename from Tests/Functional/BasicQueryTest/AbstractBasicQueryTestCase.php
rename to tests/Functional/BasicQueryTest/AbstractBasicQueryTestCase.php
index 0065311c..316b4979 100644
--- a/Tests/Functional/BasicQueryTest/AbstractBasicQueryTestCase.php
+++ b/tests/Functional/BasicQueryTest/AbstractBasicQueryTestCase.php
@@ -5,6 +5,7 @@
use Ambta\DoctrineEncryptBundle\Subscribers\DoctrineEncryptSubscriber;
use Ambta\DoctrineEncryptBundle\Tests\Functional\AbstractFunctionalTestCase;
use Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity\CascadeTarget;
+use Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity\VehicleCar;
abstract class AbstractBasicQueryTestCase extends AbstractFunctionalTestCase
{
@@ -81,4 +82,27 @@ public function testStoredDataIsEncrypted(): void
$this->assertStringEndsWith(DoctrineEncryptSubscriber::ENCRYPTION_MARKER,$passwordData);
$this->assertStringDoesNotContain('my secret',$passwordData);
}
+
+ public function testNoUpdateForUnalteredChildrenOfAbstractEntities()
+ {
+ $car = new VehicleCar();
+ $car->setSecret('top secret information');
+ $car->setNotSecret('123-test');
+ $this->entityManager->persist($car);
+ $this->entityManager->flush();
+
+ // start transaction, insert, commit
+ $this->assertEquals(3,$this->getCurrentQueryCount());
+
+ // Remove all logged queries
+ $this->resetQueryStack();
+
+ // Set NotSecret with same data - this does not modify the entity and should not trigger an update
+ $car->setNotSecret('123-test');
+ $this->entityManager->flush();
+
+ // Verify there are no queries executed
+ $this->assertNull($this->getLatestUpdateQuery());
+ $this->assertEquals(0,$this->getCurrentQueryCount());
+ }
}
diff --git a/Tests/Functional/BasicQueryTest/BasicQueryDefuseTest.php b/tests/Functional/BasicQueryTest/BasicQueryDefuseTest.php
similarity index 100%
rename from Tests/Functional/BasicQueryTest/BasicQueryDefuseTest.php
rename to tests/Functional/BasicQueryTest/BasicQueryDefuseTest.php
diff --git a/Tests/Functional/BasicQueryTest/BasicQueryHaliteTest.php b/tests/Functional/BasicQueryTest/BasicQueryHaliteTest.php
similarity index 100%
rename from Tests/Functional/BasicQueryTest/BasicQueryHaliteTest.php
rename to tests/Functional/BasicQueryTest/BasicQueryHaliteTest.php
diff --git a/Tests/Functional/DoctrineEncryptSubscriber/AbstractDoctrineEncryptSubscriberTestCase.php b/tests/Functional/DoctrineEncryptSubscriber/AbstractDoctrineEncryptSubscriberTestCase.php
similarity index 51%
rename from Tests/Functional/DoctrineEncryptSubscriber/AbstractDoctrineEncryptSubscriberTestCase.php
rename to tests/Functional/DoctrineEncryptSubscriber/AbstractDoctrineEncryptSubscriberTestCase.php
index 2c11b33f..6a2c57fe 100644
--- a/Tests/Functional/DoctrineEncryptSubscriber/AbstractDoctrineEncryptSubscriberTestCase.php
+++ b/tests/Functional/DoctrineEncryptSubscriber/AbstractDoctrineEncryptSubscriberTestCase.php
@@ -3,14 +3,19 @@
namespace Ambta\DoctrineEncryptBundle\Tests\Functional\DoctrineEncryptSubscriber;
+use Ambta\DoctrineEncryptBundle\Tests\DoctrineCompatibilityTrait;
use Ambta\DoctrineEncryptBundle\Tests\Functional\AbstractFunctionalTestCase;
use Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity\CascadeTarget;
+use Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity\ClassTableInheritanceBase;
+use Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity\ClassTableInheritanceChild;
use Ambta\DoctrineEncryptBundle\Tests\Functional\fixtures\Entity\Owner;
use Doctrine\DBAL\Logging\DebugStack;
abstract class AbstractDoctrineEncryptSubscriberTestCase extends AbstractFunctionalTestCase
{
+ use DoctrineCompatibilityTrait;
+
public function testEncryptionHappensOnOnlyAnnotatedFields(): void
{
$secret = "It's a secret";
@@ -33,8 +38,7 @@ public function testEncryptionHappensOnOnlyAnnotatedFields(): void
$this->assertEquals($secret, $owner->getSecret());
$this->assertEquals($notSecret, $owner->getNotSecret());
$stmt->bindValue(1, $owner->getId());
- $stmt->execute();
- $results = $stmt->fetchAll();
+ $results = $this->executeStatementFetchAll($stmt);
$this->assertCount(1, $results);
$result = $results[0];
$this->assertEquals($notSecret, $result['notSecret']);
@@ -71,8 +75,7 @@ public function testEncryptionCascades(): void
$this->assertEquals($secret, $cascadeTarget->getSecret());
$this->assertEquals($notSecret, $cascadeTarget->getNotSecret());
$stmt->bindValue(1, $cascadeTarget->getId());
- $stmt->execute();
- $results = $stmt->fetchAll();
+ $results = $this->executeStatementFetchAll($stmt);
$this->assertCount(1, $results);
$result = $results[0];
$this->assertEquals($notSecret, $result['notSecret']);
@@ -82,11 +85,58 @@ public function testEncryptionCascades(): void
$this->assertEquals($secret, $decrypted);
}
+ public function testEncryptionClassTableInheritance(): void
+ {
+ $secretBase = "It's a secret. On the base class.";
+ $notSecretBase = "You're all welcome to know this. On the base class.";
+ $secretChild = "It's a secret. On the child class.";
+ $notSecretChild = "You're all welcome to know this. On the child class.";
+ $em = $this->entityManager;
+ $child = new ClassTableInheritanceChild();
+ $child->setSecretBase($secretBase);
+ $child->setNotSecretBase($notSecretBase);
+ $child->setSecretChild($secretChild);
+ $child->setNotSecretChild($notSecretChild);
+ $em->persist($child);
+ $em->flush();
+ $em->clear();
+ unset($child);
+
+ $connection = $em->getConnection();
+ $stmtBase = $connection->prepare('SELECT * from classTableInheritanceBase WHERE id = ?');
+ $stmtChild = $connection->prepare('SELECT * from classTableInheritanceChild WHERE id = ?');
+ $childs = $em->getRepository(ClassTableInheritanceBase::class)->findAll();
+ self::assertCount(1, $childs);
+ /** @var ClassTableInheritanceChild $child */
+ $child = $childs[0];
+ self::assertEquals($secretBase, $child->getSecretBase());
+ self::assertEquals($notSecretBase, $child->getNotSecretBase());
+ self::assertEquals($secretChild, $child->getSecretChild());
+ self::assertEquals($notSecretChild, $child->getNotSecretChild());
+
+ // Now check that the fields are encrypted in the database. First the base table.
+ $stmtBase->bindValue(1, $child->getId());
+ $results = $this->executeStatementFetchAll($stmtBase);
+ self::assertCount(1, $results);
+ $result = $results[0];
+ self::assertEquals($notSecretBase, $result['notSecretBase']);
+ self::assertNotEquals($secretBase, $result['secretBase']);
+ self::assertStringEndsWith('', $result['secretBase']);
+ $decrypted = $this->encryptor->decrypt(str_replace('', '', $result['secretBase']));
+ self::assertEquals($secretBase, $decrypted);
+
+ // and then the child table.
+ $stmtChild->bindValue(1, $child->getId());
+ $results = $this->executeStatementFetchAll($stmtChild);
+ self::assertCount(1, $results);
+ $result = $results[0];
+ self::assertEquals($notSecretChild, $result['notSecretChild']);
+ self::assertNotEquals($secretChild, $result['secretChild']);
+ self::assertStringEndsWith('', $result['secretChild']);
+ $decrypted = $this->encryptor->decrypt(str_replace('', '', $result['secretChild']));
+ self::assertEquals($secretChild, $decrypted);
+ }
- /**
- * @throws \Doctrine\DBAL\DBALException
- * @throws \Doctrine\ORM\OptimisticLockException
- */
public function testEncryptionDoesNotHappenWhenThereIsNoChange(): void
{
$secret = "It's a secret";
@@ -111,8 +161,7 @@ public function testEncryptionDoesNotHappenWhenThereIsNoChange(): void
$connection = $em->getConnection();
$stmt = $connection->prepare('SELECT * from owner WHERE id = ?');
$stmt->bindValue(1, $owner1Id);
- $stmt->execute();
- $results = $stmt->fetchAll();
+ $results = $this->executeStatementFetchAll($stmt);
$this->assertCount(1, $results);
$result = $results[0];
$originalEncryption = $result['secret'];
@@ -145,8 +194,7 @@ public function testEncryptionDoesNotHappenWhenThereIsNoChange(): void
$this->assertCount(0, $stack->queries, "Unexpected queries:\n".var_export($stack->queries, true));
$stmt->bindValue(1, $owner1Id);
- $stmt->execute();
- $results = $stmt->fetchAll();
+ $results = $this->executeStatementFetchAll($stmt);
$this->assertCount(1, $results);
$result = $results[0];
$shouldBeTheSameAsBefore = $result['secret'];
@@ -155,6 +203,81 @@ public function testEncryptionDoesNotHappenWhenThereIsNoChange(): void
}
+ public function testEncryptionDoesNotHappenWhenThereIsNoChangeClassInheritance(): void
+ {
+ $secretBase = "It's a secret. On the base class.";
+ $notSecretBase = "You're all welcome to know this. On the base class.";
+ $secretChild = "It's a secret. On the child class.";
+ $notSecretChild = "You're all welcome to know this. On the child class.";
+ $em = $this->entityManager;
+ $child = new ClassTableInheritanceChild();
+ $child->setSecretBase($secretBase);
+ $child->setNotSecretBase($notSecretBase);
+ $child->setSecretChild($secretChild);
+ $child->setNotSecretChild($notSecretChild);
+ $em->persist($child);
+ $em->flush();
+ $em->clear();
+ $childId = $child->getId();
+ unset($child);
+
+
+ // test that it was encrypted correctly
+ $connection = $em->getConnection();
+ $stmtBase = $connection->prepare('SELECT * from classTableInheritanceBase WHERE id = ?');
+ $stmtBase->bindValue(1, $childId);
+ $result = $this->executeStatementFetch($stmtBase);
+ $originalEncryptionBase = $result['secretBase'];
+ self::assertStringEndsWith('', $originalEncryptionBase); // is encrypted
+
+ // do the same for the child.
+ $connection = $em->getConnection();
+ $stmtChild = $connection->prepare('SELECT * from classTableInheritanceChild WHERE id = ?');
+ $stmtChild->bindValue(1, $childId);
+ $result = $this->executeStatementFetch($stmtChild);
+ $originalEncryptionChild = $result['secretChild'];
+ self::assertStringEndsWith('', $originalEncryptionChild); // is encrypted
+
+ $childs = $em->getRepository(ClassTableInheritanceChild::class)->findAll();
+ $child = $childs[0];
+ self::assertEquals($secretBase, $child->getSecretBase());
+ self::assertEquals($notSecretBase, $child->getNotSecretBase());
+ self::assertEquals($secretChild, $child->getSecretChild());
+ self::assertEquals($notSecretChild, $child->getNotSecretChild());
+
+ $stack = new DebugStack();
+ $connection->getConfiguration()->setSQLLogger($stack);
+ self::assertCount(0, $stack->queries);
+ $beforeFlush = $this->subscriber->encryptCounter;
+ $em->flush();
+ $afterFlush = $this->subscriber->encryptCounter;
+ // No encryption should have happened because we didn't change anything.
+ self::assertEquals($beforeFlush, $afterFlush);
+ // No queries happened because we didn't change anything.
+ self::assertCount(0, $stack->queries, "Unexpected queries:\n" . var_export($stack->queries, true));
+
+ // flush again
+ $beforeFlush = $this->subscriber->encryptCounter;
+ $em->flush();
+ $afterFlush = $this->subscriber->encryptCounter;
+ // No encryption should have happened because we didn't change anything.
+ self::assertEquals($beforeFlush, $afterFlush);
+ // No queries happened because we didn't change anything.
+ self::assertCount(0, $stack->queries, "Unexpected queries:\n" . var_export($stack->queries, true));
+
+ $stmtBase->bindValue(1, $childId);
+ $result = $this->executeStatementFetch($stmtBase);
+ $shouldBeTheSameAsBeforeBase = $result['secretBase'];
+ self::assertStringEndsWith('', $shouldBeTheSameAsBeforeBase); // is encrypted
+ self::assertEquals($originalEncryptionBase, $shouldBeTheSameAsBeforeBase);
+
+ $stmtChild->bindValue(1, $childId);
+ $result = $this->executeStatementFetch($stmtChild);
+ $shouldBeTheSameAsBeforeChild = $result['secretChild'];
+ self::assertStringEndsWith('', $shouldBeTheSameAsBeforeChild); // is encrypted
+ self::assertEquals($originalEncryptionChild, $shouldBeTheSameAsBeforeChild);
+ }
+
public function testEncryptionDoesHappenWhenASecretIsChanged(): void
{
$secret = "It's a secret";
@@ -173,8 +296,7 @@ public function testEncryptionDoesHappenWhenASecretIsChanged(): void
$connection = $em->getConnection();
$stmt = $connection->prepare('SELECT * from owner WHERE id = ?');
$stmt->bindValue(1, $ownerId);
- $stmt->execute();
- $results = $stmt->fetchAll();
+ $results = $this->executeStatementFetchAll($stmt);
$this->assertCount(1, $results);
$result = $results[0];
$originalEncryption = $result['secret'];
@@ -190,8 +312,7 @@ public function testEncryptionDoesHappenWhenASecretIsChanged(): void
$this->assertGreaterThan($beforeFlush, $afterFlush);
$stmt->bindValue(1, $ownerId);
- $stmt->execute();
- $results = $stmt->fetchAll();
+ $results = $this->executeStatementFetchAll($stmt);
$this->assertCount(1, $results);
$result = $results[0];
$shouldBeDifferentFromBefore = $result['secret'];
diff --git a/Tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberDefuseTestCase.php b/tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberDefuseTest.php
similarity index 65%
rename from Tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberDefuseTestCase.php
rename to tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberDefuseTest.php
index 70cc47e7..346b8e3c 100644
--- a/Tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberDefuseTestCase.php
+++ b/tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberDefuseTest.php
@@ -4,9 +4,8 @@
use Ambta\DoctrineEncryptBundle\Encryptors\DefuseEncryptor;
use Ambta\DoctrineEncryptBundle\Encryptors\EncryptorInterface;
-use Ambta\DoctrineEncryptBundle\Tests\Functional\BasicQueryTest\AbstractBasicQueryTestCase;
-class DoctrineEncryptSubscriberDefuseTestCase extends AbstractDoctrineEncryptSubscriberTestCase
+class DoctrineEncryptSubscriberDefuseTest extends AbstractDoctrineEncryptSubscriberTestCase
{
protected function getEncryptor(): EncryptorInterface
{
diff --git a/Tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberHaliteTestCase.php b/tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberHaliteTest.php
similarity index 76%
rename from Tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberHaliteTestCase.php
rename to tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberHaliteTest.php
index 02fc8926..27db9ca6 100644
--- a/Tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberHaliteTestCase.php
+++ b/tests/Functional/DoctrineEncryptSubscriber/DoctrineEncryptSubscriberHaliteTest.php
@@ -4,9 +4,8 @@
use Ambta\DoctrineEncryptBundle\Encryptors\EncryptorInterface;
use Ambta\DoctrineEncryptBundle\Encryptors\HaliteEncryptor;
-use Ambta\DoctrineEncryptBundle\Tests\Functional\BasicQueryTest\AbstractBasicQueryTestCase;
-class DoctrineEncryptSubscriberHaliteTestCase extends AbstractDoctrineEncryptSubscriberTestCase
+class DoctrineEncryptSubscriberHaliteTest extends AbstractDoctrineEncryptSubscriberTestCase
{
protected function getEncryptor(): EncryptorInterface
{
diff --git a/tests/Functional/fixtures/Entity/AbstractVehicle.php b/tests/Functional/fixtures/Entity/AbstractVehicle.php
new file mode 100644
index 00000000..71d7db97
--- /dev/null
+++ b/tests/Functional/fixtures/Entity/AbstractVehicle.php
@@ -0,0 +1,76 @@
+id;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSecret()
+ {
+ return $this->secret;
+ }
+
+ /**
+ * @param mixed $secret
+ */
+ public function setSecret($secret): void
+ {
+ $this->secret = $secret;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getNotSecret()
+ {
+ return $this->notSecret;
+ }
+
+ /**
+ * @param mixed $notSecret
+ * @return $this
+ */
+ public function setNotSecret($notSecret): self
+ {
+ $this->notSecret = $notSecret;
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/Tests/Functional/fixtures/Entity/CascadeTarget.php b/tests/Functional/fixtures/Entity/CascadeTarget.php
similarity index 100%
rename from Tests/Functional/fixtures/Entity/CascadeTarget.php
rename to tests/Functional/fixtures/Entity/CascadeTarget.php
diff --git a/tests/Functional/fixtures/Entity/ClassTableInheritanceBase.php b/tests/Functional/fixtures/Entity/ClassTableInheritanceBase.php
new file mode 100644
index 00000000..e5aa089d
--- /dev/null
+++ b/tests/Functional/fixtures/Entity/ClassTableInheritanceBase.php
@@ -0,0 +1,66 @@
+id;
+ }
+
+ public function getSecretBase()
+ {
+ return $this->secretBase;
+ }
+
+ public function setSecretBase($secretBase)
+ {
+ $this->secretBase = $secretBase;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getNotSecretBase()
+ {
+ return $this->notSecretBase;
+ }
+
+ /**
+ * @param mixed $notSecretBase
+ */
+ public function setNotSecretBase($notSecretBase)
+ {
+ $this->notSecretBase = $notSecretBase;
+ }
+
+}
\ No newline at end of file
diff --git a/tests/Functional/fixtures/Entity/ClassTableInheritanceChild.php b/tests/Functional/fixtures/Entity/ClassTableInheritanceChild.php
new file mode 100644
index 00000000..7842f72f
--- /dev/null
+++ b/tests/Functional/fixtures/Entity/ClassTableInheritanceChild.php
@@ -0,0 +1,49 @@
+secretChild;
+ }
+
+ public function setSecretChild($secretChild)
+ {
+ $this->secretChild = $secretChild;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getNotSecretChild()
+ {
+ return $this->notSecretChild;
+ }
+
+ /**
+ * @param mixed $notSecretChild
+ */
+ public function setNotSecretChild($notSecretChild)
+ {
+ $this->notSecretChild = $notSecretChild;
+ }
+
+}
\ No newline at end of file
diff --git a/Tests/Functional/fixtures/Entity/Owner.php b/tests/Functional/fixtures/Entity/Owner.php
similarity index 99%
rename from Tests/Functional/fixtures/Entity/Owner.php
rename to tests/Functional/fixtures/Entity/Owner.php
index b3c3b419..3b0faa9c 100644
--- a/Tests/Functional/fixtures/Entity/Owner.php
+++ b/tests/Functional/fixtures/Entity/Owner.php
@@ -39,8 +39,6 @@ class Owner
*/
private $cascaded;
-
-
public function getId()
{
return $this->id;
@@ -88,5 +86,4 @@ public function setCascaded($cascaded)
$this->cascaded = $cascaded;
}
-
}
\ No newline at end of file
diff --git a/tests/Functional/fixtures/Entity/VehicleBicycle.php b/tests/Functional/fixtures/Entity/VehicleBicycle.php
new file mode 100644
index 00000000..5865614d
--- /dev/null
+++ b/tests/Functional/fixtures/Entity/VehicleBicycle.php
@@ -0,0 +1,36 @@
+hasSidewheels;
+ }
+
+ /**
+ * @param bool $hasSidewheels
+ * @return $this
+ */
+ public function setSidewheels($hasSidewheels): self
+ {
+ $this->hasSidewheels = $hasSidewheels;
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/tests/Functional/fixtures/Entity/VehicleCar.php b/tests/Functional/fixtures/Entity/VehicleCar.php
new file mode 100644
index 00000000..1db05b26
--- /dev/null
+++ b/tests/Functional/fixtures/Entity/VehicleCar.php
@@ -0,0 +1,36 @@
+licensePlate;
+ }
+
+ /**
+ * @param string $licensePlate
+ * @return $this
+ */
+ public function setLicensePlate($licensePlate): self
+ {
+ $this->licensePlate = $licensePlate;
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/Tests/Functional/fixtures/defuse.key b/tests/Functional/fixtures/defuse.key
similarity index 100%
rename from Tests/Functional/fixtures/defuse.key
rename to tests/Functional/fixtures/defuse.key
diff --git a/Tests/Functional/fixtures/halite.key b/tests/Functional/fixtures/halite.key
similarity index 100%
rename from Tests/Functional/fixtures/halite.key
rename to tests/Functional/fixtures/halite.key
diff --git a/Tests/Unit/DependencyInjection/DoctrineEncryptExtensionTest.php b/tests/Unit/DependencyInjection/DoctrineEncryptExtensionTest.php
similarity index 98%
rename from Tests/Unit/DependencyInjection/DoctrineEncryptExtensionTest.php
rename to tests/Unit/DependencyInjection/DoctrineEncryptExtensionTest.php
index 4aced3d8..bd4c1f33 100644
--- a/Tests/Unit/DependencyInjection/DoctrineEncryptExtensionTest.php
+++ b/tests/Unit/DependencyInjection/DoctrineEncryptExtensionTest.php
@@ -49,8 +49,6 @@ public function testConfigLoadCustom(): void
];
$this->extension->load([$config], $container);
- $this->markTestSkipped();
-
$this->assertSame(self::class, $container->getParameter('ambta_doctrine_encrypt.encryptor_class_name'));
}
diff --git a/Tests/Unit/Encryptors/DefuseEncryptorTest.php b/tests/Unit/Encryptors/DefuseEncryptorTest.php
similarity index 100%
rename from Tests/Unit/Encryptors/DefuseEncryptorTest.php
rename to tests/Unit/Encryptors/DefuseEncryptorTest.php
diff --git a/Tests/Unit/Encryptors/HaliteEncryptorTest.php b/tests/Unit/Encryptors/HaliteEncryptorTest.php
similarity index 73%
rename from Tests/Unit/Encryptors/HaliteEncryptorTest.php
rename to tests/Unit/Encryptors/HaliteEncryptorTest.php
index 794b5a2f..414d1a8f 100644
--- a/Tests/Unit/Encryptors/HaliteEncryptorTest.php
+++ b/tests/Unit/Encryptors/HaliteEncryptorTest.php
@@ -11,7 +11,7 @@ class HaliteEncryptorTest extends TestCase
public function testEncryptExtension(): void
{
- if (! extension_loaded('sodium')) {
+ if (! extension_loaded('sodium') && !class_exists('ParagonIE_Sodium_Compat')) {
$this->markTestSkipped('This test only runs when the sodium extension is enabled.');
}
$keyfile = __DIR__.'/fixtures/halite.key';
@@ -29,7 +29,7 @@ public function testEncryptExtension(): void
public function testGenerateKey(): void
{
- if (! extension_loaded('sodium')) {
+ if (! extension_loaded('sodium') && !class_exists('ParagonIE_Sodium_Compat')) {
$this->markTestSkipped('This test only runs when the sodium extension is enabled.');
}
$keyfile = sys_get_temp_dir().'/halite-'.md5(time());
@@ -44,18 +44,4 @@ public function testGenerateKey(): void
unlink($keyfile);
}
-
-
- public function testEncryptWithoutExtensionThrowsException(): void
- {
- if (extension_loaded('sodium')) {
- $this->markTestSkipped('This only runs when the sodium extension is disabled.');
- }
- $keyfile = __DIR__.'/fixtures/halite.key';
- $halite = new HaliteEncryptor($keyfile);
-
- $this->expectException(\SodiumException::class);
- $halite->encrypt(self::DATA);
- }
-
}
diff --git a/Tests/Unit/Encryptors/fixtures/defuse.key b/tests/Unit/Encryptors/fixtures/defuse.key
similarity index 100%
rename from Tests/Unit/Encryptors/fixtures/defuse.key
rename to tests/Unit/Encryptors/fixtures/defuse.key
diff --git a/Tests/Unit/Encryptors/fixtures/halite.key b/tests/Unit/Encryptors/fixtures/halite.key
similarity index 100%
rename from Tests/Unit/Encryptors/fixtures/halite.key
rename to tests/Unit/Encryptors/fixtures/halite.key
diff --git a/Tests/Unit/Subscribers/DoctrineEncryptSubscriberTest.php b/tests/Unit/Subscribers/DoctrineEncryptSubscriberTest.php
similarity index 75%
rename from Tests/Unit/Subscribers/DoctrineEncryptSubscriberTest.php
rename to tests/Unit/Subscribers/DoctrineEncryptSubscriberTest.php
index 0a407cc3..79269856 100644
--- a/Tests/Unit/Subscribers/DoctrineEncryptSubscriberTest.php
+++ b/tests/Unit/Subscribers/DoctrineEncryptSubscriberTest.php
@@ -17,6 +17,7 @@
use Doctrine\ORM\UnitOfWork;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
+use ReflectionClass;
class DoctrineEncryptSubscriberTest extends TestCase
{
@@ -35,6 +36,22 @@ class DoctrineEncryptSubscriberTest extends TestCase
*/
private $reader;
+ /** @var EntityManagerInterface|MockObject */
+ private $em;
+
+ protected function createMock($originalClassName): MockObject
+ {
+ $oldErrorLevel = ini_get('error_reporting');
+ ini_set('error_reporting',E_ALL ^ E_DEPRECATED);
+
+ $return = parent::createMock($originalClassName);
+
+ ini_set('error_reporting',$oldErrorLevel);
+
+ return $return;
+ }
+
+
protected function setUp(): void
{
$this->encryptor = $this->createMock(EncryptorInterface::class);
@@ -67,6 +84,14 @@ protected function setUp(): void
return false;
})
;
+ $this->em = $this->createMock(EntityManagerInterface::class);
+ $this->em->method('getClassMetadata')
+ ->willReturnCallback(function (string $className) {
+ $classMetaData = $this->createMock(ClassMetadata::class);
+ $classMetaData->rootEntityName = $className;
+
+ return $classMetaData;
+ });
$this->subscriber = new DoctrineEncryptSubscriber($this->reader, $this->encryptor);
}
@@ -82,11 +107,19 @@ public function testSetRestorEncryptor(): void
$this->assertSame($this->encryptor, $this->subscriber->getEncryptor());
}
+ protected function triggerProcessFields(Object $entity,bool $encrypt)
+ {
+ $class = new ReflectionClass(DoctrineEncryptSubscriber::class);
+ $method = $class->getMethod('processFields');
+ $method->setAccessible(true);
+ $method->invokeArgs($this->subscriber,[$entity,$this->em,$encrypt]);
+ }
+
public function testProcessFieldsEncrypt(): void
{
$user = new User('David', 'Switzerland');
- $this->subscriber->processFields($user, true);
+ $this->triggerProcessFields($user,true);
$this->assertStringStartsWith('encrypted-', $user->name);
$this->assertStringStartsWith('encrypted-', $user->getAddress());
@@ -96,7 +129,7 @@ public function testProcessFieldsEncryptExtend(): void
{
$user = new ExtendedUser('David', 'Switzerland', 'extra');
- $this->subscriber->processFields($user, true);
+ $this->triggerProcessFields($user,true);
$this->assertStringStartsWith('encrypted-', $user->name);
$this->assertStringStartsWith('encrypted-', $user->getAddress());
@@ -105,9 +138,10 @@ public function testProcessFieldsEncryptExtend(): void
public function testProcessFieldsEncryptEmbedded(): void
{
+
$withUser = new WithUser('Thing', 'foo', new User('David', 'Switzerland'));
- $this->subscriber->processFields($withUser, true);
+ $this->triggerProcessFields($withUser,true);
$this->assertStringStartsWith('encrypted-', $withUser->name);
$this->assertSame('foo', $withUser->foo);
@@ -119,7 +153,7 @@ public function testProcessFieldsEncryptNull(): void
{
$user = new User('David', null);
- $this->subscriber->processFields($user, true);
+ $this->triggerProcessFields($user,true);
$this->assertStringStartsWith('encrypted-', $user->name);
$this->assertNull($user->getAddress());
@@ -130,7 +164,7 @@ public function testProcessFieldsNoEncryptor(): void
$user = new User('David', 'Switzerland');
$this->subscriber->setEncryptor(null);
- $this->subscriber->processFields($user, true);
+ $this->triggerProcessFields($user,true);
$this->assertSame('David', $user->name);
$this->assertSame('Switzerland', $user->getAddress());
@@ -140,7 +174,7 @@ public function testProcessFieldsDecrypt(): void
{
$user = new User('encrypted-David', 'encrypted-Switzerland');
- $this->subscriber->processFields($user, false);
+ $this->triggerProcessFields($user,false);
$this->assertSame('David', $user->name);
$this->assertSame('Switzerland', $user->getAddress());
@@ -150,7 +184,7 @@ public function testProcessFieldsDecryptExtended(): void
{
$user = new ExtendedUser('encrypted-David', 'encrypted-Switzerland', 'encrypted-extra');
- $this->subscriber->processFields($user, false);
+ $this->triggerProcessFields($user,false);
$this->assertSame('David', $user->name);
$this->assertSame('Switzerland', $user->getAddress());
@@ -161,7 +195,7 @@ public function testProcessFieldsDecryptEmbedded(): void
{
$withUser = new WithUser('encrypted-Thing', 'foo', new User('encrypted-David', 'encrypted-Switzerland'));
- $this->subscriber->processFields($withUser, false);
+ $this->triggerProcessFields($withUser,false);
$this->assertSame('Thing', $withUser->name);
$this->assertSame('foo', $withUser->foo);
@@ -173,7 +207,7 @@ public function testProcessFieldsDecryptNull(): void
{
$user = new User('encrypted-David', null);
- $this->subscriber->processFields($user, false);
+ $this->triggerProcessFields($user,false);
$this->assertSame('David', $user->name);
$this->assertNull($user->getAddress());
@@ -184,7 +218,7 @@ public function testProcessFieldsDecryptNonEncrypted(): void
// no trailing but somethint that our mock decrypt would change if called
$user = new User('encrypted-David', 'encrypted-Switzerland');
- $this->subscriber->processFields($user, false);
+ $this->triggerProcessFields($user,false);
$this->assertSame('encrypted-David', $user->name);
$this->assertSame('encrypted-Switzerland', $user->getAddress());
@@ -208,8 +242,15 @@ public function testOnFlush(): void
->willReturn($uow)
;
$classMetaData = $this->createMock(ClassMetadata::class);
- $em->expects($this->once())->method('getClassMetadata')->willReturn($classMetaData);
- $uow->expects($this->once())->method('recomputeSingleEntityChangeSet');
+ $classMetaData->rootEntityName = User::class;
+ $em->method('getClassMetadata')
+ ->willReturnCallback(function (string $className) {
+ $classMetaData = $this->createMock(ClassMetadata::class);
+ $classMetaData->rootEntityName = $className;
+
+ return $classMetaData;
+ });
+ $uow->expects($this->any())->method('recomputeSingleEntityChangeSet');
$onFlush = new OnFlushEventArgs($em);
@@ -236,6 +277,16 @@ public function testPostFlush(): void
->method('getUnitOfWork')
->willReturn($uow)
;
+ $classMetaData = $this->createMock(ClassMetadata::class);
+ $classMetaData->rootEntityName = User::class;
+ $em->method('getClassMetadata')
+ ->willReturnCallback(function (string $className) {
+ $classMetaData = $this->createMock(ClassMetadata::class);
+ $classMetaData->rootEntityName = $className;
+
+ return $classMetaData;
+ });
+
$postFlush = new PostFlushEventArgs($em);
$this->subscriber->postFlush($postFlush);
diff --git a/Tests/Unit/Subscribers/fixtures/ExtendedUser.php b/tests/Unit/Subscribers/fixtures/ExtendedUser.php
similarity index 100%
rename from Tests/Unit/Subscribers/fixtures/ExtendedUser.php
rename to tests/Unit/Subscribers/fixtures/ExtendedUser.php
diff --git a/Tests/Unit/Subscribers/fixtures/User.php b/tests/Unit/Subscribers/fixtures/User.php
similarity index 100%
rename from Tests/Unit/Subscribers/fixtures/User.php
rename to tests/Unit/Subscribers/fixtures/User.php
diff --git a/Tests/Unit/Subscribers/fixtures/WithUser.php b/tests/Unit/Subscribers/fixtures/WithUser.php
similarity index 100%
rename from Tests/Unit/Subscribers/fixtures/WithUser.php
rename to tests/Unit/Subscribers/fixtures/WithUser.php
diff --git a/Tests/bootstrap.php b/tests/bootstrap.php
similarity index 100%
rename from Tests/bootstrap.php
rename to tests/bootstrap.php