diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb37379a..bb9ec895 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: uses: "shivammathur/setup-php@v2" with: coverage: "none" - php-version: "8.0" + php-version: "8.1" - name: "Validate composer.json" run: "composer validate --strict --no-check-lock" @@ -40,7 +40,7 @@ jobs: run: "vendor/bin/phpstan analyze" - name: "PHPCompatibility" - run: "vendor/bin/phpcs src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 7.2-8.1" + run: "vendor/bin/phpcs src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.1-8.3" tests: name: "Tests ${{ matrix.php-version }} ${{ matrix.dependency-versions }}" @@ -51,16 +51,16 @@ jobs: fail-fast: false matrix: # normal, highest, non-dev installs - php-version: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2'] + php-version: ['8.1', '8.2', '8.3'] composer-options: ['--prefer-stable'] dependency-versions: ['highest'] include: # testing lowest PHP version with lowest dependencies - - php-version: '7.2.5' + - php-version: '8.1' dependency-versions: 'lowest' composer-options: '--prefer-lowest' env: - SYMFONY_PHPUNIT_VERSION: 7 + SYMFONY_PHPUNIT_VERSION: 9 steps: - name: "Checkout code" @@ -85,7 +85,7 @@ jobs: - name: Unit Tests run: vendor/bin/simple-phpunit env: - SYMFONY_PHPUNIT_VERSION: "${{ matrix.dependency-versions == 'lowest' && '7' || '' }}" + SYMFONY_PHPUNIT_VERSION: "${{ matrix.dependency-versions == 'lowest' && '9' || '' }}" - name: Install symlinks for demo's uses: "ramsey/composer-install@v2" @@ -104,7 +104,7 @@ jobs: - name: Demo symfony5.4 - Unit Tests run: demo/symfony5.4/bin/phpunit -c demo/symfony5.4/phpunit.xml.dist env: - SYMFONY_PHPUNIT_VERSION: "${{ matrix.dependency-versions == 'lowest' && '7' || '' }}" + SYMFONY_PHPUNIT_VERSION: "${{ matrix.dependency-versions == 'lowest' && '9' || '' }}" - name: Demo symfony6.x - Install dependencies uses: "ramsey/composer-install@v2" @@ -124,8 +124,20 @@ jobs: dependency-versions: "${{ matrix.dependency-versions }}" composer-options: "--prefer-dist --no-progress" working-directory: "demo/symfony7.x" - if: ${{ startsWith(matrix.php-version , '8.2') }} + if: ${{ matrix.php-version == '8.2' || matrix.php-version == '8.3' }} - name: Demo symfony7.x - Unit Tests run: demo/symfony7.x/vendor/bin/simple-phpunit -c demo/symfony7.x/phpunit.xml.dist - if: ${{ startsWith(matrix.php-version , '8.2') }} \ No newline at end of file + if: ${{ matrix.php-version == '8.2' || matrix.php-version == '8.3' }} + + - name: Demo symfony7.x Doctrine3.x - Install dependencies + uses: "ramsey/composer-install@v2" + with: + dependency-versions: "${{ matrix.dependency-versions }}" + composer-options: "--prefer-dist --no-progress" + working-directory: "demo/symfony7.x_doctrine3.x" + if: ${{ matrix.php-version == '8.2' || matrix.php-version == '8.3' }} + + - name: Demo symfony7.x Doctrine3.x - Unit Tests + run: demo/symfony7.x_doctrine3.x/vendor/bin/simple-phpunit -c demo/symfony7.x_doctrine3.x/phpunit.xml.dist + if: ${{ matrix.php-version == '8.2' || matrix.php-version == '8.3' }} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index feddc2d9..939918d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,10 @@ matrix: fast_finish: true include: # Minimum supported versions - - php: 7.2 + - php: 8.1 env: COMPOSER_FLAGS="--prefer-lowest" ## with sodium extension installed. - - php: 7.2 + - php: 8.1 env: COMPOSER_FLAGS="--prefer-lowest" before_install: - sudo add-apt-repository ppa:ondrej/php -y @@ -23,17 +23,14 @@ matrix: - printf "\n" | pecl install libsodium - composer update $COMPOSER_FLAGS --prefer-dist --no-interaction - - php: 7.3 + - php: 8.2 env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text" - - php: 7.4 - env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text" - - - php: 8.0 + - php: 8.3 env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text" # Latest commit to master - - php: 7.3 + - php: 8.1 env: STABILITY="dev" allow_failures: diff --git a/composer.json b/composer.json index 6072c591..004df45e 100644 --- a/composer.json +++ b/composer.json @@ -5,25 +5,24 @@ "license": "MIT", "description": "Encrypted symfony entity's by verified and standardized libraries", "require": { - "php": "^7.2|^8.0", - "paragonie/halite": "^4.6|^5.0", - "doctrine/orm": "^2.5", + "php": "^8.1", + "doctrine/orm": "^2.18|^3.0", "doctrine/doctrine-bundle": "^2.0.8|^2.1", "symfony/property-access": "^5.4|^6.0|^7.0", "symfony/dependency-injection": "^5.4|^6.0|^7.0", "symfony/yaml": "^5.4|^6.0|^7.0", "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/config": "^5.4|^6.0|^7.0", - "doctrine/annotations": "^1.13|^2.0", - "symfony/expression-language": "^5.4|^6.0|^7.0" + "symfony/config": "^5.4|^6.0|^7.0" }, "require-dev": { + "paragonie/halite": "^4.6|^5.0", "defuse/php-encryption": "^2.1", "doctrine/cache": "^1.11", "phpstan/phpstan": "^1.4", - "jetbrains/phpstorm-attributes": "^1.0", "phpcompatibility/php-compatibility": "^9.3", - "symfony/phpunit-bridge": "^7.0" + "squizlabs/php_codesniffer": "^3.8", + "symfony/phpunit-bridge": "^7.0.4", + "symfony/expression-language": "^5.4|^6.0|^7.0" }, "suggest": { "defuse/php-encryption": "Alternative for halite for use with older php-versions", @@ -32,17 +31,17 @@ }, "autoload": { "psr-4": { - "Ambta\\DoctrineEncryptBundle\\": "src/" + "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" + "phpcs-compatibility-test" : "vendor/bin/phpcs src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.1-8.3" }, "autoload-dev": { "psr-4": { - "Ambta\\DoctrineEncryptBundle\\Tests\\": "tests/" + "DoctrineEncryptBundle\\Tests\\": "tests/" } }, "config": { diff --git a/demo/composer.json b/demo/composer.json index b943b38b..d900a292 100644 --- a/demo/composer.json +++ b/demo/composer.json @@ -19,7 +19,9 @@ "printf 'HALITE_SECRET=\"%s\"' \"$(cat shared/.Halite.key)\" > symfony6.x/.env.local", "printf 'HALITE_SECRET=\"%s\"' \"$(cat shared/.Halite.key)\" > symfony6.x/.env.test.local", "printf 'HALITE_SECRET=\"%s\"' \"$(cat shared/.Halite.key)\" > symfony7.x/.env.local", - "printf 'HALITE_SECRET=\"%s\"' \"$(cat shared/.Halite.key)\" > symfony7.x/.env.test.local" + "printf 'HALITE_SECRET=\"%s\"' \"$(cat shared/.Halite.key)\" > symfony7.x/.env.test.local", + "printf 'HALITE_SECRET=\"%s\"' \"$(cat shared/.Halite.key)\" > symfony7.x_doctrine3.x/.env.local", + "printf 'HALITE_SECRET=\"%s\"' \"$(cat shared/.Halite.key)\" > symfony7.x_doctrine3.x/.env.test.local" ] }, "extra": { @@ -27,9 +29,11 @@ "shared/templates": "symfony5.4/templates", "./shared/templates": "symfony6.x/templates", "././shared/templates": "symfony7.x/templates", + "./././shared/templates": "symfony7.x_doctrine3.x/templates", "shared/var/data.db": "symfony5.4/var/data.db", "./shared/var/data.db": "symfony6.x/var/data.db", - "././shared/var/data.db": "symfony7.x/var/data.db" + "././shared/var/data.db": "symfony7.x/var/data.db", + "./././shared/var/data.db": "symfony7.x_doctrine3.x/var/data.db" } } } diff --git a/demo/symfony5.4/composer.json b/demo/symfony5.4/composer.json index 8776fcc6..062aedf3 100644 --- a/demo/symfony5.4/composer.json +++ b/demo/symfony5.4/composer.json @@ -4,22 +4,24 @@ "minimum-stability": "stable", "prefer-stable": true, "require": { - "php": ">=7.2.5", + "php": "^8.1", "ext-ctype": "*", "ext-iconv": "*", + "doctrine/orm": "^2.13", "doctrineencryptbundle/doctrine-encrypt-bundle": "@dev", "doctrine/persistence": "^3.2", - "paragonie/halite": "^4.6|^5.0", + "paragonie/halite": "^4.6", "symfony/console": "5.4.*", "symfony/dotenv": "5.4.*", - "symfony/flex": "^1.21|^2", + "symfony/flex": "^2.2", "symfony/framework-bundle": "5.4.*", "symfony/runtime": "5.4.*", "symfony/twig-bundle": "5.4.*", - "symfony/yaml": "5.4.*" + "symfony/yaml": "5.4.*", + "doctrine/annotations": "^1.13|^2.0" }, "require-dev": { - "symfony/phpunit-bridge": "^7.0" + "symfony/phpunit-bridge": "^7.0.4" }, "config": { "allow-plugins": { diff --git a/demo/symfony5.4/config/bundles.php b/demo/symfony5.4/config/bundles.php index 7a648836..621bccdb 100644 --- a/demo/symfony5.4/config/bundles.php +++ b/demo/symfony5.4/config/bundles.php @@ -4,5 +4,5 @@ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], - Ambta\DoctrineEncryptBundle\AmbtaDoctrineEncryptBundle::class => ['all' => true], + DoctrineEncryptBundle\DoctrineEncryptBundle::class => ['all' => true], ]; diff --git a/demo/symfony5.4/config/packages/ambta_doctrine_encrypt.yaml b/demo/symfony5.4/config/packages/ambta_doctrine_encrypt.yaml deleted file mode 100644 index ddbd65af..00000000 --- a/demo/symfony5.4/config/packages/ambta_doctrine_encrypt.yaml +++ /dev/null @@ -1,3 +0,0 @@ -ambta_doctrine_encrypt: - enable_secret_generation: false - secret: '%env(HALITE_SECRET)%' \ No newline at end of file diff --git a/demo/symfony5.4/config/packages/doctrine.yaml b/demo/symfony5.4/config/packages/doctrine.yaml index 7f2c0c90..705b25eb 100644 --- a/demo/symfony5.4/config/packages/doctrine.yaml +++ b/demo/symfony5.4/config/packages/doctrine.yaml @@ -6,6 +6,7 @@ doctrine: # either here or in the DATABASE_URL env var (see .env file) #server_version: '15' orm: + # enable_lazy_ghost_objects: true auto_generate_proxy_classes: true naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware auto_mapping: true @@ -16,6 +17,12 @@ doctrine: 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: diff --git a/demo/symfony5.4/config/packages/doctrine_encrypt.yaml b/demo/symfony5.4/config/packages/doctrine_encrypt.yaml new file mode 100644 index 00000000..76ec03d6 --- /dev/null +++ b/demo/symfony5.4/config/packages/doctrine_encrypt.yaml @@ -0,0 +1,3 @@ +doctrine_encrypt: + enable_secret_generation: false + secret: '%env(HALITE_SECRET)%' \ No newline at end of file diff --git a/demo/symfony5.4/config/packages/php8/doctrine.yaml b/demo/symfony5.4/config/packages/php8/doctrine.yaml deleted file mode 100644 index 1710f9fa..00000000 --- a/demo/symfony5.4/config/packages/php8/doctrine.yaml +++ /dev/null @@ -1,16 +0,0 @@ -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/symfony5.4/src/Entity/Annotation/Secret.php b/demo/symfony5.4/src/Entity/Annotation/Secret.php index 26e42a84..64506ba1 100644 --- a/demo/symfony5.4/src/Entity/Annotation/Secret.php +++ b/demo/symfony5.4/src/Entity/Annotation/Secret.php @@ -2,7 +2,6 @@ namespace App\Entity\Annotation; -use Ambta\DoctrineEncryptBundle\Configuration\Encrypted; use Doctrine\ORM\Mapping as ORM; use App\Repository\Annotation\SecretRepository; @@ -19,8 +18,7 @@ class Secret implements \App\Entity\SecretInterface private $name; /** - * @ORM\Column(type="string",nullable=false) - * @Encrypted + * @ORM\Column(type="encrypted",nullable=false) */ private $secret; diff --git a/demo/symfony5.4/src/Entity/Attribute/Secret.php b/demo/symfony5.4/src/Entity/Attribute/Secret.php index 121af154..004f40c0 100644 --- a/demo/symfony5.4/src/Entity/Attribute/Secret.php +++ b/demo/symfony5.4/src/Entity/Attribute/Secret.php @@ -2,7 +2,6 @@ namespace App\Entity\Attribute; -use Ambta\DoctrineEncryptBundle\Configuration\Encrypted; use App\Repository\Attribute\SecretRepository; use Doctrine\ORM\Mapping as ORM; @@ -15,8 +14,7 @@ class Secret implements \App\Entity\SecretInterface private $name; - #[ORM\Column(type:"string", nullable:false)] - #[Encrypted] + #[ORM\Column(type:"encrypted", nullable:false)] private $secret; /** diff --git a/demo/symfony5.4/src/Repository/Annotation/SecretRepository.php b/demo/symfony5.4/src/Repository/Annotation/SecretRepository.php index ce2edcc2..51427a94 100644 --- a/demo/symfony5.4/src/Repository/Annotation/SecretRepository.php +++ b/demo/symfony5.4/src/Repository/Annotation/SecretRepository.php @@ -4,14 +4,7 @@ use App\Entity\Annotation\Secret; use App\Repository\AbstractSecretRepository; - -// Alias is needed because of test with both php 7.2, 7.4 and 8.0 -if (!interface_exists('\Doctrine\Common\Persistence\ManagerRegistry')) { - class_alias( - '\Doctrine\Persistence\ManagerRegistry', - '\Doctrine\Common\Persistence\ManagerRegistry' - ); -} +use Doctrine\Persistence\ManagerRegistry; /** * @method Secret|null find($id, $lockMode = null, $lockVersion = null) @@ -20,7 +13,7 @@ class_alias( */ class SecretRepository extends AbstractSecretRepository { - public function __construct(\Doctrine\Common\Persistence\ManagerRegistry $registry) + public function __construct(ManagerRegistry $registry) { parent::__construct($registry, Secret::class); } diff --git a/demo/symfony5.4/tests/SecretTest.php b/demo/symfony5.4/tests/SecretTest.php index 6b65211d..a6a5665d 100644 --- a/demo/symfony5.4/tests/SecretTest.php +++ b/demo/symfony5.4/tests/SecretTest.php @@ -2,7 +2,7 @@ namespace App\Tests; -use Ambta\DoctrineEncryptBundle\Subscribers\DoctrineEncryptSubscriber; +use DoctrineEncryptBundle\Service\Encrypt; use App\Entity; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -53,7 +53,7 @@ private function testSecretsAreEncryptedInDatabase(string $className) self::assertEquals($newSecretObject->getName(), $actualSecretObject->getName()); // Make sure it is encrypted self::assertNotEquals($newSecretObject->getSecret(),$actualRawSecret); - self::assertStringEndsWith(DoctrineEncryptSubscriber::ENCRYPTION_MARKER,$actualRawSecret); + self::assertStringEndsWith(Encrypt::ENCRYPTION_MARKER,$actualRawSecret); } /** diff --git a/demo/symfony6.x/composer.json b/demo/symfony6.x/composer.json index 941a3570..5c507435 100644 --- a/demo/symfony6.x/composer.json +++ b/demo/symfony6.x/composer.json @@ -4,20 +4,25 @@ "minimum-stability": "stable", "prefer-stable": true, "require": { - "php": ">=8.0.2", + "php": "^8.1", "ext-ctype": "*", "ext-iconv": "*", + "doctrine/orm": "^2.13", "doctrineencryptbundle/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.*" + "doctrine/persistence": "^3.2", + "paragonie/halite": "^5.0", + "symfony/console": "6.4.*", + "symfony/dotenv": "6.4.*", + "symfony/flex": "^2.3", + "symfony/framework-bundle": "6.4.*", + "symfony/runtime": "6.4.*", + "symfony/twig-bundle": "6.4.*", + "symfony/yaml": "6.4.*", + "doctrine/annotations": "^1.13|^2.0" }, "require-dev": { "symfony/maker-bundle": "^1.0", - "symfony/phpunit-bridge": "^7.0" + "symfony/phpunit-bridge": "^7.0.4" }, "config": { "allow-plugins": { @@ -67,7 +72,7 @@ "extra": { "symfony": { "allow-contrib": false, - "require": "6.0.*" + "require": "6.4.*" } }, "repositories": [ diff --git a/demo/symfony6.x/config/bundles.php b/demo/symfony6.x/config/bundles.php index b7e7b92c..ebcead12 100644 --- a/demo/symfony6.x/config/bundles.php +++ b/demo/symfony6.x/config/bundles.php @@ -5,5 +5,5 @@ 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], + DoctrineEncryptBundle\DoctrineEncryptBundle::class => ['all' => true], ]; diff --git a/demo/symfony6.x/config/packages/ambta_doctrine_encrypt.yaml b/demo/symfony6.x/config/packages/ambta_doctrine_encrypt.yaml deleted file mode 100644 index ddbd65af..00000000 --- a/demo/symfony6.x/config/packages/ambta_doctrine_encrypt.yaml +++ /dev/null @@ -1,3 +0,0 @@ -ambta_doctrine_encrypt: - enable_secret_generation: false - secret: '%env(HALITE_SECRET)%' \ No newline at end of file diff --git a/demo/symfony6.x/config/packages/doctrine.yaml b/demo/symfony6.x/config/packages/doctrine.yaml index bff6abbf..76db7da6 100644 --- a/demo/symfony6.x/config/packages/doctrine.yaml +++ b/demo/symfony6.x/config/packages/doctrine.yaml @@ -6,6 +6,7 @@ doctrine: # either here or in the DATABASE_URL env var (see .env file) #server_version: '13' orm: + # enable_lazy_ghost_objects: true auto_generate_proxy_classes: true naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware auto_mapping: true diff --git a/demo/symfony6.x/config/packages/doctrine_encrypt.yaml b/demo/symfony6.x/config/packages/doctrine_encrypt.yaml new file mode 100644 index 00000000..76ec03d6 --- /dev/null +++ b/demo/symfony6.x/config/packages/doctrine_encrypt.yaml @@ -0,0 +1,3 @@ +doctrine_encrypt: + enable_secret_generation: false + secret: '%env(HALITE_SECRET)%' \ No newline at end of file diff --git a/demo/symfony6.x/phpunit.xml.dist b/demo/symfony6.x/phpunit.xml.dist index af3f1474..875a9706 100644 --- a/demo/symfony6.x/phpunit.xml.dist +++ b/demo/symfony6.x/phpunit.xml.dist @@ -11,10 +11,10 @@ + - @@ -33,10 +33,6 @@ - - diff --git a/demo/symfony6.x/src/Entity/Annotation/Secret.php b/demo/symfony6.x/src/Entity/Annotation/Secret.php index 49dce58a..082f6a44 100644 --- a/demo/symfony6.x/src/Entity/Annotation/Secret.php +++ b/demo/symfony6.x/src/Entity/Annotation/Secret.php @@ -2,7 +2,6 @@ namespace App\Entity\Annotation; -use Ambta\DoctrineEncryptBundle\Configuration\Encrypted; use Doctrine\ORM\Mapping as ORM; use App\Repository\Annotation\SecretRepository; @@ -19,8 +18,7 @@ class Secret implements \App\Entity\SecretInterface private $name; /** - * @ORM\Column(type="string",nullable=false) - * @Encrypted + * @ORM\Column(type="encrypted",nullable=false) */ private $secret; diff --git a/demo/symfony6.x/src/Entity/Attribute/Secret.php b/demo/symfony6.x/src/Entity/Attribute/Secret.php index c76a80db..7e001053 100644 --- a/demo/symfony6.x/src/Entity/Attribute/Secret.php +++ b/demo/symfony6.x/src/Entity/Attribute/Secret.php @@ -2,7 +2,6 @@ namespace App\Entity\Attribute; -use Ambta\DoctrineEncryptBundle\Configuration\Encrypted; use App\Repository\Attribute\SecretRepository; use Doctrine\ORM\Mapping as ORM; @@ -16,8 +15,7 @@ class Secret implements \App\Entity\SecretInterface private $name; - #[ORM\Column(type:"string",nullable:false)] - #[Encrypted] + #[ORM\Column(type:"encrypted",nullable:false)] private $secret; /** diff --git a/demo/symfony6.x/tests/SecretTest.php b/demo/symfony6.x/tests/SecretTest.php index e2783e66..19d8b525 100644 --- a/demo/symfony6.x/tests/SecretTest.php +++ b/demo/symfony6.x/tests/SecretTest.php @@ -2,7 +2,7 @@ namespace App\Tests; -use Ambta\DoctrineEncryptBundle\Subscribers\DoctrineEncryptSubscriber; +use DoctrineEncryptBundle\Service\Encrypt; use App\Entity; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -53,7 +53,7 @@ private function testSecretsAreEncryptedInDatabase(string $className) self::assertEquals($newSecretObject->getName(), $actualSecretObject->getName()); // Make sure it is encrypted self::assertNotEquals($newSecretObject->getSecret(),$actualRawSecret); - self::assertStringEndsWith(DoctrineEncryptSubscriber::ENCRYPTION_MARKER,$actualRawSecret); + self::assertStringEndsWith(Encrypt::ENCRYPTION_MARKER,$actualRawSecret); } /** diff --git a/demo/symfony7.x/composer.json b/demo/symfony7.x/composer.json index 1c314051..cc0dd74a 100644 --- a/demo/symfony7.x/composer.json +++ b/demo/symfony7.x/composer.json @@ -7,7 +7,10 @@ "php": ">=8.2", "ext-ctype": "*", "ext-iconv": "*", + "doctrine/orm": "^2.18", "doctrineencryptbundle/doctrine-encrypt-bundle": "@dev", + "doctrine/persistence": "^3.2", + "paragonie/halite": "^5.0", "symfony/console": "7.0.*", "symfony/dotenv": "7.0.*", "symfony/flex": "^2", diff --git a/demo/symfony7.x/composer.lock b/demo/symfony7.x/composer.lock index f8325ab9..4be0ba7b 100644 --- a/demo/symfony7.x/composer.lock +++ b/demo/symfony7.x/composer.lock @@ -4,84 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "71eca238f24ddce773e7b05a12d47577", + "content-hash": "e99c69747ede75faeaa5d4d1457999d1", "packages": [ - { - "name": "doctrine/annotations", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "shasum": "" - }, - "require": { - "doctrine/lexer": "^2 || ^3", - "ext-tokenizer": "*", - "php": "^7.2 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" - }, - "require-dev": { - "doctrine/cache": "^2.0", - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6", - "vimeo/psalm": "^4.10" - }, - "suggest": { - "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.1" - }, - "time": "2023-02-02T22:02:53+00:00" - }, { "name": "doctrine/cache", "version": "2.2.0", @@ -177,16 +101,16 @@ }, { "name": "doctrine/collections", - "version": "2.1.4", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "72328a11443a0de79967104ad36ba7b30bded134" + "reference": "07e16cd7b80a2cffed99e36b541876af172f0257" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/72328a11443a0de79967104ad36ba7b30bded134", - "reference": "72328a11443a0de79967104ad36ba7b30bded134", + "url": "https://api.github.com/repos/doctrine/collections/zipball/07e16cd7b80a2cffed99e36b541876af172f0257", + "reference": "07e16cd7b80a2cffed99e36b541876af172f0257", "shasum": "" }, "require": { @@ -198,7 +122,7 @@ "ext-json": "*", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^10.5", "vimeo/psalm": "^5.11" }, "type": "library", @@ -243,7 +167,7 @@ ], "support": { "issues": "https://github.com/doctrine/collections/issues", - "source": "https://github.com/doctrine/collections/tree/2.1.4" + "source": "https://github.com/doctrine/collections/tree/2.2.0" }, "funding": [ { @@ -259,7 +183,7 @@ "type": "tidelift" } ], - "time": "2023-10-03T09:22:33+00:00" + "time": "2024-02-25T22:55:36+00:00" }, { "name": "doctrine/common", @@ -354,16 +278,16 @@ }, { "name": "doctrine/dbal", - "version": "3.7.2", + "version": "3.8.2", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "0ac3c270590e54910715e9a1a044cc368df282b2" + "reference": "a19a1d05ca211f41089dffcc387733a6875196cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/0ac3c270590e54910715e9a1a044cc368df282b2", - "reference": "0ac3c270590e54910715e9a1a044cc368df282b2", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/a19a1d05ca211f41089dffcc387733a6875196cb", + "reference": "a19a1d05ca211f41089dffcc387733a6875196cb", "shasum": "" }, "require": { @@ -379,14 +303,14 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.42", + "phpstan/phpstan": "1.10.57", "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "9.6.13", + "phpunit/phpunit": "9.6.16", "psalm/plugin-phpunit": "0.18.4", "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.7.2", - "symfony/cache": "^5.4|^6.0", - "symfony/console": "^4.4|^5.4|^6.0", + "squizlabs/php_codesniffer": "3.8.1", + "symfony/cache": "^5.4|^6.0|^7.0", + "symfony/console": "^4.4|^5.4|^6.0|^7.0", "vimeo/psalm": "4.30.0" }, "suggest": { @@ -447,7 +371,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.7.2" + "source": "https://github.com/doctrine/dbal/tree/3.8.2" }, "funding": [ { @@ -463,20 +387,20 @@ "type": "tidelift" } ], - "time": "2023-11-19T08:06:58+00:00" + "time": "2024-02-12T18:36:36+00:00" }, { "name": "doctrine/deprecations", - "version": "1.1.2", + "version": "1.1.3", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", - "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", "shasum": "" }, "require": { @@ -508,22 +432,22 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + "source": "https://github.com/doctrine/deprecations/tree/1.1.3" }, - "time": "2023-09-27T20:04:15+00:00" + "time": "2024-01-30T19:34:25+00:00" }, { "name": "doctrine/doctrine-bundle", - "version": "2.11.1", + "version": "2.11.3", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineBundle.git", - "reference": "4089f1424b724786c062aea50aae5f773449b94b" + "reference": "492725310ae9a1b5b20d6ae09fb5ae6404616e68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/4089f1424b724786c062aea50aae5f773449b94b", - "reference": "4089f1424b724786c062aea50aae5f773449b94b", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/492725310ae9a1b5b20d6ae09fb5ae6404616e68", + "reference": "492725310ae9a1b5b20d6ae09fb5ae6404616e68", "shasum": "" }, "require": { @@ -544,18 +468,18 @@ }, "conflict": { "doctrine/annotations": ">=3.0", - "doctrine/orm": "<2.14 || >=4.0", + "doctrine/orm": "<2.17 || >=4.0", "twig/twig": "<1.34 || >=2.0 <2.4" }, "require-dev": { "doctrine/annotations": "^1 || ^2", "doctrine/coding-standard": "^12", "doctrine/deprecations": "^1.0", - "doctrine/orm": "^2.14 || ^3.0", + "doctrine/orm": "^2.17 || ^3.0", "friendsofphp/proxy-manager-lts": "^1.0", - "phpunit/phpunit": "^9.5.26 || ^10.0", + "phpunit/phpunit": "^9.5.26", "psalm/plugin-phpunit": "^0.18.4", - "psalm/plugin-symfony": "^4", + "psalm/plugin-symfony": "^5", "psr/log": "^1.1.4 || ^2.0 || ^3.0", "symfony/phpunit-bridge": "^6.1 || ^7.0", "symfony/property-info": "^5.4 || ^6.0 || ^7.0", @@ -568,7 +492,7 @@ "symfony/web-profiler-bundle": "^5.4 || ^6.0 || ^7.0", "symfony/yaml": "^5.4 || ^6.0 || ^7.0", "twig/twig": "^1.34 || ^2.12 || ^3.0", - "vimeo/psalm": "^4.30" + "vimeo/psalm": "^5.15" }, "suggest": { "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", @@ -613,7 +537,7 @@ ], "support": { "issues": "https://github.com/doctrine/DoctrineBundle/issues", - "source": "https://github.com/doctrine/DoctrineBundle/tree/2.11.1" + "source": "https://github.com/doctrine/DoctrineBundle/tree/2.11.3" }, "funding": [ { @@ -629,7 +553,7 @@ "type": "tidelift" } ], - "time": "2023-11-15T20:01:50+00:00" + "time": "2024-02-10T20:56:20+00:00" }, { "name": "doctrine/event-manager", @@ -724,16 +648,16 @@ }, { "name": "doctrine/inflector", - "version": "2.0.8", + "version": "2.0.10", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff" + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/f9301a5b2fb1216b2b08f02ba04dc45423db6bff", - "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", "shasum": "" }, "require": { @@ -795,7 +719,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.8" + "source": "https://github.com/doctrine/inflector/tree/2.0.10" }, "funding": [ { @@ -811,7 +735,7 @@ "type": "tidelift" } ], - "time": "2023-06-16T13:40:37+00:00" + "time": "2024-02-18T20:23:39+00:00" }, { "name": "doctrine/instantiator", @@ -885,28 +809,27 @@ }, { "name": "doctrine/lexer", - "version": "2.1.0", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", - "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "require-dev": { - "doctrine/coding-standard": "^9 || ^10", - "phpstan/phpstan": "^1.3", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^4.11 || ^5.0" + "vimeo/psalm": "^5.21" }, "type": "library", "autoload": { @@ -943,7 +866,7 @@ ], "support": { "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/2.1.0" + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, "funding": [ { @@ -959,20 +882,20 @@ "type": "tidelift" } ], - "time": "2022-12-14T08:49:07+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { "name": "doctrine/orm", - "version": "2.17.1", + "version": "2.18.2", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "1a4fe6e0bb67762370937a7e6cee3da40a9122d1" + "reference": "52a6a21387380b09419a66a7ec1c66f6cab69b20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/1a4fe6e0bb67762370937a7e6cee3da40a9122d1", - "reference": "1a4fe6e0bb67762370937a7e6cee3da40a9122d1", + "url": "https://api.github.com/repos/doctrine/orm/zipball/52a6a21387380b09419a66a7ec1c66f6cab69b20", + "reference": "52a6a21387380b09419a66a7ec1c66f6cab69b20", "shasum": "" }, "require": { @@ -985,7 +908,7 @@ "doctrine/event-manager": "^1.2 || ^2", "doctrine/inflector": "^1.4 || ^2.0", "doctrine/instantiator": "^1.3 || ^2", - "doctrine/lexer": "^2", + "doctrine/lexer": "^2 || ^3", "doctrine/persistence": "^2.4 || ^3", "ext-ctype": "*", "php": "^7.1 || ^8.0", @@ -1001,14 +924,14 @@ "doctrine/annotations": "^1.13 || ^2", "doctrine/coding-standard": "^9.0.2 || ^12.0", "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/phpstan": "~1.4.10 || 1.10.35", + "phpstan/phpstan": "~1.4.10 || 1.10.59", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "psr/log": "^1 || ^2 || ^3", "squizlabs/php_codesniffer": "3.7.2", - "symfony/cache": "^4.4 || ^5.4 || ^6.0", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2", - "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0", - "vimeo/psalm": "4.30.0 || 5.15.0" + "symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0", + "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "vimeo/psalm": "4.30.0 || 5.22.2" }, "suggest": { "ext-dom": "Provides support for XSD validation for XML mapping files", @@ -1021,7 +944,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\ORM\\": "lib/Doctrine/ORM" + "Doctrine\\ORM\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1058,22 +981,22 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.17.1" + "source": "https://github.com/doctrine/orm/tree/2.18.2" }, - "time": "2023-11-17T06:25:40+00:00" + "time": "2024-03-01T09:47:18+00:00" }, { "name": "doctrine/persistence", - "version": "3.2.0", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/doctrine/persistence.git", - "reference": "63fee8c33bef740db6730eb2a750cd3da6495603" + "reference": "b6fd1f126b13c1f7e7321f7338b14a19116b5de4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/persistence/zipball/63fee8c33bef740db6730eb2a750cd3da6495603", - "reference": "63fee8c33bef740db6730eb2a750cd3da6495603", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/b6fd1f126b13c1f7e7321f7338b14a19116b5de4", + "reference": "b6fd1f126b13c1f7e7321f7338b14a19116b5de4", "shasum": "" }, "require": { @@ -1142,7 +1065,7 @@ ], "support": { "issues": "https://github.com/doctrine/persistence/issues", - "source": "https://github.com/doctrine/persistence/tree/3.2.0" + "source": "https://github.com/doctrine/persistence/tree/3.3.1" }, "funding": [ { @@ -1158,20 +1081,20 @@ "type": "tidelift" } ], - "time": "2023-05-17T18:32:04+00:00" + "time": "2024-03-01T19:53:13+00:00" }, { "name": "doctrine/sql-formatter", - "version": "1.1.3", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5" + "reference": "a321d114e0a18e6497f8a2cd6f890e000cc17ecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/25a06c7bf4c6b8218f47928654252863ffc890a5", - "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/a321d114e0a18e6497f8a2cd6f890e000cc17ecc", + "reference": "a321d114e0a18e6497f8a2cd6f890e000cc17ecc", "shasum": "" }, "require": { @@ -1208,27 +1131,24 @@ ], "support": { "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.1.3" + "source": "https://github.com/doctrine/sql-formatter/tree/1.2.0" }, - "time": "2022-05-23T21:33:49+00:00" + "time": "2023-08-16T21:49:04+00:00" }, { "name": "doctrineencryptbundle/doctrine-encrypt-bundle", - "version": "dev-symfony7", + "version": "6.0.x-dev", "dist": { "type": "path", "url": "../..", - "reference": "9f23159c8a9c27a86110bd81f1e704b714df5074" + "reference": "d1137a8cd0aefca0c0e25e2a2671cc101c9fb50b" }, "require": { - "doctrine/annotations": "^1.13|^2.0", - "doctrine/doctrine-bundle": "^2.0", - "doctrine/orm": "^2.5", - "paragonie/halite": "^4.6|^5.0", - "php": "^7.2|^8.0", + "doctrine/doctrine-bundle": "^2.0.8|^2.1", + "doctrine/orm": "^2.18|^3.0", + "php": "^8.1", "symfony/config": "^5.4|^6.0|^7.0", "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", "symfony/http-kernel": "^5.4|^6.0|^7.0", "symfony/property-access": "^5.4|^6.0|^7.0", "symfony/yaml": "^5.4|^6.0|^7.0" @@ -1236,10 +1156,12 @@ "require-dev": { "defuse/php-encryption": "^2.1", "doctrine/cache": "^1.11", - "jetbrains/phpstorm-attributes": "^1.0", + "paragonie/halite": "^4.6|^5.0", "phpcompatibility/php-compatibility": "^9.3", "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^6.0|^7.0" + "squizlabs/php_codesniffer": "^3.8", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/phpunit-bridge": "^7.0.4" }, "suggest": { "defuse/php-encryption": "Alternative for halite for use with older php-versions", @@ -1249,12 +1171,12 @@ "type": "library", "autoload": { "psr-4": { - "Ambta\\DoctrineEncryptBundle\\": "src/" + "DoctrineEncryptBundle\\": "src/" } }, "autoload-dev": { "psr-4": { - "Ambta\\DoctrineEncryptBundle\\Tests\\": "tests/" + "DoctrineEncryptBundle\\Tests\\": "tests/" } }, "scripts": { @@ -1265,7 +1187,7 @@ "\"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" + "vendor/bin/phpcs src/ tests/ --standard=PHPCompatibility --runtime-set testVersion 8.1-8.3" ] }, "license": [ @@ -1815,16 +1737,16 @@ }, { "name": "symfony/cache", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "5e2376f726d61541b9617512e374808d12c1dc22" + "reference": "fc822951dd360a593224bb2cef90a087d0dff60f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/5e2376f726d61541b9617512e374808d12c1dc22", - "reference": "5e2376f726d61541b9617512e374808d12c1dc22", + "url": "https://api.github.com/repos/symfony/cache/zipball/fc822951dd360a593224bb2cef90a087d0dff60f", + "reference": "fc822951dd360a593224bb2cef90a087d0dff60f", "shasum": "" }, "require": { @@ -1891,7 +1813,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.0.0" + "source": "https://github.com/symfony/cache/tree/v7.0.4" }, "funding": [ { @@ -1907,7 +1829,7 @@ "type": "tidelift" } ], - "time": "2023-11-25T15:40:35+00:00" + "time": "2024-02-22T20:27:20+00:00" }, { "name": "symfony/cache-contracts", @@ -1987,16 +1909,16 @@ }, { "name": "symfony/config", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "8789646600f4e7e451dde9e1dc81cfa429f3857a" + "reference": "44deeba7233f08f383185ffa37dace3b3bc87364" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/8789646600f4e7e451dde9e1dc81cfa429f3857a", - "reference": "8789646600f4e7e451dde9e1dc81cfa429f3857a", + "url": "https://api.github.com/repos/symfony/config/zipball/44deeba7233f08f383185ffa37dace3b3bc87364", + "reference": "44deeba7233f08f383185ffa37dace3b3bc87364", "shasum": "" }, "require": { @@ -2042,7 +1964,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.0.0" + "source": "https://github.com/symfony/config/tree/v7.0.4" }, "funding": [ { @@ -2058,20 +1980,20 @@ "type": "tidelift" } ], - "time": "2023-11-09T08:30:23+00:00" + "time": "2024-02-26T07:52:39+00:00" }, { "name": "symfony/console", - "version": "v7.0.1", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cdce5c684b2f920bb1343deecdfba356ffad83d5" + "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cdce5c684b2f920bb1343deecdfba356ffad83d5", - "reference": "cdce5c684b2f920bb1343deecdfba356ffad83d5", + "url": "https://api.github.com/repos/symfony/console/zipball/6b099f3306f7c9c2d2786ed736d0026b2903205f", + "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f", "shasum": "" }, "require": { @@ -2135,7 +2057,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.0.1" + "source": "https://github.com/symfony/console/tree/v7.0.4" }, "funding": [ { @@ -2151,20 +2073,20 @@ "type": "tidelift" } ], - "time": "2023-12-01T15:10:06+00:00" + "time": "2024-02-22T20:27:20+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.0.1", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "f6667642954bce638733f254c39e5b5700b47ba4" + "reference": "47f37af245df8457ea63409fc242b3cc825ce5eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f6667642954bce638733f254c39e5b5700b47ba4", - "reference": "f6667642954bce638733f254c39e5b5700b47ba4", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/47f37af245df8457ea63409fc242b3cc825ce5eb", + "reference": "47f37af245df8457ea63409fc242b3cc825ce5eb", "shasum": "" }, "require": { @@ -2215,7 +2137,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.0.1" + "source": "https://github.com/symfony/dependency-injection/tree/v7.0.4" }, "funding": [ { @@ -2231,7 +2153,7 @@ "type": "tidelift" } ], - "time": "2023-12-01T15:10:06+00:00" + "time": "2024-02-22T20:27:20+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2302,16 +2224,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "5e8e95e8389d03f2f3ae16a6c7c804849ed483b5" + "reference": "aded7ef586f9c75a04627326a5748f29ceba3506" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/5e8e95e8389d03f2f3ae16a6c7c804849ed483b5", - "reference": "5e8e95e8389d03f2f3ae16a6c7c804849ed483b5", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/aded7ef586f9c75a04627326a5748f29ceba3506", + "reference": "aded7ef586f9c75a04627326a5748f29ceba3506", "shasum": "" }, "require": { @@ -2388,7 +2310,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v7.0.0" + "source": "https://github.com/symfony/doctrine-bridge/tree/v7.0.4" }, "funding": [ { @@ -2404,20 +2326,20 @@ "type": "tidelift" } ], - "time": "2023-11-17T16:04:05+00:00" + "time": "2024-02-04T16:21:40+00:00" }, { "name": "symfony/dotenv", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "782d55cb978921499086df75888bda88888ad1c3" + "reference": "8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/782d55cb978921499086df75888bda88888ad1c3", - "reference": "782d55cb978921499086df75888bda88888ad1c3", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067", + "reference": "8017ea2f0ff4fbda6ae1bf3f5409d5ecff982067", "shasum": "" }, "require": { @@ -2462,7 +2384,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v7.0.0" + "source": "https://github.com/symfony/dotenv/tree/v7.0.4" }, "funding": [ { @@ -2478,20 +2400,20 @@ "type": "tidelift" } ], - "time": "2023-10-26T18:22:49+00:00" + "time": "2024-02-09T10:53:15+00:00" }, { "name": "symfony/error-handler", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "80b1258be1b84c12a345d0ec3881bbf2e5270cc2" + "reference": "677b24759decff69e65b1e9d1471d90f95ced880" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/80b1258be1b84c12a345d0ec3881bbf2e5270cc2", - "reference": "80b1258be1b84c12a345d0ec3881bbf2e5270cc2", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/677b24759decff69e65b1e9d1471d90f95ced880", + "reference": "677b24759decff69e65b1e9d1471d90f95ced880", "shasum": "" }, "require": { @@ -2537,7 +2459,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.0.0" + "source": "https://github.com/symfony/error-handler/tree/v7.0.4" }, "funding": [ { @@ -2553,20 +2475,20 @@ "type": "tidelift" } ], - "time": "2023-10-20T16:35:23+00:00" + "time": "2024-02-22T20:27:20+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.0.0", + "version": "v7.0.3", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e" + "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c459b40ffe67c49af6fd392aac374c9edf8a027e", - "reference": "c459b40ffe67c49af6fd392aac374c9edf8a027e", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/834c28d533dd0636f910909d01b9ff45cc094b5e", + "reference": "834c28d533dd0636f910909d01b9ff45cc094b5e", "shasum": "" }, "require": { @@ -2617,7 +2539,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.0.3" }, "funding": [ { @@ -2633,7 +2555,7 @@ "type": "tidelift" } ], - "time": "2023-07-27T16:29:09+00:00" + "time": "2024-01-23T15:02:46+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -2711,81 +2633,18 @@ ], "time": "2023-05-23T14:45:45+00:00" }, - { - "name": "symfony/expression-language", - "version": "v7.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/expression-language.git", - "reference": "46520d885b340f44a369967835a4bec178d7ab3d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/expression-language/zipball/46520d885b340f44a369967835a4bec178d7ab3d", - "reference": "46520d885b340f44a369967835a4bec178d7ab3d", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/cache": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\ExpressionLanguage\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an engine that can compile and evaluate expressions", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/expression-language/tree/v7.0.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2023-07-27T16:29:09+00:00" - }, { "name": "symfony/filesystem", - "version": "v7.0.0", + "version": "v7.0.3", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7" + "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/7da8ea2362a283771478c5f7729cfcb43a76b8b7", - "reference": "7da8ea2362a283771478c5f7729cfcb43a76b8b7", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/2890e3a825bc0c0558526c04499c13f83e1b6b12", + "reference": "2890e3a825bc0c0558526c04499c13f83e1b6b12", "shasum": "" }, "require": { @@ -2819,7 +2678,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.0.0" + "source": "https://github.com/symfony/filesystem/tree/v7.0.3" }, "funding": [ { @@ -2835,7 +2694,7 @@ "type": "tidelift" } ], - "time": "2023-07-27T06:33:22+00:00" + "time": "2024-01-23T15:02:46+00:00" }, { "name": "symfony/finder", @@ -2903,16 +2762,16 @@ }, { "name": "symfony/flex", - "version": "v2.4.2", + "version": "v2.4.5", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "67ee785f1aedada76461de7a7ec10cd7f8ff8d36" + "reference": "b0a405f40614c9f584b489d54f91091817b0e26e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/67ee785f1aedada76461de7a7ec10cd7f8ff8d36", - "reference": "67ee785f1aedada76461de7a7ec10cd7f8ff8d36", + "url": "https://api.github.com/repos/symfony/flex/zipball/b0a405f40614c9f584b489d54f91091817b0e26e", + "reference": "b0a405f40614c9f584b489d54f91091817b0e26e", "shasum": "" }, "require": { @@ -2948,7 +2807,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.4.2" + "source": "https://github.com/symfony/flex/tree/v2.4.5" }, "funding": [ { @@ -2964,20 +2823,20 @@ "type": "tidelift" } ], - "time": "2023-12-05T14:09:35+00:00" + "time": "2024-03-02T08:16:47+00:00" }, { "name": "symfony/framework-bundle", - "version": "v7.0.1", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "71f790cc6a81828b810c830ad72b1bc4c8ceaf74" + "reference": "b58bcb2f9c32405b8fbaa24a1e38c8a10bad7b21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/71f790cc6a81828b810c830ad72b1bc4c8ceaf74", - "reference": "71f790cc6a81828b810c830ad72b1bc4c8ceaf74", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/b58bcb2f9c32405b8fbaa24a1e38c8a10bad7b21", + "reference": "b58bcb2f9c32405b8fbaa24a1e38c8a10bad7b21", "shasum": "" }, "require": { @@ -3015,7 +2874,7 @@ "symfony/mime": "<6.4", "symfony/property-access": "<6.4", "symfony/property-info": "<6.4", - "symfony/scheduler": "<6.4", + "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", "symfony/security-core": "<6.4", "symfony/security-csrf": "<6.4", "symfony/serializer": "<6.4", @@ -3053,7 +2912,7 @@ "symfony/process": "^6.4|^7.0", "symfony/property-info": "^6.4|^7.0", "symfony/rate-limiter": "^6.4|^7.0", - "symfony/scheduler": "^6.4|^7.0", + "symfony/scheduler": "^6.4.4|^7.0.4", "symfony/security-bundle": "^6.4|^7.0", "symfony/semaphore": "^6.4|^7.0", "symfony/serializer": "^6.4|^7.0", @@ -3094,7 +2953,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.0.1" + "source": "https://github.com/symfony/framework-bundle/tree/v7.0.4" }, "funding": [ { @@ -3110,20 +2969,20 @@ "type": "tidelift" } ], - "time": "2023-12-01T16:37:31+00:00" + "time": "2024-02-26T07:52:39+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "47d72323200934694def5d57083899d774a2b110" + "reference": "439fdfdd344943254b1ef6278613e79040548045" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/47d72323200934694def5d57083899d774a2b110", - "reference": "47d72323200934694def5d57083899d774a2b110", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/439fdfdd344943254b1ef6278613e79040548045", + "reference": "439fdfdd344943254b1ef6278613e79040548045", "shasum": "" }, "require": { @@ -3171,7 +3030,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.0.0" + "source": "https://github.com/symfony/http-foundation/tree/v7.0.4" }, "funding": [ { @@ -3187,20 +3046,20 @@ "type": "tidelift" } ], - "time": "2023-11-07T15:10:37+00:00" + "time": "2024-02-08T19:22:56+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.0.1", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "4787639523dcdda32f69063f7fca2ad709f3c6d2" + "reference": "065e2234d907c0fc4fc942bf223f7cfc016d0ac7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4787639523dcdda32f69063f7fca2ad709f3c6d2", - "reference": "4787639523dcdda32f69063f7fca2ad709f3c6d2", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/065e2234d907c0fc4fc942bf223f7cfc016d0ac7", + "reference": "065e2234d907c0fc4fc942bf223f7cfc016d0ac7", "shasum": "" }, "require": { @@ -3248,7 +3107,7 @@ "symfony/process": "^6.4|^7.0", "symfony/property-access": "^6.4|^7.0", "symfony/routing": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", "symfony/stopwatch": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3", @@ -3283,7 +3142,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.0.1" + "source": "https://github.com/symfony/http-kernel/tree/v7.0.4" }, "funding": [ { @@ -3299,20 +3158,20 @@ "type": "tidelift" } ], - "time": "2023-12-01T17:08:48+00:00" + "time": "2024-02-27T06:35:35+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "875e90aeea2777b6f135677f618529449334a612" + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", - "reference": "875e90aeea2777b6f135677f618529449334a612", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", "shasum": "" }, "require": { @@ -3323,9 +3182,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3364,7 +3220,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" }, "funding": [ { @@ -3380,20 +3236,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", + "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", "shasum": "" }, "require": { @@ -3404,9 +3260,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3448,7 +3301,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" }, "funding": [ { @@ -3464,20 +3317,20 @@ "type": "tidelift" } ], - "time": "2023-01-26T09:26:14+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", "shasum": "" }, "require": { @@ -3491,9 +3344,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3531,7 +3381,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" }, "funding": [ { @@ -3547,20 +3397,20 @@ "type": "tidelift" } ], - "time": "2023-07-28T09:04:16+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.28.0", + "version": "v1.29.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11" + "reference": "86fcae159633351e5fd145d1c47de6c528f8caff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", - "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/86fcae159633351e5fd145d1c47de6c528f8caff", + "reference": "86fcae159633351e5fd145d1c47de6c528f8caff", "shasum": "" }, "require": { @@ -3569,9 +3419,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -3611,7 +3458,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.29.0" }, "funding": [ { @@ -3627,20 +3474,20 @@ "type": "tidelift" } ], - "time": "2023-08-16T06:22:46+00:00" + "time": "2024-01-29T20:11:03+00:00" }, { "name": "symfony/property-access", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "740e8cb8c54a4f16c82179e8558c29d9fc49901d" + "reference": "44e3746d4de8d0961a44ee332c74dd0918266127" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/740e8cb8c54a4f16c82179e8558c29d9fc49901d", - "reference": "740e8cb8c54a4f16c82179e8558c29d9fc49901d", + "url": "https://api.github.com/repos/symfony/property-access/zipball/44e3746d4de8d0961a44ee332c74dd0918266127", + "reference": "44e3746d4de8d0961a44ee332c74dd0918266127", "shasum": "" }, "require": { @@ -3687,7 +3534,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v7.0.0" + "source": "https://github.com/symfony/property-access/tree/v7.0.4" }, "funding": [ { @@ -3703,20 +3550,20 @@ "type": "tidelift" } ], - "time": "2023-09-27T14:05:33+00:00" + "time": "2024-02-16T13:44:10+00:00" }, { "name": "symfony/property-info", - "version": "v7.0.0", + "version": "v7.0.3", "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "ce627df05f5629ce4feec536ee827ad0a12689b6" + "reference": "e160f92ea827243abf2dbf36b8460b1377194406" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/ce627df05f5629ce4feec536ee827ad0a12689b6", - "reference": "ce627df05f5629ce4feec536ee827ad0a12689b6", + "url": "https://api.github.com/repos/symfony/property-info/zipball/e160f92ea827243abf2dbf36b8460b1377194406", + "reference": "e160f92ea827243abf2dbf36b8460b1377194406", "shasum": "" }, "require": { @@ -3770,7 +3617,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.0.0" + "source": "https://github.com/symfony/property-info/tree/v7.0.3" }, "funding": [ { @@ -3786,20 +3633,20 @@ "type": "tidelift" } ], - "time": "2023-11-25T08:38:27+00:00" + "time": "2024-01-23T15:02:46+00:00" }, { "name": "symfony/routing", - "version": "v7.0.1", + "version": "v7.0.3", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "fc55062907669835af6408558ae4d1dafef74b1e" + "reference": "858b26756ffc35a11238b269b484ee3a393a74d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/fc55062907669835af6408558ae4d1dafef74b1e", - "reference": "fc55062907669835af6408558ae4d1dafef74b1e", + "url": "https://api.github.com/repos/symfony/routing/zipball/858b26756ffc35a11238b269b484ee3a393a74d3", + "reference": "858b26756ffc35a11238b269b484ee3a393a74d3", "shasum": "" }, "require": { @@ -3851,7 +3698,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.0.1" + "source": "https://github.com/symfony/routing/tree/v7.0.3" }, "funding": [ { @@ -3867,20 +3714,20 @@ "type": "tidelift" } ], - "time": "2023-12-01T15:10:06+00:00" + "time": "2024-01-30T13:55:15+00:00" }, { "name": "symfony/runtime", - "version": "v7.0.0", + "version": "v7.0.3", "source": { "type": "git", "url": "https://github.com/symfony/runtime.git", - "reference": "65a4e69b1cdcee4f4f7a619a41d4b7ec79e85406" + "reference": "ef2c2fd4b40fb8cd22221154399ad8888e81cdb5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/runtime/zipball/65a4e69b1cdcee4f4f7a619a41d4b7ec79e85406", - "reference": "65a4e69b1cdcee4f4f7a619a41d4b7ec79e85406", + "url": "https://api.github.com/repos/symfony/runtime/zipball/ef2c2fd4b40fb8cd22221154399ad8888e81cdb5", + "reference": "ef2c2fd4b40fb8cd22221154399ad8888e81cdb5", "shasum": "" }, "require": { @@ -3930,7 +3777,7 @@ "runtime" ], "support": { - "source": "https://github.com/symfony/runtime/tree/v7.0.0" + "source": "https://github.com/symfony/runtime/tree/v7.0.3" }, "funding": [ { @@ -3946,25 +3793,25 @@ "type": "tidelift" } ], - "time": "2023-10-20T16:35:23+00:00" + "time": "2024-01-23T15:02:46+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.4.0", + "version": "v3.4.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", - "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", + "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", "shasum": "" }, "require": { "php": ">=8.1", - "psr/container": "^2.0" + "psr/container": "^1.1|^2.0" }, "conflict": { "ext-psr": "<1.1|>=2" @@ -4012,7 +3859,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" }, "funding": [ { @@ -4028,20 +3875,20 @@ "type": "tidelift" } ], - "time": "2023-07-30T20:28:31+00:00" + "time": "2023-12-26T14:02:43+00:00" }, { "name": "symfony/string", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620" + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/92bd2bfbba476d4a1838e5e12168bef2fd1e6620", - "reference": "92bd2bfbba476d4a1838e5e12168bef2fd1e6620", + "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", + "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", "shasum": "" }, "require": { @@ -4098,7 +3945,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.0.0" + "source": "https://github.com/symfony/string/tree/v7.0.4" }, "funding": [ { @@ -4114,20 +3961,20 @@ "type": "tidelift" } ], - "time": "2023-11-29T08:40:23+00:00" + "time": "2024-02-01T13:17:36+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.4.0", + "version": "v3.4.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5" + "reference": "06450585bf65e978026bda220cdebca3f867fde7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/dee0c6e5b4c07ce851b462530088e64b255ac9c5", - "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/06450585bf65e978026bda220cdebca3f867fde7", + "reference": "06450585bf65e978026bda220cdebca3f867fde7", "shasum": "" }, "require": { @@ -4176,7 +4023,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.4.1" }, "funding": [ { @@ -4192,20 +4039,20 @@ "type": "tidelift" } ], - "time": "2023-07-25T15:08:44+00:00" + "time": "2023-12-26T14:02:43+00:00" }, { "name": "symfony/twig-bridge", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "883b7b035670b02e06f1dd6cbe71fa1dabcdec7d" + "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/883b7b035670b02e06f1dd6cbe71fa1dabcdec7d", - "reference": "883b7b035670b02e06f1dd6cbe71fa1dabcdec7d", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", + "reference": "d16aa4eb5bdaeb6e7407782431dc70530f3b1df5", "shasum": "" }, "require": { @@ -4248,7 +4095,7 @@ "symfony/security-core": "^6.4|^7.0", "symfony/security-csrf": "^6.4|^7.0", "symfony/security-http": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", + "symfony/serializer": "^6.4.3|^7.0.3", "symfony/stopwatch": "^6.4|^7.0", "symfony/translation": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0", @@ -4284,7 +4131,7 @@ "description": "Provides integration for Twig with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bridge/tree/v7.0.0" + "source": "https://github.com/symfony/twig-bridge/tree/v7.0.4" }, "funding": [ { @@ -4300,20 +4147,20 @@ "type": "tidelift" } ], - "time": "2023-11-26T15:16:53+00:00" + "time": "2024-02-15T11:33:06+00:00" }, { "name": "symfony/twig-bundle", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "42c4a60f1b83894cd85a6b00533f8216c413ac11" + "reference": "acab2368f53491e018bf31ef48b39df55a6812ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/42c4a60f1b83894cd85a6b00533f8216c413ac11", - "reference": "42c4a60f1b83894cd85a6b00533f8216c413ac11", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/acab2368f53491e018bf31ef48b39df55a6812ef", + "reference": "acab2368f53491e018bf31ef48b39df55a6812ef", "shasum": "" }, "require": { @@ -4368,7 +4215,7 @@ "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v7.0.0" + "source": "https://github.com/symfony/twig-bundle/tree/v7.0.4" }, "funding": [ { @@ -4384,20 +4231,20 @@ "type": "tidelift" } ], - "time": "2023-11-26T15:16:53+00:00" + "time": "2024-02-15T11:33:06+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.0.0", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "cf0220fc7607476fd0d001ab3ed9e830d1fdda56" + "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cf0220fc7607476fd0d001ab3ed9e830d1fdda56", - "reference": "cf0220fc7607476fd0d001ab3ed9e830d1fdda56", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e03ad7c1535e623edbb94c22cc42353e488c6670", + "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670", "shasum": "" }, "require": { @@ -4451,7 +4298,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.0.0" + "source": "https://github.com/symfony/var-dumper/tree/v7.0.4" }, "funding": [ { @@ -4467,20 +4314,20 @@ "type": "tidelift" } ], - "time": "2023-11-27T12:39:18+00:00" + "time": "2024-02-15T11:33:06+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.0.1", + "version": "v7.0.4", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "a3d7c877414fcd59ab7075ecdc3b8f9c00f7bcc3" + "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/a3d7c877414fcd59ab7075ecdc3b8f9c00f7bcc3", - "reference": "a3d7c877414fcd59ab7075ecdc3b8f9c00f7bcc3", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", + "reference": "dfb0acb6803eb714f05d97dd4c5abe6d5fa9fe41", "shasum": "" }, "require": { @@ -4525,7 +4372,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.0.1" + "source": "https://github.com/symfony/var-exporter/tree/v7.0.4" }, "funding": [ { @@ -4541,20 +4388,20 @@ "type": "tidelift" } ], - "time": "2023-11-30T11:38:21+00:00" + "time": "2024-02-26T10:35:24+00:00" }, { "name": "symfony/yaml", - "version": "v7.0.0", + "version": "v7.0.3", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "0055b230c408428b9b5cde7c55659555be5c0278" + "reference": "2d4fca631c00700597e9442a0b2451ce234513d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/0055b230c408428b9b5cde7c55659555be5c0278", - "reference": "0055b230c408428b9b5cde7c55659555be5c0278", + "url": "https://api.github.com/repos/symfony/yaml/zipball/2d4fca631c00700597e9442a0b2451ce234513d3", + "reference": "2d4fca631c00700597e9442a0b2451ce234513d3", "shasum": "" }, "require": { @@ -4596,7 +4443,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.0.0" + "source": "https://github.com/symfony/yaml/tree/v7.0.3" }, "funding": [ { @@ -4612,7 +4459,7 @@ "type": "tidelift" } ], - "time": "2023-11-07T10:26:03+00:00" + "time": "2024-01-23T15:02:46+00:00" }, { "name": "twig/twig", @@ -4689,1632 +4536,69 @@ ], "packages-dev": [ { - "name": "myclabs/deep-copy", - "version": "1.11.1", + "name": "symfony/phpunit-bridge", + "version": "v7.0.4", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "url": "https://github.com/symfony/phpunit-bridge.git", + "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/54ca13ec990a40411ad978e08d994fca6cdd865f", + "reference": "54ca13ec990a40411ad978e08d994fca6cdd865f", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": ">=7.2.5" }, "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v4.17.1", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": ">=7.0" + "phpunit/phpunit": "<7.5|9.1.2" }, "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/error-handler": "^5.4|^6.4|^7.0", + "symfony/polyfill-php81": "^1.27" }, "bin": [ - "bin/php-parse" + "bin/simple-phpunit" ], - "type": "library", + "type": "symfony-bridge", "extra": { - "branch-alias": { - "dev-master": "4.9-dev" + "thanks": { + "name": "phpunit/phpunit", + "url": "https://github.com/sebastianbergmann/phpunit" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "PhpParser\\": "lib/PhpParser" - } + "Symfony\\Bridge\\PhpUnit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Nikita Popov" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], + "description": "Provides utilities for PHPUnit, especially user deprecation notices management", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" - }, - "time": "2023-08-13T19:53:39+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" - }, - "time": "2021-07-20T11:28:43+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "10.1.9", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "a56a9ab2f680246adcf3db43f38ddf1765774735" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/a56a9ab2f680246adcf3db43f38ddf1765774735", - "reference": "a56a9ab2f680246adcf3db43f38ddf1765774735", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.15", - "php": ">=8.1", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-text-template": "^3.0", - "sebastian/code-unit-reverse-lookup": "^3.0", - "sebastian/complexity": "^3.0", - "sebastian/environment": "^6.0", - "sebastian/lines-of-code": "^2.0", - "sebastian/version": "^4.0", - "theseer/tokenizer": "^1.2.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.1" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "10.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.9" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-11-23T12:23:20+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "4.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-31T06:24:48+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^10.0" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:56:09+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-31T14:07:24+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:57:52+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "10.5.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5aedff46afba98dddecaa12349ec044d9103d4fe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5aedff46afba98dddecaa12349ec044d9103d4fe", - "reference": "5aedff46afba98dddecaa12349ec044d9103d4fe", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.5", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-invoker": "^4.0", - "phpunit/php-text-template": "^3.0", - "phpunit/php-timer": "^6.0", - "sebastian/cli-parser": "^2.0", - "sebastian/code-unit": "^2.0", - "sebastian/comparator": "^5.0", - "sebastian/diff": "^5.0", - "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.1", - "sebastian/global-state": "^6.0.1", - "sebastian/object-enumerator": "^5.0", - "sebastian/recursion-context": "^5.0", - "sebastian/type": "^4.0", - "sebastian/version": "^4.0" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "10.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.2" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2023-12-05T14:54:33+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae", - "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:58:15+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:58:43+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T06:59:15+00:00" - }, - { - "name": "sebastian/comparator", - "version": "5.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-14T13:18:12+00:00" - }, - { - "name": "sebastian/complexity", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", - "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.10", - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-09-28T11:50:59+00:00" - }, - { - "name": "sebastian/diff", - "version": "5.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", - "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-05-01T07:48:21+00:00" - }, - { - "name": "sebastian/environment", - "version": "6.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951", - "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-04-11T05:39:26+00:00" - }, - { - "name": "sebastian/exporter", - "version": "5.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc", - "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-09-24T13:22:09+00:00" - }, - { - "name": "sebastian/global-state", - "version": "6.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4", - "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-07-19T07:19:23+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", - "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.10", - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-08-31T09:25:50+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "5.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:08:32+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:06:18+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "5.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:05:40+00:00" - }, - { - "name": "sebastian/type", - "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-03T07:10:45+00:00" - }, - { - "name": "sebastian/version", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2023-02-07T11:34:05+00:00" - }, - { - "name": "symfony/phpunit-bridge", - "version": "v7.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "c2d059b25e31274157dd7727131cd1cf33650207" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/c2d059b25e31274157dd7727131cd1cf33650207", - "reference": "c2d059b25e31274157dd7727131cd1cf33650207", - "shasum": "" - }, - "require": { - "php": ">=7.2.5" - }, - "conflict": { - "phpunit/phpunit": "<7.5|9.1.2" - }, - "require-dev": { - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/error-handler": "^5.4|^6.4|^7.0", - "symfony/polyfill-php81": "^1.27" - }, - "bin": [ - "bin/simple-phpunit" - ], - "type": "symfony-bridge", - "extra": { - "thanks": { - "name": "phpunit/phpunit", - "url": "https://github.com/sebastianbergmann/phpunit" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Bridge\\PhpUnit\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides utilities for PHPUnit, especially user deprecation notices management", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v7.0.1" + "source": "https://github.com/symfony/phpunit-bridge/tree/v7.0.4" }, "funding": [ { @@ -6330,57 +4614,7 @@ "type": "tidelift" } ], - "time": "2023-12-01T09:26:31+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.2", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.2" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2023-11-20T00:12:19+00:00" + "time": "2024-02-08T19:22:56+00:00" } ], "aliases": [], diff --git a/demo/symfony7.x/config/bundles.php b/demo/symfony7.x/config/bundles.php index 7566e6ac..9336f355 100644 --- a/demo/symfony7.x/config/bundles.php +++ b/demo/symfony7.x/config/bundles.php @@ -3,6 +3,6 @@ return [ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], - Ambta\DoctrineEncryptBundle\AmbtaDoctrineEncryptBundle::class => ['all' => true], + DoctrineEncryptBundle\DoctrineEncryptBundle::class => ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], ]; diff --git a/demo/symfony7.x/config/packages/ambta_doctrine_encrypt.yaml b/demo/symfony7.x/config/packages/ambta_doctrine_encrypt.yaml deleted file mode 100644 index ddbd65af..00000000 --- a/demo/symfony7.x/config/packages/ambta_doctrine_encrypt.yaml +++ /dev/null @@ -1,3 +0,0 @@ -ambta_doctrine_encrypt: - enable_secret_generation: false - secret: '%env(HALITE_SECRET)%' \ No newline at end of file diff --git a/demo/symfony7.x/config/packages/doctrine.yaml b/demo/symfony7.x/config/packages/doctrine.yaml index 87d77a85..6ea6f4be 100644 --- a/demo/symfony7.x/config/packages/doctrine.yaml +++ b/demo/symfony7.x/config/packages/doctrine.yaml @@ -6,6 +6,8 @@ doctrine: # either here or in the DATABASE_URL env var (see .env file) #server_version: '13' orm: + report_fields_where_declared: true + enable_lazy_ghost_objects: true auto_generate_proxy_classes: true naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware auto_mapping: true diff --git a/demo/symfony7.x/config/packages/doctrine_encrypt.yaml b/demo/symfony7.x/config/packages/doctrine_encrypt.yaml new file mode 100644 index 00000000..76ec03d6 --- /dev/null +++ b/demo/symfony7.x/config/packages/doctrine_encrypt.yaml @@ -0,0 +1,3 @@ +doctrine_encrypt: + enable_secret_generation: false + secret: '%env(HALITE_SECRET)%' \ No newline at end of file diff --git a/demo/symfony7.x/phpunit.xml.dist b/demo/symfony7.x/phpunit.xml.dist index f317760c..ba1bd023 100644 --- a/demo/symfony7.x/phpunit.xml.dist +++ b/demo/symfony7.x/phpunit.xml.dist @@ -10,6 +10,7 @@ + @@ -26,7 +27,4 @@ includeUncoveredFiles="true" > - - - diff --git a/demo/symfony7.x/src/Entity/Attribute/Secret.php b/demo/symfony7.x/src/Entity/Attribute/Secret.php index c76a80db..7e001053 100644 --- a/demo/symfony7.x/src/Entity/Attribute/Secret.php +++ b/demo/symfony7.x/src/Entity/Attribute/Secret.php @@ -2,7 +2,6 @@ namespace App\Entity\Attribute; -use Ambta\DoctrineEncryptBundle\Configuration\Encrypted; use App\Repository\Attribute\SecretRepository; use Doctrine\ORM\Mapping as ORM; @@ -16,8 +15,7 @@ class Secret implements \App\Entity\SecretInterface private $name; - #[ORM\Column(type:"string",nullable:false)] - #[Encrypted] + #[ORM\Column(type:"encrypted",nullable:false)] private $secret; /** diff --git a/demo/symfony7.x/src/Repository/AbstractSecretRepository.php b/demo/symfony7.x/src/Repository/AbstractSecretRepository.php index 5caa75d1..840bc639 100644 --- a/demo/symfony7.x/src/Repository/AbstractSecretRepository.php +++ b/demo/symfony7.x/src/Repository/AbstractSecretRepository.php @@ -9,7 +9,7 @@ abstract class AbstractSecretRepository extends ServiceEntityRepository /** * @return array The objects. */ - public function findAll() + public function findAll(): array { $qb = $this->createQueryBuilder('s'); $qb->select('s') diff --git a/demo/symfony7.x/symfony.lock b/demo/symfony7.x/symfony.lock index bc57875d..52c86ecd 100644 --- a/demo/symfony7.x/symfony.lock +++ b/demo/symfony7.x/symfony.lock @@ -1,13 +1,4 @@ { - "doctrine/annotations": { - "version": "2.0", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "main", - "version": "1.10", - "ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05" - } - }, "doctrine/doctrine-bundle": { "version": "2.11", "recipe": { diff --git a/demo/symfony7.x/tests/SecretTest.php b/demo/symfony7.x/tests/SecretTest.php index ec038ed6..918d6891 100644 --- a/demo/symfony7.x/tests/SecretTest.php +++ b/demo/symfony7.x/tests/SecretTest.php @@ -2,7 +2,7 @@ namespace App\Tests; -use Ambta\DoctrineEncryptBundle\Subscribers\DoctrineEncryptSubscriber; +use DoctrineEncryptBundle\Service\Encrypt; use App\Entity; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -53,7 +53,7 @@ private function testSecretsAreEncryptedInDatabase(string $className) self::assertEquals($newSecretObject->getName(), $actualSecretObject->getName()); // Make sure it is encrypted self::assertNotEquals($newSecretObject->getSecret(),$actualRawSecret); - self::assertStringEndsWith(DoctrineEncryptSubscriber::ENCRYPTION_MARKER,$actualRawSecret); + self::assertStringEndsWith(Encrypt::ENCRYPTION_MARKER,$actualRawSecret); } /** diff --git a/demo/symfony7.x_doctrine3.x/.env b/demo/symfony7.x_doctrine3.x/.env new file mode 100644 index 00000000..751352a3 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/.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. +# https://symfony.com/doc/current/configuration/secrets.html +# +# 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=6856f2521a71b9cace5415c5b1269956 +###< 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://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" +# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" +DATABASE_PATH="var/data.db" +DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" +###< doctrine/doctrine-bundle ### diff --git a/demo/symfony7.x_doctrine3.x/.gitignore b/demo/symfony7.x_doctrine3.x/.gitignore new file mode 100644 index 00000000..ff329d37 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/.gitignore @@ -0,0 +1,22 @@ +# 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 ### diff --git a/demo/symfony7.x_doctrine3.x/README.md b/demo/symfony7.x_doctrine3.x/README.md new file mode 100644 index 00000000..5295fba5 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/README.md @@ -0,0 +1,28 @@ +This demo-installation demonstrates a simple symfony 7.x-application using only attributes and Doctrine 3.x. + +Annotations are no longer allowed in symfony 7.x + +# 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/symfony7.x_doctrine3.x/bin/console b/demo/symfony7.x_doctrine3.x/bin/console new file mode 100755 index 00000000..c933dc53 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/bin/console @@ -0,0 +1,17 @@ +#!/usr/bin/env php += 80000) { + require dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit'; + } else { + define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php'); + require PHPUNIT_COMPOSER_INSTALL; + PHPUnit\TextUI\Command::main(); + } +} else { + if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) { + echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n"; + exit(1); + } + + require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php'; +} diff --git a/demo/symfony7.x_doctrine3.x/composer.json b/demo/symfony7.x_doctrine3.x/composer.json new file mode 100644 index 00000000..06a3f1de --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/composer.json @@ -0,0 +1,80 @@ +{ + "type": "project", + "license": "proprietary", + "minimum-stability": "stable", + "prefer-stable": true, + "require": { + "php": ">=8.2", + "ext-ctype": "*", + "ext-iconv": "*", + "doctrine/orm": "^3.0", + "doctrineencryptbundle/doctrine-encrypt-bundle": "@dev", + "doctrine/persistence": "^3.2", + "paragonie/halite": "^5.0", + "symfony/console": "7.0.*", + "symfony/dotenv": "7.0.*", + "symfony/flex": "^2", + "symfony/framework-bundle": "7.0.*", + "symfony/runtime": "7.0.*", + "symfony/twig-bundle": "7.0.*", + "symfony/yaml": "7.0.*" + }, + "require-dev": { + "symfony/phpunit-bridge": "^7.0" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true, + "symfony/flex": true, + "symfony/runtime": true + }, + "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": "*", + "symfony/polyfill-php81": "*", + "symfony/polyfill-php82": "*" + }, + "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": "7.0.*" + } + }, + "repositories": [ + { + "type": "path", + "url": "../../" + } + ] +} diff --git a/demo/symfony7.x_doctrine3.x/config/bundles.php b/demo/symfony7.x_doctrine3.x/config/bundles.php new file mode 100644 index 00000000..9336f355 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/config/bundles.php @@ -0,0 +1,8 @@ + ['all' => true], + Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], + DoctrineEncryptBundle\DoctrineEncryptBundle::class => ['all' => true], + Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], +]; diff --git a/demo/symfony7.x_doctrine3.x/config/packages/cache.yaml b/demo/symfony7.x_doctrine3.x/config/packages/cache.yaml new file mode 100644 index 00000000..6899b720 --- /dev/null +++ b/demo/symfony7.x_doctrine3.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/symfony7.x_doctrine3.x/config/packages/doctrine.yaml b/demo/symfony7.x_doctrine3.x/config/packages/doctrine.yaml new file mode 100644 index 00000000..6ea6f4be --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/config/packages/doctrine.yaml @@ -0,0 +1,45 @@ +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: + report_fields_where_declared: true + enable_lazy_ghost_objects: true + auto_generate_proxy_classes: true + naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware + auto_mapping: true + mappings: + 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/symfony7.x_doctrine3.x/config/packages/doctrine_encrypt.yaml b/demo/symfony7.x_doctrine3.x/config/packages/doctrine_encrypt.yaml new file mode 100644 index 00000000..76ec03d6 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/config/packages/doctrine_encrypt.yaml @@ -0,0 +1,3 @@ +doctrine_encrypt: + enable_secret_generation: false + secret: '%env(HALITE_SECRET)%' \ No newline at end of file diff --git a/demo/symfony7.x_doctrine3.x/config/packages/framework.yaml b/demo/symfony7.x_doctrine3.x/config/packages/framework.yaml new file mode 100644 index 00000000..355bd4ce --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/config/packages/framework.yaml @@ -0,0 +1,23 @@ +# see https://symfony.com/doc/current/reference/configuration/framework.html +framework: + secret: '%env(APP_SECRET)%' + #csrf_protection: true + handle_all_throwables: 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 + +when@test: + framework: + test: true + session: + storage_factory_id: session.storage.factory.mock_file diff --git a/demo/symfony7.x_doctrine3.x/config/packages/routing.yaml b/demo/symfony7.x_doctrine3.x/config/packages/routing.yaml new file mode 100644 index 00000000..4b766ce5 --- /dev/null +++ b/demo/symfony7.x_doctrine3.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/symfony7.x_doctrine3.x/config/packages/twig.yaml b/demo/symfony7.x_doctrine3.x/config/packages/twig.yaml new file mode 100644 index 00000000..3cc1a452 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/config/packages/twig.yaml @@ -0,0 +1,8 @@ +twig: + default_path: '%kernel.project_dir%/templates' + globals: + appVersion: "7.x" + +when@test: + twig: + strict_variables: true diff --git a/demo/symfony7.x_doctrine3.x/config/preload.php b/demo/symfony7.x_doctrine3.x/config/preload.php new file mode 100644 index 00000000..5ebcdb21 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/config/preload.php @@ -0,0 +1,5 @@ + + + + + + + + + + + + + + + + + tests + + + + + + diff --git a/demo/symfony7.x_doctrine3.x/public/index.php b/demo/symfony7.x_doctrine3.x/public/index.php new file mode 100644 index 00000000..9982c218 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/public/index.php @@ -0,0 +1,9 @@ +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 === 'attribute') { + $secret = new \App\Entity\Attribute\Secret(); + } else { + return new Response('Type is only allowed to be "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/symfony7.x_doctrine3.x/src/Entity/.gitignore b/demo/symfony7.x_doctrine3.x/src/Entity/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/demo/symfony7.x_doctrine3.x/src/Entity/Attribute/Secret.php b/demo/symfony7.x_doctrine3.x/src/Entity/Attribute/Secret.php new file mode 100644 index 00000000..7e001053 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/src/Entity/Attribute/Secret.php @@ -0,0 +1,92 @@ +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/symfony7.x_doctrine3.x/src/Entity/SecretInterface.php b/demo/symfony7.x_doctrine3.x/src/Entity/SecretInterface.php new file mode 100644 index 00000000..cd234732 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/src/Entity/SecretInterface.php @@ -0,0 +1,11 @@ + The objects. + */ + public function findAll(): array + { + $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/symfony7.x_doctrine3.x/src/Repository/Attribute/SecretRepository.php b/demo/symfony7.x_doctrine3.x/src/Repository/Attribute/SecretRepository.php new file mode 100644 index 00000000..6cd86e30 --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/src/Repository/Attribute/SecretRepository.php @@ -0,0 +1,20 @@ +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(Encrypt::ENCRYPTION_MARKER,$actualRawSecret); + } + + /** + * @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/symfony7.x_doctrine3.x/tests/bootstrap.php b/demo/symfony7.x_doctrine3.x/tests/bootstrap.php new file mode 100644 index 00000000..469dccee --- /dev/null +++ b/demo/symfony7.x_doctrine3.x/tests/bootstrap.php @@ -0,0 +1,11 @@ +bootEnv(dirname(__DIR__).'/.env'); +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist index e0767699..4db01306 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -18,17 +18,19 @@ ./Command - ./Configuration ./DependencyInjection ./Encryptors - ./Subscribers + ./Factories + ./Service + ./Traits + ./Types - + diff --git a/src/AmbtaDoctrineEncryptBundle.php b/src/AmbtaDoctrineEncryptBundle.php deleted file mode 100644 index 11b24614..00000000 --- a/src/AmbtaDoctrineEncryptBundle.php +++ /dev/null @@ -1,17 +0,0 @@ - **/ abstract class AbstractCommand extends Command { @@ -22,31 +18,20 @@ abstract class AbstractCommand extends Command protected $entityManager; /** - * @var DoctrineEncryptSubscriber + * @var Encrypt */ - protected $subscriber; + protected $service; /** - * @var Reader|AttributeReader + * @param ContainerInterface $container + * + * @return void */ - protected $annotationReader; - - /** - * AbstractCommand constructor. - * - * @param EntityManager $entityManager - * @param Reader|AttributeReader $annotationReader - * @param DoctrineEncryptSubscriber $subscriber - */ - public function __construct( - EntityManagerInterface $entityManager, - $annotationReader, - DoctrineEncryptSubscriber $subscriber - ) { + public function __construct(EntityManagerInterface $entityManager, Encrypt $service) + { parent::__construct(); $this->entityManager = $entityManager; - $this->annotationReader = $annotationReader; - $this->subscriber = $subscriber; + $this->service = $service; } /** @@ -75,53 +60,4 @@ protected function getTableCount(string $entityName): int return (int) $query->getSingleScalarResult(); } - - /** - * Return an array of entity-metadata for all entities - * that have at least one encrypted property. - * - * @return array - */ - protected function getEncryptionableEntityMetaData(): array - { - $validMetaData = []; - $metaDataArray = $this->entityManager->getMetadataFactory()->getAllMetadata(); - - foreach ($metaDataArray as $entityMetaData) - { - if ($entityMetaData instanceof ClassMetadataInfo and $entityMetaData->isMappedSuperclass) { - continue; - } - - $properties = $this->getEncryptionableProperties($entityMetaData); - if (count($properties) == 0) { - continue; - } - - $validMetaData[] = $entityMetaData; - } - - return $validMetaData; - } - - /** - * @param $entityMetaData - * - * @return array - */ - protected function getEncryptionableProperties($entityMetaData): array - { - //Create reflectionClass for each meta data object - $reflectionClass = new \ReflectionClass($entityMetaData->name); - $propertyArray = $reflectionClass->getProperties(); - $properties = []; - - foreach ($propertyArray as $property) { - if ($this->annotationReader->getPropertyAnnotation($property, 'Ambta\DoctrineEncryptBundle\Configuration\Encrypted')) { - $properties[] = $property; - } - } - - return $properties; - } } diff --git a/src/Command/DoctrineDecryptDatabaseCommand.php b/src/Command/DoctrineDecryptDatabaseCommand.php index 592fa87f..da8f91bb 100644 --- a/src/Command/DoctrineDecryptDatabaseCommand.php +++ b/src/Command/DoctrineDecryptDatabaseCommand.php @@ -1,21 +1,20 @@ - * @author Michael Feinbier */ class DoctrineDecryptDatabaseCommand extends AbstractCommand { @@ -37,22 +36,22 @@ protected function configure(): void */ protected function execute(InputInterface $input, OutputInterface $output): int { - // Get entity manager, question helper, subscriber service and annotation reader + // Get entity manager, question helper and service $question = $this->getHelper('question'); + $batchSize = $input->getArgument('batchSize'); // Get list of supported encryptors $supportedExtensions = DoctrineEncryptExtension::SupportedEncryptorClasses; - $batchSize = $input->getArgument('batchSize'); // If encryptor has been set use that encryptor else use default if ($input->getArgument('encryptor')) { if (isset($supportedExtensions[$input->getArgument('encryptor')])) { $reflection = new \ReflectionClass($supportedExtensions[$input->getArgument('encryptor')]); $encryptor = $reflection->newInstance(); - $this->subscriber->setEncryptor($encryptor); + $this->service->setEncryptor($encryptor); } else { if (class_exists($input->getArgument('encryptor'))) { - $this->subscriber->setEncryptor($input->getArgument('encryptor')); + $this->service->setEncryptor($input->getArgument('encryptor')); } else { $output->writeln('Given encryptor does not exists'); @@ -63,18 +62,25 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } + $encryptTypes = array_keys(DoctrineEncryptBundle::ENCRYPT_TYPES); + + $totalPropertyCount = 0; + $encryptDetails = []; // Get entity manager metadata $metaDataArray = $this->entityManager->getMetadataFactory()->getAllMetadata(); - - // Set counter and loop through entity manager meta data - $propertyCount = 0; foreach ($metaDataArray as $metaData) { - if ($metaData instanceof ClassMetadataInfo and $metaData->isMappedSuperclass) { + if ($metaData instanceof ClassMetadata && $metaData->isMappedSuperclass) { continue; } - $countProperties = count($this->getEncryptionableProperties($metaData)); - $propertyCount += $countProperties; + foreach ($metaData->fieldMappings as $fieldMapping) { + if (in_array ($fieldMapping['type'], $encryptTypes)) { + if (! array_key_exists($metaData->name, $encryptDetails)) { + $encryptDetails[$metaData->name] = $metaData; + } + $totalPropertyCount++; + } + } } $defaultAnswer = false; @@ -87,8 +93,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $confirmationQuestion = new ConfirmationQuestion( - '' . count($metaDataArray) . ' entities found which are containing ' . $propertyCount . ' properties with the encryption tag. ' . PHP_EOL . '' . - 'Which are going to be decrypted with [' . get_class($this->subscriber->getEncryptor()) . ']. ' . PHP_EOL . '' . + '' . count($encryptDetails) . ' entities found which are containing properties with the encryption types.' . PHP_EOL . '' . + 'Which are going to be decrypted with [' . get_class($this->service->getEncryptor()) . ']. ' . PHP_EOL . ''. 'Wrong settings can mess up your data and it will be unrecoverable. ' . PHP_EOL . '' . 'I advise you to make a backup. ' . PHP_EOL . '' . 'Continue with this action? (y/yes)', $defaultAnswer @@ -99,44 +105,51 @@ protected function execute(InputInterface $input, OutputInterface $output): int } // Start decrypting database + $_ENV['DOCTRINE_SKIP_ENCRYPT'] = true; $output->writeln('' . PHP_EOL . 'Decrypting all fields. This can take up to several minutes depending on the database size.'); - $valueCounter = 0; - - // Loop through entity manager meta data - foreach ($this->getEncryptionableEntityMetaData() as $metaData) { + $pac = PropertyAccess::createPropertyAccessor(); + $unitOfWork = $this->entityManager->getUnitOfWork(); + foreach ($encryptDetails as $entityName => $classMeta) { $i = 0; - $iterator = $this->getEntityIterator($metaData->name); - $totalCount = $this->getTableCount($metaData->name); + $valueCounter = 0; + $iterator = $this->getEntityIterator($entityName); + $totalCount = $this->getTableCount($entityName); - $output->writeln(sprintf('Processing %s', $metaData->name)); + $output->writeln(sprintf('Processing %s\'s records', $entityName)); $progressBar = new ProgressBar($output, $totalCount); foreach ($iterator as $row) { $entity = (is_array($row) ? $row[0] : $row); - // Create reflectionClass for each entity - $entityReflectionClass = new \ReflectionClass($entity); - - //Get the current encryptor used - $encryptorUsed = $this->subscriber->getEncryptor(); - - //Loop through the property's in the entity - foreach ($this->getEncryptionableProperties($metaData) as $property) { - $methodeName = ucfirst($property->getName()); - - $getter = 'get' . $methodeName; - $setter = 'set' . $methodeName; - - //Check if getter and setter are set - if ($entityReflectionClass->hasMethod($getter) && $entityReflectionClass->hasMethod($setter)) { - $unencrypted = $entity->$getter(); - $entity->$setter($unencrypted); - $valueCounter++; + $changeData = []; + // tell the unit of work that an value has changed no matter if the value + // is actually different from the value already persistent + // need all the values checked for the count + foreach ($metaData->fieldMappings as $fieldMapping) { + if (in_array ($fieldMapping['type'], $encryptTypes)) { + $value = $pac->getValue($entity, $fieldMapping['fieldName']); + if (! is_null ($value) && ! empty($value)) + { + $valueCounter++; + array_push($changeData, ['fieldName' => $fieldMapping['fieldName'], 'value' => $value]); + } } } - $this->subscriber->setEncryptor(null); - $this->entityManager->persist($entity); + if (count($changeData)) + { + $originalData = $unitOfWork->getOriginalEntityData($entity); + foreach ($changeData as $changeDetail) + { + $originalData[$changeDetail['fieldName']] = null; + } + $unitOfWork->setOriginalEntityData($entity, $originalData); + foreach ($changeData as $changeDetail) + { + $unitOfWork->propertyChanged($entity, $changeDetail['fieldName'], null, $changeDetail['value']); + } + $this->entityManager->persist($entity); + } if (($i % $batchSize) === 0) { $this->entityManager->flush(); @@ -144,21 +157,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $progressBar->advance(1); $i++; - - $this->subscriber->setEncryptor($encryptorUsed); } - $progressBar->finish(); $output->writeln(''); - $encryptorUsed = $this->subscriber->getEncryptor(); - $this->subscriber->setEncryptor(null); $this->entityManager->flush(); $this->entityManager->clear(); - $this->subscriber->setEncryptor($encryptorUsed); } - $output->writeln('' . PHP_EOL . 'Decryption finished values found: ' . $valueCounter . ', decrypted: ' . $this->subscriber->decryptCounter . '.' . PHP_EOL . 'All values are now decrypted.'); + $output->writeln('' . PHP_EOL . 'Decryption finished. Estimated values decrypted: ' . $valueCounter . '.' . PHP_EOL . 'All values are now decrypted.'); return defined('AbstractCommand::SUCCESS') ? AbstractCommand::SUCCESS : 0; } diff --git a/src/Command/DoctrineEncryptDatabaseCommand.php b/src/Command/DoctrineEncryptDatabaseCommand.php index b376c9ac..7204dc88 100644 --- a/src/Command/DoctrineEncryptDatabaseCommand.php +++ b/src/Command/DoctrineEncryptDatabaseCommand.php @@ -1,20 +1,20 @@ - * @author Michael Feinbier */ class DoctrineEncryptDatabaseCommand extends AbstractCommand { @@ -36,7 +36,7 @@ protected function configure(): void */ protected function execute(InputInterface $input, OutputInterface $output): int { - // Get entity manager, question helper, subscriber service and annotation reader + // Get entity manager, question helper and service $question = $this->getHelper('question'); $batchSize = $input->getArgument('batchSize'); @@ -48,10 +48,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (isset($supportedExtensions[$input->getArgument('encryptor')])) { $reflection = new \ReflectionClass($supportedExtensions[$input->getArgument('encryptor')]); $encryptor = $reflection->newInstance(); - $this->subscriber->setEncryptor($encryptor); + $this->service->setEncryptor($encryptor); } else { if (class_exists($input->getArgument('encryptor'))) { - $this->subscriber->setEncryptor($input->getArgument('encryptor')); + $this->service->setEncryptor($input->getArgument('encryptor')); } else { $output->writeln('Given encryptor does not exists'); @@ -62,6 +62,27 @@ protected function execute(InputInterface $input, OutputInterface $output): int } } + $encryptTypes = array_keys(DoctrineEncryptBundle::ENCRYPT_TYPES); + + $totalPropertyCount = 0; + $encryptDetails = []; + // Get entity manager metadata + $metaDataArray = $this->entityManager->getMetadataFactory()->getAllMetadata(); + foreach ($metaDataArray as $metaData) { + if ($metaData instanceof ClassMetadata && $metaData->isMappedSuperclass) { + continue; + } + + foreach ($metaData->fieldMappings as $fieldMapping) { + if (in_array ($fieldMapping['type'], $encryptTypes)) { + if (! array_key_exists($metaData->name, $encryptDetails)) { + $encryptDetails[$metaData->name] = $metaData; + } + $totalPropertyCount++; + } + } + } + $defaultAnswer = false; $answer = $input->getOption('answer'); if ($answer) { @@ -72,10 +93,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int } // Get entity manager metadata - $metaDataArray = $this->getEncryptionableEntityMetaData(); $confirmationQuestion = new ConfirmationQuestion( - '' . count($metaDataArray) . ' entities found which are containing properties with the encryption tag.' . PHP_EOL . '' . - 'Which are going to be encrypted with [' . get_class($this->subscriber->getEncryptor()) . ']. ' . PHP_EOL . ''. + '' . count($encryptDetails) . ' entities found which are containing properties with the encryption tag.' . PHP_EOL . '' . + 'Which are going to be encrypted with [' . get_class($this->service->getEncryptor()) . ']. ' . PHP_EOL . ''. 'Wrong settings can mess up your data and it will be unrecoverable. ' . PHP_EOL . '' . 'I advise you to make a backup. ' . PHP_EOL . '' . 'Continue with this action? (y/yes)', $defaultAnswer @@ -85,37 +105,70 @@ protected function execute(InputInterface $input, OutputInterface $output): int return defined('AbstractCommand::FAILURE') ? AbstractCommand::FAILURE : 1; } - // Start decrypting database + // Start encrypting database $output->writeln('' . PHP_EOL . 'Encrypting all fields can take up to several minutes depending on the database size.'); - // Loop through entity manager meta data - foreach ($metaDataArray as $metaData) { + $pac = PropertyAccess::createPropertyAccessor(); + $unitOfWork = $this->entityManager->getUnitOfWork(); + foreach ($encryptDetails as $entityName => $classMeta) { $i = 0; - $iterator = $this->getEntityIterator($metaData->name); - $totalCount = $this->getTableCount($metaData->name); + $valueCounter = 0; + $iterator = $this->getEntityIterator($entityName); + $totalCount = $this->getTableCount($entityName); - $output->writeln(sprintf('Processing %s', $metaData->name)); + $output->writeln(sprintf('Processing %s\'s records', $entityName)); $progressBar = new ProgressBar($output, $totalCount); foreach ($iterator as $row) { $entity = (is_array($row) ? $row[0] : $row); - $this->subscriber->processFields($entity, $this->entityManager); - $this->entityManager->persist($entity); + + $changeData = []; + // tell the unit of work that an value has changed no matter if the value + // is actually different from the value already persistent + // need all the values checked for the count + foreach ($metaData->fieldMappings as $fieldMapping) { + if (in_array ($fieldMapping['type'], $encryptTypes)) { + $value = $pac->getValue($entity, $fieldMapping['fieldName']); + if (! is_null ($value) && ! empty($value)) + { + $valueCounter++; + array_push($changeData, ['fieldName' => $fieldMapping['fieldName'], 'value' => $value]); + } + } + } + + if (count($changeData)) + { + $originalData = $unitOfWork->getOriginalEntityData($entity); + foreach ($changeData as $changeDetail) + { + $originalData[$changeDetail['fieldName']] = null; + } + $unitOfWork->setOriginalEntityData($entity, $originalData); + foreach ($changeData as $changeDetail) + { + $unitOfWork->propertyChanged($entity, $changeDetail['fieldName'], null, $changeDetail['value']); + } + $this->entityManager->persist($entity); + } if (($i % $batchSize) === 0) { $this->entityManager->flush(); $this->entityManager->clear(); - $progressBar->advance($batchSize); } + $progressBar->advance(1); $i++; } $progressBar->finish(); $output->writeln(''); $this->entityManager->flush(); + $this->entityManager->clear(); + + // $classMeta->setChangeTrackingPolicy($ctp); } // Say it is finished - $output->writeln('Encryption finished. Values encrypted: ' . $this->subscriber->encryptCounter . ' values.' . PHP_EOL . 'All values are now encrypted.'); + $output->writeln('' . PHP_EOL . 'Encryption finished. Estimated values encrypted: ' . $valueCounter . '.' . PHP_EOL . 'All values are now encrypted.'); return defined('AbstractCommand::SUCCESS') ? AbstractCommand::SUCCESS : 0; } diff --git a/src/Command/DoctrineEncryptStatusCommand.php b/src/Command/DoctrineEncryptStatusCommand.php index 6ed6344e..66772b4f 100644 --- a/src/Command/DoctrineEncryptStatusCommand.php +++ b/src/Command/DoctrineEncryptStatusCommand.php @@ -1,16 +1,14 @@ - * @author Michael Feinbier */ class DoctrineEncryptStatusCommand extends AbstractCommand { @@ -29,19 +27,26 @@ protected function configure(): void */ protected function execute(InputInterface $input, OutputInterface $output): int { - $metaDataArray = $this->entityManager->getMetadataFactory()->getAllMetadata(); + $encryptTypes = array_keys(DoctrineEncryptBundle::ENCRYPT_TYPES); $totalCount = 0; + $encryptDetails = []; + // Get entity manager metadata + $metaDataArray = $this->entityManager->getMetadataFactory()->getAllMetadata(); foreach ($metaDataArray as $metaData) { - if ($metaData instanceof ClassMetadataInfo && $metaData->isMappedSuperclass) { + if ($metaData instanceof ClassMetadata && $metaData->isMappedSuperclass) { continue; } $count = 0; - $encryptedPropertiesCount = count($this->getEncryptionableProperties($metaData)); - if ($encryptedPropertiesCount > 0) { - $totalCount += $encryptedPropertiesCount; - $count += $encryptedPropertiesCount; + foreach ($metaData->fieldMappings as $fieldMapping) { + if (in_array ($fieldMapping['type'], $encryptTypes)) { + if (! array_key_exists($metaData->name, $encryptDetails)) { + $encryptDetails[$metaData->name] = true; + } + $count++; + $totalCount++; + } } if ($count > 0) { @@ -52,7 +57,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)); + $output->writeln(sprintf('%d entities found which contain %d encrypted properties.', count($encryptDetails), $totalCount)); return defined('AbstractCommand::SUCCESS') ? AbstractCommand::SUCCESS : 0; } diff --git a/src/Configuration/Annotation.php b/src/Configuration/Annotation.php deleted file mode 100644 index 0896617d..00000000 --- a/src/Configuration/Annotation.php +++ /dev/null @@ -1,12 +0,0 @@ - - * @Annotation - * @NamedArgumentConstructor - * @Target("PROPERTY") - */ -#[Attribute(Attribute::TARGET_PROPERTY)] -final class Encrypted implements Annotation -{ - private const ALLOWED_TYPES = [ - 'string', - 'datetime', - 'json', - 'array' - ]; - - /** - * @var string - */ - public $type; - - - - /** - * @param 'string'|'datetime'|'json'|'array' $type - */ - public function __construct(string $type = 'string') - { - if (!in_array ($type, self::ALLOWED_TYPES, true)) - { - throw new InvalidArgumentException (sprintf('%s is not a supported type for %s', $type, self::class)); - } - $this->type = $type; - } -} diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index b9a5a330..10d9ce57 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -1,6 +1,6 @@ getRootNode(); - } else { - // BC layer for symfony/config 4.1 and older - $rootNode = $treeBuilder->root('ambta_doctrine_encrypt'); - } + $treeBuilder = new TreeBuilder('doctrine_encrypt'); + $rootNode = $treeBuilder->getRootNode(); // Grammar of config tree $rootNode diff --git a/src/DependencyInjection/DoctrineEncryptExtension.php b/src/DependencyInjection/DoctrineEncryptExtension.php index 10d30237..f1ca7ea6 100644 --- a/src/DependencyInjection/DoctrineEncryptExtension.php +++ b/src/DependencyInjection/DoctrineEncryptExtension.php @@ -1,9 +1,9 @@ setParameter('ambta_doctrine_encrypt.encryptor_class_name', $config['encryptor_class_full']); - $container->setParameter('ambta_doctrine_encrypt.secret_directory_path',$config['secret_directory_path']); - $container->setParameter('ambta_doctrine_encrypt.enable_secret_generation',$config['enable_secret_generation']); + $container->setParameter('doctrine_encrypt.encryptor_class_name', $config['encryptor_class_full']); + $container->setParameter('doctrine_encrypt.secret_directory_path',$config['secret_directory_path']); + $container->setParameter('doctrine_encrypt.enable_secret_generation',$config['enable_secret_generation']); if (isset($config['secret'])) { - $container->setParameter('ambta_doctrine_encrypt.secret',$config['secret']); + $container->setParameter('doctrine_encrypt.secret',$config['secret']); } // Load service file @@ -64,20 +64,6 @@ public function load(array $configs, ContainerBuilder $container): void if (Kernel::MAJOR_VERSION < 5 || (Kernel::MAJOR_VERSION === 5 && Kernel::MINOR_VERSION < 4)) { throw new \RuntimeException('doctrineencryptbundle/doctrine-encrypt-bundle expects symfony-version >= 5.4!'); } - - // Symfony 5-6 - if (Kernel::MAJOR_VERSION < 7) { - // PHP 7.x (no attributes) - if (PHP_VERSION_ID < 80000) { - $loader->load('services_subscriber_with_annotations.yml'); - // PHP 8.x (annotations and attributes) - } else { - $loader->load('services_subscriber_with_annotations_and_attributes.yml'); - } - // Symfony 7 (only attributes) - } else { - $loader->load('service_listeners_with_attributes.yml'); - } } /** @@ -87,6 +73,6 @@ public function load(array $configs, ContainerBuilder $container): void */ public function getAlias(): string { - return 'ambta_doctrine_encrypt'; + return 'doctrine_encrypt'; } } diff --git a/src/DoctrineEncryptBundle.php b/src/DoctrineEncryptBundle.php new file mode 100644 index 00000000..0086efab --- /dev/null +++ b/src/DoctrineEncryptBundle.php @@ -0,0 +1,53 @@ + Encrypted::class, + 'encrypted_datetime' => EncryptedDateTime::class, + 'encrypted_json' => EncryptedJSON::class, + 'encrypted_array' => EncryptedArray::class + ]; + + public function boot(): void + { + $connections = $this->container->get('doctrine')->getConnections(); + $service = $this->container->get('doctrine_encrypt.encrypt_service'); + foreach (self::ENCRYPT_TYPES as $encyptName => $encryptClass) + { + if (! Type::hasType($encyptName)) + { + Type::addType($encyptName, $encryptClass); + /** @var \DoctrineEncryptBundle\Traits\DoctrineEncrypt $addedType */ + $addedType = Type::getType($encyptName); + $addedType->setService($service); + } + + foreach ($connections as $connectionName => $connection) + { + $databasePlatform = $connection->getDatabasePlatform(); + if (! $databasePlatform->hasDoctrineTypeMappingFor($encyptName)) + { + $databasePlatform->registerDoctrineTypeMapping($encyptName, $encyptName); + } + } + } + } + + public function getContainerExtension(): ?ExtensionInterface + { + return new DoctrineEncryptExtension(); + } +} diff --git a/src/Encryptors/DefuseEncryptor.php b/src/Encryptors/DefuseEncryptor.php index e60f48de..d619e045 100644 --- a/src/Encryptors/DefuseEncryptor.php +++ b/src/Encryptors/DefuseEncryptor.php @@ -1,13 +1,9 @@ */ class DefuseEncryptor implements EncryptorInterface diff --git a/src/Encryptors/EncryptorInterface.php b/src/Encryptors/EncryptorInterface.php index fdb1847c..a1428e5d 100644 --- a/src/Encryptors/EncryptorInterface.php +++ b/src/Encryptors/EncryptorInterface.php @@ -1,11 +1,9 @@ */ interface EncryptorInterface { diff --git a/src/Encryptors/HaliteEncryptor.php b/src/Encryptors/HaliteEncryptor.php index 345ff1d4..029ba824 100644 --- a/src/Encryptors/HaliteEncryptor.php +++ b/src/Encryptors/HaliteEncryptor.php @@ -1,6 +1,6 @@ */ class HaliteEncryptor implements EncryptorInterface { - /** @var EncryptionKey|null */ + /** + * @var EncryptionKey|null + */ private $encryptionKey = null; - /** @var string */ + + /** + * @var string + */ private $secret; /** diff --git a/src/Factories/SecretFactory.php b/src/Factories/SecretFactory.php index 9a89caa9..4b5bd335 100644 --- a/src/Factories/SecretFactory.php +++ b/src/Factories/SecretFactory.php @@ -1,10 +1,10 @@ - * - * @internal - */ -final class AttributeAnnotationReader implements Reader -{ - /** - * @var Reader - */ - private $annotationReader; - - /** - * @var AttributeReader - */ - private $attributeReader; - - public function __construct(AttributeReader $attributeReader, Reader $annotationReader, string $cacheDir) - { - $this->attributeReader = $attributeReader; - $annotationsCache = new FilesystemAdapter('', 0, $cacheDir.'/doctrine_encrypt'); - $this->annotationReader = new PsrCachedReader ($annotationReader, $annotationsCache); - } - - /** - * @return Annotation[] - */ - public function getClassAnnotations(ReflectionClass $class): array - { - $annotations = $this->attributeReader->getClassAnnotations($class); - - if ([] !== $annotations) { - return $annotations; - } - - return $this->annotationReader->getClassAnnotations($class); - } - - /** - * @param class-string $annotationName the name of the annotation - * - * @return T|null the Annotation or NULL, if the requested annotation does not exist - * - * @template T - */ - public function getClassAnnotation(ReflectionClass $class, $annotationName) - { - $annotation = $this->attributeReader->getClassAnnotation($class, $annotationName); - - if (null !== $annotation) { - return $annotation; - } - - return $this->annotationReader->getClassAnnotation($class, $annotationName); - } - - /** - * @return Annotation[] - */ - public function getPropertyAnnotations(\ReflectionProperty $property): array - { - $propertyAnnotations = $this->attributeReader->getPropertyAnnotations($property); - - if ([] !== $propertyAnnotations) { - return $propertyAnnotations; - } - - return $this->annotationReader->getPropertyAnnotations($property); - } - - /** - * @param class-string $annotationName the name of the annotation - * - * @return T|null the Annotation or NULL, if the requested annotation does not exist - * - * @template T - */ - public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName) - { - $annotation = $this->attributeReader->getPropertyAnnotation($property, $annotationName); - - if (null !== $annotation) { - return $annotation; - } - - return $this->annotationReader->getPropertyAnnotation($property, $annotationName); - } - - public function getMethodAnnotations(ReflectionMethod $method): array - { - throw new \BadMethodCallException('Not implemented'); - } - - /** - * @param ReflectionMethod $method - * @param $annotationName - * @return mixed - */ - public function getMethodAnnotation(ReflectionMethod $method, $annotationName): mixed - { - throw new \BadMethodCallException('Not implemented'); - } -} diff --git a/src/Mapping/AttributeReader.php b/src/Mapping/AttributeReader.php deleted file mode 100644 index bd82a031..00000000 --- a/src/Mapping/AttributeReader.php +++ /dev/null @@ -1,102 +0,0 @@ - - * - * @internal - */ -final class AttributeReader -{ - /** @var array */ - private $isRepeatableAttribute = []; - - /** - * @param ReflectionClass $class - * @return Annotation[] - */ - public function getClassAnnotations(ReflectionClass $class): array - { - return (method_exists($class, 'getAttributes')) ? $this->convertToAttributeInstances($class->getAttributes()) : []; - } - - /** - * @phpstan-param class-string $annotationName - * - * @return Annotation|Annotation[]|null - */ - public function getClassAnnotation(ReflectionClass $class, string $annotationName) - { - return $this->getClassAnnotations($class)[$annotationName] ?? null; - } - - /** - * @param \ReflectionProperty $property - * @return Annotation[] - */ - public function getPropertyAnnotations(\ReflectionProperty $property): array - { - return (method_exists($property, 'getAttributes')) ? $this->convertToAttributeInstances($property->getAttributes()) : []; - } - - /** - * @phpstan-param class-string $annotationName - * - * @return Annotation|Annotation[]|null - */ - public function getPropertyAnnotation(\ReflectionProperty $property, string $annotationName) - { - return $this->getPropertyAnnotations($property)[$annotationName] ?? null; - } - - /** - * @param array<\ReflectionAttribute> $attributes - * - * @return array - */ - private function convertToAttributeInstances(array $attributes): array - { - $instances = []; - - foreach ($attributes as $attribute) { - $attributeName = $attribute->getName(); - assert(is_string($attributeName)); - // Make sure we only get Gedmo Annotations - if (!is_subclass_of($attributeName, Annotation::class)) { - continue; - } - - $instance = $attribute->newInstance(); - assert($instance instanceof Annotation); - - if ($this->isRepeatable($attributeName)) { - if (!isset($instances[$attributeName])) { - $instances[$attributeName] = []; - } - - $instances[$attributeName][] = $instance; - } else { - $instances[$attributeName] = $instance; - } - } - - return $instances; - } - - private function isRepeatable(string $attributeClassName): bool - { - if (isset($this->isRepeatableAttribute[$attributeClassName])) { - return $this->isRepeatableAttribute[$attributeClassName]; - } - - $reflectionClass = new ReflectionClass($attributeClassName); - $attribute = $reflectionClass->getAttributes()[0]->newInstance(); - - return $this->isRepeatableAttribute[$attributeClassName] = ($attribute->flags & Attribute::IS_REPEATABLE) > 0; - } -} diff --git a/src/Resources/config/service_listeners_with_attributes.yml b/src/Resources/config/service_listeners_with_attributes.yml deleted file mode 100644 index 2e0c497e..00000000 --- a/src/Resources/config/service_listeners_with_attributes.yml +++ /dev/null @@ -1,15 +0,0 @@ -services: - ambta_doctrine_attribute_reader: - class: Ambta\DoctrineEncryptBundle\Mapping\AttributeReader - - ambta_doctrine_annotation_reader: '@ambta_doctrine_attribute_reader' - - ambta_doctrine_encrypt.orm_subscriber: - class: Ambta\DoctrineEncryptBundle\Subscribers\DoctrineEncryptSubscriber - arguments: [ '@ambta_doctrine_attribute_reader', '@ambta_doctrine_encrypt.encryptor' ] - tags: - - { name: 'doctrine.event_listener', event: 'postLoad' } - - { name: 'doctrine.event_listener', event: 'onFlush' } - - { name: 'doctrine.event_listener', event: 'preFlush' } - - { name: 'doctrine.event_listener', event: 'postFlush' } - - { name: 'doctrine.event_listener', event: 'onClear' } \ No newline at end of file diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 067864e9..29e4287b 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -1,27 +1,26 @@ services: - ambta_doctrine_encrypt.subscriber: - alias: ambta_doctrine_encrypt.orm_subscriber + doctrine_encrypt.encrypt_service: + class: DoctrineEncryptBundle\Service\Encrypt + arguments: ["@doctrine_encrypt.encryptor"] + public: true - ambta_doctrine_encrypt.command.decrypt.database: - class: Ambta\DoctrineEncryptBundle\Command\DoctrineDecryptDatabaseCommand + doctrine_encrypt.command.decrypt.database: + class: DoctrineEncryptBundle\Command\DoctrineDecryptDatabaseCommand tags: ['console.command'] arguments: - "@doctrine.orm.entity_manager" - - "@ambta_doctrine_annotation_reader" - - "@ambta_doctrine_encrypt.subscriber" + - "@doctrine_encrypt.encrypt_service" - ambta_doctrine_encrypt.command.encrypt.database: - class: Ambta\DoctrineEncryptBundle\Command\DoctrineEncryptDatabaseCommand + doctrine_encrypt.command.encrypt.database: + class: DoctrineEncryptBundle\Command\DoctrineEncryptDatabaseCommand tags: ['console.command'] arguments: - "@doctrine.orm.entity_manager" - - "@ambta_doctrine_annotation_reader" - - "@ambta_doctrine_encrypt.subscriber" + - "@doctrine_encrypt.encrypt_service" - ambta_doctrine_encrypt.command.encrypt.status: - class: Ambta\DoctrineEncryptBundle\Command\DoctrineEncryptStatusCommand + doctrine_encrypt.command.encrypt.status: + class: DoctrineEncryptBundle\Command\DoctrineEncryptStatusCommand tags: ['console.command'] arguments: - "@doctrine.orm.entity_manager" - - "@ambta_doctrine_annotation_reader" - - "@ambta_doctrine_encrypt.subscriber" + - "@doctrine_encrypt.encrypt_service" diff --git a/src/Resources/config/services_subscriber_with_annotations.yml b/src/Resources/config/services_subscriber_with_annotations.yml deleted file mode 100644 index 6ef90ad3..00000000 --- a/src/Resources/config/services_subscriber_with_annotations.yml +++ /dev/null @@ -1,9 +0,0 @@ -services: - # Use the regular annotation reader - ambta_doctrine_annotation_reader: "@annotations.reader" - - ambta_doctrine_encrypt.orm_subscriber: - class: Ambta\DoctrineEncryptBundle\Subscribers\DoctrineEncryptSubscriber - arguments: ["@ambta_doctrine_annotation_reader", "@ambta_doctrine_encrypt.encryptor"] - tags: - - { name: doctrine.event_subscriber } \ No newline at end of file diff --git a/src/Resources/config/services_subscriber_with_annotations_and_attributes.yml b/src/Resources/config/services_subscriber_with_annotations_and_attributes.yml deleted file mode 100644 index f2f88b9b..00000000 --- a/src/Resources/config/services_subscriber_with_annotations_and_attributes.yml +++ /dev/null @@ -1,13 +0,0 @@ -services: - ambta_doctrine_attribute_reader: - class: Ambta\DoctrineEncryptBundle\Mapping\AttributeReader - - ambta_doctrine_annotation_reader: - class: Ambta\DoctrineEncryptBundle\Mapping\AttributeAnnotationReader - arguments: ["@ambta_doctrine_attribute_reader", "@annotations.reader", "%kernel.cache_dir%"] - - ambta_doctrine_encrypt.orm_subscriber: - class: Ambta\DoctrineEncryptBundle\Subscribers\DoctrineEncryptSubscriber - arguments: ["@ambta_doctrine_annotation_reader", "@ambta_doctrine_encrypt.encryptor"] - tags: - - { name: doctrine.event_subscriber } \ No newline at end of file diff --git a/src/Resources/config/services_with_secret.yml b/src/Resources/config/services_with_secret.yml index a98d13b5..303766e1 100644 --- a/src/Resources/config/services_with_secret.yml +++ b/src/Resources/config/services_with_secret.yml @@ -1,5 +1,5 @@ services: - ambta_doctrine_encrypt.encryptor: - class: "%ambta_doctrine_encrypt.encryptor_class_name%" + doctrine_encrypt.encryptor: + class: "%doctrine_encrypt.encryptor_class_name%" arguments: - $secret: "%ambta_doctrine_encrypt.secret%" \ No newline at end of file + $secret: "%doctrine_encrypt.secret%" \ No newline at end of file diff --git a/src/Resources/config/services_with_secretfactory.yml b/src/Resources/config/services_with_secretfactory.yml index 55fa670b..40bbdc73 100644 --- a/src/Resources/config/services_with_secretfactory.yml +++ b/src/Resources/config/services_with_secretfactory.yml @@ -1,11 +1,11 @@ services: - ambta_doctrine_encrypt.encryptor: - class: "%ambta_doctrine_encrypt.encryptor_class_name%" + doctrine_encrypt.encryptor: + class: "%doctrine_encrypt.encryptor_class_name%" arguments: - - '@=service("ambta_doctrine_encrypt.secret_factory").getSecret(parameter("ambta_doctrine_encrypt.encryptor_class_name"))' + - '@=service("doctrine_encrypt.secret_factory").getSecret(parameter("doctrine_encrypt.encryptor_class_name"))' - ambta_doctrine_encrypt.secret_factory: - class: Ambta\DoctrineEncryptBundle\Factories\SecretFactory + doctrine_encrypt.secret_factory: + class: DoctrineEncryptBundle\Factories\SecretFactory arguments: - $secretDirectory: '%ambta_doctrine_encrypt.secret_directory_path%' - $enableSecretCreation: '%ambta_doctrine_encrypt.enable_secret_generation%' \ No newline at end of file + $secretDirectory: '%doctrine_encrypt.secret_directory_path%' + $enableSecretCreation: '%doctrine_encrypt.enable_secret_generation%' \ No newline at end of file diff --git a/src/Resources/doc/usage.md b/src/Resources/doc/usage.md index 67ff2f5a..f749157a 100644 --- a/src/Resources/doc/usage.md +++ b/src/Resources/doc/usage.md @@ -1,11 +1,5 @@ # Usage -### Column Type - -Ensure that the column type for the property is always set to string or text. - -The column type should always relate back to one of the database supported string types as the encrypted data saved to the database will always be a string. - ### Entity ``` php @@ -39,75 +33,6 @@ class User { It is as simple as that, the field will now be encrypted the first time the users entity gets edited. We keep an prefix to check if data is encrypted or not so, unencrypted data will still work even if the field is encrypted. -#### Supported Data Types - -The supported data types to be encrypted and decrypted are: -* string (The Default) -* datetime -* json -* array - -Example usage in the Entity: - -```php -namespace Acme\DemoBundle\Entity; - -use Doctrine\ORM\Mapping as ORM; - -// importing @Encrypted annotation -use Ambta\DoctrineEncryptBundle\Configuration\Encrypted; - -/** - * @ORM\Entity - * @ORM\Table(name="test") - */ -class Test { - - .. - - /** - * @ORM\Column(type="string", name="string") - * @Encrypted - * @var string - */ - private $string; - - /** - * @ORM\Column(type="string", name="another_string") - * @Encrypted(type="string") - * @var string - */ - private $anotherString; - - /** - * @ORM\Column(type="text", name="datetime") - * @Encrypted(type="datetime") - * @var DateTime - */ - private $datetime; - - /** - * @ORM\Column(type="text", name="json") - * @Encrypted(type="json") - * @var array - */ - private $json; - - /** - * @ORM\Column(type="text", name="array") - * @Encrypted(type="array") - * @var array - */ - private $array; - - .. - -} -``` - -Please note again that the ORM Column types relate to string values as the saved data whould be the encrypted string. -If using MySql for example you will not be able to use the JSON functions directly in the database when the json data is encrypted. - ### Entity Method Behaviour The bundle will not know what the entity methods do in the getter and setter so if there are any additional behaviour. diff --git a/src/Service/Encrypt.php b/src/Service/Encrypt.php new file mode 100755 index 00000000..f714ba84 --- /dev/null +++ b/src/Service/Encrypt.php @@ -0,0 +1,123 @@ +'; + + /** + * Encryptor + * + * @var EncryptorInterface|null + */ + private $encryptor; + + /** + * Doctrine AbstractPlatform. Always uses MySQL80Platform to ensure that the values are encrypted and decrypted the same even if moved between databases + * + * @var AbstractPlatform + */ + private $platform; + + /** + * Initialization of service + * + * @param EncryptorInterface $encryptor (Optional) An EncryptorInterface. + */ + public function __construct(EncryptorInterface $encryptor) + { + $this->encryptor = $encryptor; + $this->platform = new MySQL80Platform(); + } + + /** + * Change the encryptor + * + * @param EncryptorInterface|null $encryptor + */ + public function setEncryptor(?EncryptorInterface $encryptor = null) + { + $this->encryptor = $encryptor; + } + + /** + * Get the current encryptor + * + * @return EncryptorInterface|null returns the encryptor class or null + */ + public function getEncryptor(): ?EncryptorInterface + { + return $this->encryptor; + } + + /** + * Process encrypt + * + * @param 'string'|'datetime'|'json'|'array' $type + * @param mixed $value + * + * @return mixed + * + */ + public function encrypt(string $type, $value) + { + // TODO : probably cannot return null when $this->encryptor is not set + if (is_null ($value) || is_null ($this->encryptor)) + { + return null; + } + + $encryptDbalType = Type::getType($type); + $usedValue = $encryptDbalType->convertToDatabaseValue($value, $this->platform); + if (array_key_exists('DOCTRINE_SKIP_ENCRYPT', $_ENV) && $_ENV['DOCTRINE_SKIP_ENCRYPT'] === true) + { + return $usedValue; + } + + if (substr($usedValue, -strlen(self::ENCRYPTION_MARKER)) != self::ENCRYPTION_MARKER) + { + return $this->getEncryptor()->encrypt($usedValue).self::ENCRYPTION_MARKER; + } + + return $usedValue; + } + + /** + * Process decrypt + * + * @param 'string'|'datetime'|'json'|'array' $type + * @param mixed $value + * + * @return mixed + * + */ + public function decrypt(string $type, $value) + { + // TODO : probably cannot return null when $this->encryptor is not set + if (is_null ($value) || is_null ($this->encryptor)) + { + return null; + } + + if (substr($value, -strlen(self::ENCRYPTION_MARKER)) == self::ENCRYPTION_MARKER) + { + $encryptDbalType = Type::getType($type); + $currentValue = $this->getEncryptor()->decrypt(substr($value, 0, -strlen(self::ENCRYPTION_MARKER))); + return $encryptDbalType->convertToPHPValue($currentValue, $this->platform); + } + + return $value; + } +} diff --git a/src/Subscribers/DoctrineEncryptSubscriber.php b/src/Subscribers/DoctrineEncryptSubscriber.php deleted file mode 100644 index 1b7d4c6e..00000000 --- a/src/Subscribers/DoctrineEncryptSubscriber.php +++ /dev/null @@ -1,401 +0,0 @@ -'; - - /** - * Encryptor interface namespace - */ - const ENCRYPTOR_INTERFACE_NS = 'Ambta\DoctrineEncryptBundle\Encryptors\EncryptorInterface'; - - /** - * Encrypted annotation full name - */ - const ENCRYPTED_ANN_NAME = 'Ambta\DoctrineEncryptBundle\Configuration\Encrypted'; - - /** - * Encryptor - * @var EncryptorInterface|null - */ - private $encryptor; - - /** - * Annotation reader - * @var Reader|AttributeReader - */ - private $annReader; - - /** - * Used for restoring the encryptor after changing it - * @var EncryptorInterface|string - */ - private $restoreEncryptor; - - /** - * Used for restoring the encryptor after changing it - * @var PropertyAccessorInterface|string - */ - private $pac; - - /** - * Count amount of decrypted values in this service - * @var integer - */ - public $decryptCounter = 0; - - /** - * Count amount of encrypted values in this service - * @var integer - */ - public $encryptCounter = 0; - - /** @var array */ - private $cachedDecryptions = []; - - /** @var array */ - private $cachedClassProperties = []; - - /** @var array */ - private $cachedClassPropertiesAreEmbedded = []; - - /** @var array */ - private $cachedClassPropertiesAreEncrypted = []; - - /** @var array */ - private $cachedClassesContainAnEncryptProperty = []; - - /** - * Initialization of subscriber - * - * @param Reader|AttributeReader $annReader - * @param EncryptorInterface $encryptor (Optional) An EncryptorInterface. - */ - public function __construct($annReader, EncryptorInterface $encryptor) - { - $this->annReader = $annReader; - $this->encryptor = $encryptor; - $this->restoreEncryptor = $this->encryptor; - $this->pac = PropertyAccess::createPropertyAccessor(); - } - - /** - * Change the encryptor - * - * @param EncryptorInterface|null $encryptor - */ - public function setEncryptor(?EncryptorInterface $encryptor = null) - { - $this->encryptor = $encryptor; - } - - /** - * Get the current encryptor - * - * @return EncryptorInterface|null returns the encryptor class or null - */ - public function getEncryptor(): ?EncryptorInterface - { - return $this->encryptor; - } - - /** - * Restore encryptor to the one set in the constructor. - */ - public function restoreEncryptor() - { - $this->encryptor = $this->restoreEncryptor; - } - - /** - * Listen a postLoad lifecycle event. - * Decrypt entities property's values when loaded into the entity manger - * - * @param LifecycleEventArgs $args - */ - public function postLoad($args) - { - $entity = $args->getObject(); - $this->processFields($entity, $args->getObjectManager(), false); - } - - /** - * Listen to onflush event - * Encrypt entities that are inserted into the database - * - * @param PreFlushEventArgs $preFlushEventArgs - */ - public function preFlush(PreFlushEventArgs $preFlushEventArgs) - { - $objectManager = method_exists($preFlushEventArgs, 'getObjectManager') ? $preFlushEventArgs->getObjectManager() : $preFlushEventArgs->getEntityManager(); - $unitOfWork = $objectManager->getUnitOfWork(); - foreach ($unitOfWork->getIdentityMap() as $entityName => $entityArray) { - if (isset($this->cachedDecryptions[$entityName])) { - foreach ($entityArray as $entityId => $instance) { - $this->processFields($instance, $objectManager, true); - } - } - } - $this->cachedDecryptions = []; - } - - /** - * Listen to onflush event - * Encrypt entities that are inserted into the database - * - * @param OnFlushEventArgs $onFlushEventArgs - */ - public function onFlush(OnFlushEventArgs $onFlushEventArgs) - { - $objectManager = method_exists($onFlushEventArgs, 'getObjectManager') ? $onFlushEventArgs->getObjectManager() : $onFlushEventArgs->getEntityManager(); - $unitOfWork = $objectManager->getUnitOfWork(); - foreach ($unitOfWork->getScheduledEntityInsertions() as $entity) { - $encryptCounterBefore = $this->encryptCounter; - $this->processFields($entity,$objectManager,true); - if ($this->encryptCounter > $encryptCounterBefore) { - $classMetadata = $objectManager->getClassMetadata(get_class($entity)); - $unitOfWork->recomputeSingleEntityChangeSet($classMetadata, $entity); - } - } - } - - /** - * Listen to postFlush event - * Decrypt entities after having been inserted into the database - * - * @param PostFlushEventArgs $postFlushEventArgs - */ - public function postFlush(PostFlushEventArgs $postFlushEventArgs) - { - $objectManager = method_exists($postFlushEventArgs, 'getObjectManager') ? $postFlushEventArgs->getObjectManager() : $postFlushEventArgs->getEntityManager(); - $unitOfWork = $objectManager->getUnitOfWork(); - foreach ($unitOfWork->getIdentityMap() as $entityMap) { - foreach ($entityMap as $entity) { - $this->processFields($entity,$objectManager, false); - } - } - } - - public function onClear(OnClearEventArgs $onClearEventArgs) - { - $this->cachedDecryptions = []; - $this->decryptCounter = 0; - $this->encryptCounter = 0; - } - - /** - * Realization of EventSubscriber interface method. - * - * @return array Return all events which this subscriber is listening - */ - public function getSubscribedEvents(): array - { - return array( - Events::postLoad, - Events::onFlush, - Events::preFlush, - Events::postFlush, - Events::onClear, - ); - } - - /** - * 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, EntityManagerInterface $entityManager, bool $isEncryptOperation = true): ?object - { - if (!empty($this->encryptor) && $this->containsEncryptProperties($entity)) { - $realClass = ClassUtils::getClass($entity); - - // Get ReflectionClass of our entity - $properties = $this->getClassProperties($realClass); - - // Foreach property in the reflection class - foreach ($properties as $refProperty) { - if ($this->isPropertyAnEmbeddedMapping($refProperty)) { - $this->handleEmbeddedAnnotation($entity, $entityManager, $refProperty, $isEncryptOperation); - continue; - } - - /** - * If property is an normal value and contains the Encrypt tag, lets encrypt/decrypt that property - */ - $encryptType = $this->getEncryptedPropertyType($refProperty); - if ($encryptType) { - $encryptDbalType = \Doctrine\DBAL\Types\Type::getType($encryptType); - $platform = new MySQL80Platform(); - $rootEntityName = $entityManager->getClassMetadata(get_class($entity))->rootEntityName; - - $value = $this->pac->getValue($entity, $refProperty->getName()); - if (!is_null($value) and !empty($value)) { - if ($isEncryptOperation) { - // Convert to a string using doctrine-type - $usedValue = $encryptDbalType->convertToDatabaseValue($value, $platform); - - if (isset($this->cachedDecryptions[$rootEntityName][spl_object_id($entity)][$refProperty->getName()][$usedValue])) { - $this->pac->setValue($entity, $refProperty->getName(), $this->cachedDecryptions[$rootEntityName][spl_object_id($entity)][$refProperty->getName()][$usedValue]); - } elseif (substr($usedValue, -strlen(self::ENCRYPTION_MARKER)) != self::ENCRYPTION_MARKER) { - $this->encryptCounter++; - $currentPropValue = $this->encryptor->encrypt($usedValue).self::ENCRYPTION_MARKER; - $this->pac->setValue($entity, $refProperty->getName(), $currentPropValue); - } - } else { - if (substr($value, -strlen(self::ENCRYPTION_MARKER)) == self::ENCRYPTION_MARKER) { - $this->decryptCounter++; - $currentPropValue = $this->encryptor->decrypt(substr($value, 0, -5)); - $this->cachedDecryptions[$rootEntityName][spl_object_id($entity)][$refProperty->getName()][$currentPropValue] = $value; - - // Convert from a string to the PHP-type again using dbal-type - $actualValue = $encryptDbalType->convertToPHPValue($currentPropValue, $platform); - $this->pac->setValue($entity, $refProperty->getName(),$actualValue); - } - } - } - } - } - - return $entity; - } - - return $entity; - } - - private function handleEmbeddedAnnotation($entity, EntityManagerInterface $entityManager, ReflectionProperty $embeddedProperty, bool $isEncryptOperation = true) - { - $propName = $embeddedProperty->getName(); - - $embeddedEntity = $this->pac->getValue($entity, $propName); - - if ($embeddedEntity) { - $this->processFields($embeddedEntity, $entityManager, $isEncryptOperation); - } - } - - /** - * Recursive function to get an associative array of class properties - * including inherited ones from extended classes - * - * @param string $className Class name - * - * @return array|ReflectionProperty[] - */ - private function getClassProperties(string $className): array - { - if (!array_key_exists($className,$this->cachedClassProperties)) { - $reflectionClass = new ReflectionClass($className); - $properties = $reflectionClass->getProperties(); - $propertiesArray = array(); - - foreach ($properties as $property) { - $propertyName = $property->getName(); - $propertiesArray[$propertyName] = $property; - } - - if ($parentClass = $reflectionClass->getParentClass()) { - $parentPropertiesArray = $this->getClassProperties($parentClass->getName()); - if (count($parentPropertiesArray) > 0) { - $propertiesArray = array_merge($parentPropertiesArray, $propertiesArray); - } - } - - $this->cachedClassProperties[$className] = $propertiesArray; - } - - return $this->cachedClassProperties[$className]; - } - - /** - * @return bool - */ - private function isPropertyAnEmbeddedMapping(ReflectionProperty $refProperty) - { - $key = $refProperty->getDeclaringClass()->getName().$refProperty->getName(); - if (!array_key_exists($key,$this->cachedClassPropertiesAreEmbedded)) { - $this->cachedClassPropertiesAreEmbedded[$key] = (bool) $this->annReader->getPropertyAnnotation($refProperty, 'Doctrine\ORM\Mapping\Embedded'); - } - - return $this->cachedClassPropertiesAreEmbedded[$key]; - } - - /** - * @return string|null - */ - private function getEncryptedPropertyType(ReflectionProperty $refProperty) - { - $key = $refProperty->getDeclaringClass()->getName().$refProperty->getName(); - if (!array_key_exists($key,$this->cachedClassPropertiesAreEncrypted)) { - $type = null; - $propertyAnnotation = $this->annReader->getPropertyAnnotation($refProperty, self::ENCRYPTED_ANN_NAME); - if ($propertyAnnotation) - { - $type = $propertyAnnotation->type; - } - $this->cachedClassPropertiesAreEncrypted[$key] = $type; - } - - return $this->cachedClassPropertiesAreEncrypted[$key]; - } - - private function containsEncryptProperties($entity) - { - $realClass = ClassUtils::getClass($entity); - - if (!array_key_exists($realClass,$this->cachedClassesContainAnEncryptProperty)) { - $this->cachedClassesContainAnEncryptProperty[$realClass] = false; - - // Get ReflectionClass of our entity - $properties = $this->getClassProperties($realClass); - - // Foreach property in the reflection class - foreach ($properties as $refProperty) { - if ($this->isPropertyAnEmbeddedMapping($refProperty)) { - $embeddedEntity = $this->pac->getValue($entity, $refProperty->getName()); - - if ($this->containsEncryptProperties($embeddedEntity)) { - $this->cachedClassesContainAnEncryptProperty[$realClass] = true; - } - } else { - if ($this->getEncryptedPropertyType($refProperty)) { - $this->cachedClassesContainAnEncryptProperty[$realClass] = true; - } - } - } - } - - return $this->cachedClassesContainAnEncryptProperty[$realClass]; - } -} diff --git a/src/Traits/DoctrineEncrypt.php b/src/Traits/DoctrineEncrypt.php new file mode 100755 index 00000000..a9b7ca4e --- /dev/null +++ b/src/Traits/DoctrineEncrypt.php @@ -0,0 +1,23 @@ +service = $service; + } +} diff --git a/src/Types/Encrypted.php b/src/Types/Encrypted.php new file mode 100755 index 00000000..56abc2c8 --- /dev/null +++ b/src/Types/Encrypted.php @@ -0,0 +1,29 @@ +service->decrypt ('string', $value); + } + + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): mixed + { + return $this->service->encrypt ('string', $value); + } + + public function getName(): string + { + return self::TYPE; + } +} diff --git a/src/Types/EncryptedArray.php b/src/Types/EncryptedArray.php new file mode 100755 index 00000000..5695bded --- /dev/null +++ b/src/Types/EncryptedArray.php @@ -0,0 +1,29 @@ +service->decrypt ('simple_array', $value); + } + + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): mixed + { + return $this->service->encrypt ('simple_array', $value); + } + + public function getName(): string + { + return self::TYPE; + } +} diff --git a/src/Types/EncryptedDateTime.php b/src/Types/EncryptedDateTime.php new file mode 100755 index 00000000..f6117941 --- /dev/null +++ b/src/Types/EncryptedDateTime.php @@ -0,0 +1,29 @@ +service->decrypt ('datetime', $value); + } + + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): mixed + { + return $this->service->encrypt ('datetime', $value); + } + + public function getName(): string + { + return self::TYPE; + } +} diff --git a/src/Types/EncryptedJSON.php b/src/Types/EncryptedJSON.php new file mode 100755 index 00000000..7f7a1aa7 --- /dev/null +++ b/src/Types/EncryptedJSON.php @@ -0,0 +1,29 @@ +service->decrypt ('json', $value); + } + + public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): mixed + { + return $this->service->encrypt ('json', $value); + } + + public function getName(): string + { + return self::TYPE; + } +} diff --git a/tests/DoctrineCompatibilityTrait.php b/tests/DoctrineCompatibilityTrait.php index 071cce40..b12844e1 100644 --- a/tests/DoctrineCompatibilityTrait.php +++ b/tests/DoctrineCompatibilityTrait.php @@ -1,6 +1,6 @@ executeQuery()->fetchAllAssociative(); - } else { - $statement->execute(); - return $statement->fetchAll(); - } + return $statement->executeQuery()->fetchAllAssociative(); } /** @@ -24,13 +19,8 @@ private function executeStatementFetchAll(\Doctrine\DBAL\Statement $statement) * * Helper-method since methods changed in different supported versions of Doctrine */ - private function executeStatementFetch(\Doctrine\DBAL\Statement $statement) + public function executeStatementFetch(\Doctrine\DBAL\Statement $statement) { - if (method_exists($statement,'executeQuery')) { - return $statement->executeQuery()->fetchAssociative(); - } else { - $statement->execute(); - return $statement->fetch(); - } + return $statement->executeQuery()->fetchAssociative(); } } \ No newline at end of file diff --git a/tests/Functional/AbstractFunctionalTestCase.php b/tests/Functional/AbstractFunctionalTestCase.php index 6b976375..727174a4 100644 --- a/tests/Functional/AbstractFunctionalTestCase.php +++ b/tests/Functional/AbstractFunctionalTestCase.php @@ -1,80 +1,93 @@ encryptor = $this->getEncryptor(); + $this->service = new Encrypt($this->encryptor); + + foreach (DoctrineEncryptBundle::ENCRYPT_TYPES as $encyptName => $encryptClass) + { + if (! Type::hasType($encyptName)) + { + Type::addType($encyptName, $encryptClass); + } + /** @var \DoctrineEncryptBundle\Traits\DoctrineEncrypt $addedType */ + $addedType = Type::getType($encyptName); + $addedType->setService($this->service); + } // database configuration parameters $this->dbFile = tempnam(sys_get_temp_dir(), 'amb_db'); - $conn = array( + $connOptions = array( 'driver' => 'pdo_sqlite', 'path' => $this->dbFile, ); + // Create a simple "default" Doctrine ORM configuration + $isDevMode = true; + $proxyDir = null; + $cache = null; + $reportFieldsWhereDeclared = true; + $config = ORMSetup::createAttributeMetadataConfiguration( + array(__DIR__.'/fixtures/Entity'), + $isDevMode, + $proxyDir, + $cache, + $reportFieldsWhereDeclared + ); // obtaining the entity manager - $this->entityManager = EntityManager::create($conn, $config); + $conn = DriverManager::getConnection($connOptions, $config); + $dbalConf = $conn->getConfiguration(); + $this->entityManager = new EntityManager($conn, $dbalConf); $schemaTool = new SchemaTool($this->entityManager); $classes = $this->entityManager->getMetadataFactory()->getAllMetadata(); $schemaTool->dropSchema($classes); $schemaTool->createSchema($classes); - $this->sqlLoggerStack = new DebugStack(); - $this->entityManager->getConnection()->getConfiguration()->setSQLLogger($this->sqlLoggerStack); - - $this->encryptor = $this->getEncryptor(); - $annotationCacheDirectory = __DIR__.'/cache'; - $this->createNewCacheDirectory ($annotationCacheDirectory); - $annotationReader = new AttributeAnnotationReader (new AttributeReader(), new AnnotationReader(), $annotationCacheDirectory); - $this->subscriber = new DoctrineEncryptSubscriber($annotationReader, $this->encryptor); - $this->entityManager->getEventManager()->addEventSubscriber($this->subscriber); - error_reporting(E_ALL); } @@ -84,59 +97,6 @@ public function tearDown(): void unlink($this->dbFile); } - protected function createNewCacheDirectory (string $annotationCacheDirectory): void - { - $this->recurseRmdir ($annotationCacheDirectory); - mkdir ($annotationCacheDirectory); - } - - protected function recurseRmdir ($dir): bool - { - $contents = scandir($dir); - if (is_array ($contents)) - { - $files = array_diff ($contents, array('.','..')); - foreach ($files as $file) - { - (is_dir("$dir/$file") && !is_link("$dir/$file")) ? $this->recurseRmdir("$dir/$file") : unlink("$dir/$file"); - } - return rmdir($dir); - } - - return false; - } - - protected function getLatestInsertQuery(): ?array - { - $insertQueries = array_values(array_filter($this->sqlLoggerStack->queries, static function ($queryData) { - return stripos($queryData['sql'], 'INSERT ') === 0; - })); - - return current(array_reverse($insertQueries)) ?: null; - } - - protected function getLatestUpdateQuery(): ?array - { - $updateQueries = array_values(array_filter($this->sqlLoggerStack->queries,static function ($queryData) { - return stripos($queryData['sql'], 'UPDATE ') === 0; - })); - - return current(array_reverse($updateQueries)) ?: null; - } - - /** - * Using the SQL Logger Stack this method retrieves the current query count executed in this test. - */ - 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. * diff --git a/tests/Functional/BasicQueryTest/AbstractBasicQueryTestCase.php b/tests/Functional/BasicQueryTest/AbstractBasicQueryTestCase.php index 316b4979..42139ca4 100644 --- a/tests/Functional/BasicQueryTest/AbstractBasicQueryTestCase.php +++ b/tests/Functional/BasicQueryTest/AbstractBasicQueryTestCase.php @@ -1,11 +1,16 @@ setNotSecret('My public information'); $user->setSecret('top secret information'); $this->entityManager->persist($user); + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(2, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($user))); $this->entityManager->flush(); // Start transaction; insert; commit $this->assertEquals('top secret information',$user->getSecret()); - $this->assertEquals(3,$this->getCurrentQueryCount()); + + $user->setSecret('top secret information'); + $this->entityManager->persist($user); + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(0, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($user))); + + $user->setSecret('top secret info'); + $this->entityManager->persist($user); + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(1, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($user))); + $this->entityManager->flush(); } public function testNoUpdateOnReadEncrypted(): void { $this->entityManager->beginTransaction(); - $this->assertEquals(1,$this->getCurrentQueryCount()); $user = new CascadeTarget(); $user->setNotSecret('My public information'); $user->setSecret('top secret information'); $this->entityManager->persist($user); + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(2, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($user))); $this->entityManager->flush(); - $this->assertEquals(2,$this->getCurrentQueryCount()); // Test if no query is executed when doing nothing $this->entityManager->flush(); - $this->assertEquals(2,$this->getCurrentQueryCount()); // Test if no query is executed when reading unrelated field $user->getNotSecret(); + $this->entityManager->persist($user); + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(0, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($user))); $this->entityManager->flush(); - $this->assertEquals(2,$this->getCurrentQueryCount()); // Test if no query is executed when reading related field and if field is valid $this->assertEquals('top secret information',$user->getSecret()); + $this->entityManager->persist($user); + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(0, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($user))); $this->entityManager->flush(); - $this->assertEquals(2,$this->getCurrentQueryCount()); // Test if 1 query is executed when updating entity $user->setSecret('top secret information change'); + $this->entityManager->persist($user); + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(1, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($user))); $this->entityManager->flush(); - $this->assertEquals(3,$this->getCurrentQueryCount()); $this->assertEquals('top secret information change',$user->getSecret()); - - $this->entityManager->rollback(); - $this->assertEquals(4,$this->getCurrentQueryCount()); } public function testStoredDataIsEncrypted(): void @@ -66,21 +85,28 @@ public function testStoredDataIsEncrypted(): void $this->entityManager->persist($user); $this->entityManager->flush(); - $queryData = $this->getLatestInsertQuery(); - $params = array_values($queryData['params']); - $passwordData = $params[0] === 'My public information' ? $params[1] : $params[0]; + $connection = $this->entityManager->getConnection(); + $stmt = $connection->prepare('SELECT * from CascadeTarget WHERE id = ?'); + $stmt->bindValue(1, $user->getId()); + $results = $this->executeStatementFetchAll($stmt); + $passwordData = $results[0]['secret']; - $this->assertStringEndsWith(DoctrineEncryptSubscriber::ENCRYPTION_MARKER,$passwordData); + $this->assertStringEndsWith(Encrypt::ENCRYPTION_MARKER,$passwordData); $this->assertStringDoesNotContain('my secret',$passwordData); + $this->assertEquals('my secret',$user->getSecret()); $user->setSecret('my secret has changed'); $this->entityManager->flush(); - $queryData = $this->getLatestUpdateQuery(); - $passwordData = array_values($queryData['params'])[0]; + $connection = $this->entityManager->getConnection(); + $stmt = $connection->prepare('SELECT * from CascadeTarget WHERE id = ?'); + $stmt->bindValue(1, $user->getId()); + $results = $this->executeStatementFetchAll($stmt); + $passwordData = $results[0]['secret']; - $this->assertStringEndsWith(DoctrineEncryptSubscriber::ENCRYPTION_MARKER,$passwordData); - $this->assertStringDoesNotContain('my secret',$passwordData); + $this->assertStringEndsWith(Encrypt::ENCRYPTION_MARKER,$passwordData); + $this->assertStringDoesNotContain('my secret has changed',$passwordData); + $this->assertEquals('my secret has changed',$user->getSecret()); } public function testNoUpdateForUnalteredChildrenOfAbstractEntities() @@ -89,20 +115,96 @@ public function testNoUpdateForUnalteredChildrenOfAbstractEntities() $car->setSecret('top secret information'); $car->setNotSecret('123-test'); $this->entityManager->persist($car); + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(3, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($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->persist($car); + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(0, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($car))); + $this->entityManager->flush(); + } + + public function testEntitySetterUseStrtoupper() + { + $user = new CascadeTargetStrtoupper(); + $user->setNotSecret('My public information'); + $user->setSecret('my secret'); + $this->entityManager->persist($user); $this->entityManager->flush(); - // Verify there are no queries executed - $this->assertNull($this->getLatestUpdateQuery()); - $this->assertEquals(0,$this->getCurrentQueryCount()); + $connection = $this->entityManager->getConnection(); + $stmt = $connection->prepare('SELECT * from CascadeTargetStrtoupper WHERE id = ?'); + $stmt->bindValue(1, $user->getId()); + $results = $this->executeStatementFetchAll($stmt); + $passwordData = $results[0]['secret']; + $secret = $user->getSecret(); + + $this->assertStringEndsWith(Encrypt::ENCRYPTION_MARKER,$passwordData); + $this->assertStringDoesNotContain('my secret',$passwordData); + $this->assertStringDoesNotContain('MY SECRET',$passwordData); + $this->assertEquals('MY SECRET', $secret); + } + + public function testEntityDateTime() + { + $datetime = new DateTime(); + $user = new CascadeTargetDateTime(); + $user->setNotSecret('My public information'); + $user->setSecret($datetime); + $this->entityManager->persist($user); + $this->entityManager->flush(); + + $connection = $this->entityManager->getConnection(); + $stmt = $connection->prepare('SELECT * from CascadeTargetDateTime WHERE id = ?'); + $stmt->bindValue(1, $user->getId()); + $results = $this->executeStatementFetchAll($stmt); + $passwordData = $results[0]['secret']; + $secret = $user->getSecret(); + + $this->assertStringEndsWith(Encrypt::ENCRYPTION_MARKER,$passwordData); + $this->assertEquals($datetime->format('Y-m-d H:i:s'), $secret->format('Y-m-d H:i:s')); + } + + public function testEntityJSON() + { + $json = ['test' => 'value']; + $user = new CascadeTargetJSON(); + $user->setNotSecret('My public information'); + $user->setSecret($json); + $this->entityManager->persist($user); + $this->entityManager->flush(); + + $connection = $this->entityManager->getConnection(); + $stmt = $connection->prepare('SELECT * from CascadeTargetJSON WHERE id = ?'); + $stmt->bindValue(1, $user->getId()); + $results = $this->executeStatementFetchAll($stmt); + $passwordData = $results[0]['secret']; + $secret = $user->getSecret(); + + $this->assertStringEndsWith(Encrypt::ENCRYPTION_MARKER,$passwordData); + $this->assertEquals($json, $secret); + } + + public function testEntityArray() + { + $array = ['test', 'value']; + $user = new CascadeTargetArray(); + $user->setNotSecret('My public information'); + $user->setSecret($array); + $this->entityManager->persist($user); + $this->entityManager->flush(); + + $connection = $this->entityManager->getConnection(); + $stmt = $connection->prepare('SELECT * from CascadeTargetArray WHERE id = ?'); + $stmt->bindValue(1, $user->getId()); + $results = $this->executeStatementFetchAll($stmt); + $passwordData = $results[0]['secret']; + $secret = $user->getSecret(); + + $this->assertStringEndsWith(Encrypt::ENCRYPTION_MARKER,$passwordData); + $this->assertEquals($array, $secret); } } diff --git a/tests/Functional/BasicQueryTest/BasicQueryDefuseTest.php b/tests/Functional/BasicQueryTest/BasicQueryDefuseTest.php index 5eec8c08..ed969c93 100644 --- a/tests/Functional/BasicQueryTest/BasicQueryDefuseTest.php +++ b/tests/Functional/BasicQueryTest/BasicQueryDefuseTest.php @@ -1,9 +1,9 @@ assertEquals($secret, $owner->getSecret()); $this->assertEquals($notSecret, $owner->getNotSecret()); + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(0, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($owner))); } - $stack = new DebugStack(); - $connection->getConfiguration()->setSQLLogger($stack); - $this->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. - $this->assertEquals($beforeFlush, $afterFlush); - // No queries happened because we didn't change anything. - $this->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. - $this->assertEquals($beforeFlush, $afterFlush); - // No queries happened because we didn't change anything. - $this->assertCount(0, $stack->queries, "Unexpected queries:\n".var_export($stack->queries, true)); $stmt->bindValue(1, $owner1Id); $results = $this->executeStatementFetchAll($stmt); @@ -249,25 +225,15 @@ public function testEncryptionDoesNotHappenWhenThereIsNoChangeClassInheritance() 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; + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(0, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($child))); $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)); + // No queries happened because we didn't change anything. + $this->entityManager->getUnitOfWork()->computeChangeSets(); + $this->assertEquals(0, count($this->entityManager->getUnitOfWork()->getEntityChangeSet($child))); // 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); @@ -309,11 +275,7 @@ public function testEncryptionDoesHappenWhenASecretIsChanged(): void /** @var Owner $owner */ $owner = $em->getRepository(Owner::class)->find($ownerId); $owner->setSecret('A NEW SECRET!!!'); - $beforeFlush = $this->subscriber->encryptCounter; $em->flush(); - $afterFlush = $this->subscriber->encryptCounter; - // No encryption should have happened because we didn't change anything. - $this->assertGreaterThan($beforeFlush, $afterFlush); $stmt->bindValue(1, $ownerId); $results = $this->executeStatementFetchAll($stmt); @@ -323,56 +285,4 @@ public function testEncryptionDoesHappenWhenASecretIsChanged(): void $this->assertStringEndsWith('', $shouldBeDifferentFromBefore); // is encrypted $this->assertNotEquals($originalEncryption, $shouldBeDifferentFromBefore); } - - public function testEntitySetterUseStrtoupper() - { - $user = new CascadeTargetStrtoupper(); - $user->setNotSecret('My public information'); - $user->setSecret('my secret'); - $this->entityManager->persist($user); - $this->entityManager->flush(); - - $queryData = $this->getLatestInsertQuery(); - $params = array_values($queryData['params']); - $passwordData = $params[0] === 'My public information' ? $params[1] : $params[0]; - $secret = $user->getSecret(); - - $this->assertStringEndsWith(DoctrineEncryptSubscriber::ENCRYPTION_MARKER,$passwordData); - $this->assertStringDoesNotContain('my secret',$passwordData); - $this->assertEquals('MY SECRET', $secret); - } - - public function testEntityWithDateTimeJsonAndArrayProperties() - { - $user = new DateTimeJsonArrayTarget(); - $datetime = new DateTime(); - $jsonArray = [ - 'key' => 'value' - ]; - $array = [0, 1]; - $user->setDate($datetime); - $user->setJson($jsonArray); - $user->setArray($array); - $this->entityManager->persist($user); - $this->entityManager->flush(); - - $queryData = $this->getLatestInsertQuery(); - $params = array_values($queryData['params']); - - foreach ($params as $param) - { - $this->assertStringEndsWith(DoctrineEncryptSubscriber::ENCRYPTION_MARKER,$param); - } - - $entityDate = $user->getDate(); - $entityJson = $user->getJson(); - $entityArray = $user->getArray(); - - // Doctrine datetime type is only for date and time. milliseconds and timezone is not stored. - // We only test the date and time accordingly - // https://www.doctrine-project.org/projects/doctrine-dbal/en/3.7/reference/types.html#datetime - $this->assertEquals($datetime->format('Y-m-d\\TH:i:s'), $entityDate->format('Y-m-d\\TH:i:s')); - $this->assertEquals($jsonArray, $entityJson); - $this->assertEquals($array, $entityArray); - } } diff --git a/tests/Functional/DoctrineEncryptService/DoctrineEncryptServiceDefuseTest.php b/tests/Functional/DoctrineEncryptService/DoctrineEncryptServiceDefuseTest.php new file mode 100644 index 00000000..7ffa48f3 --- /dev/null +++ b/tests/Functional/DoctrineEncryptService/DoctrineEncryptServiceDefuseTest.php @@ -0,0 +1,14 @@ + "VehicleCar","bike" => "VehicleBicycle"])] abstract class AbstractVehicle { /** * @var int - * @ORM\Id - * @ORM\Column(type="integer") - * @ORM\GeneratedValue */ + #[ORM\Id] + #[ORM\Column(type:"integer")] + #[ORM\GeneratedValue] private $id; - /** - * @Ambta\DoctrineEncryptBundle\Configuration\Encrypted() - * @ORM\Column(type="string", nullable=true) - */ + #[ORM\Column(type:"encrypted", nullable:true)] private $secret; - /** - * @ORM\Column(type="string", nullable=true) - */ + #[ORM\Column(type:"string", nullable:true)] private $notSecret; /** diff --git a/tests/Functional/fixtures/Entity/CascadeTarget.php b/tests/Functional/fixtures/Entity/CascadeTarget.php index c98a3f5a..d020a15f 100644 --- a/tests/Functional/fixtures/Entity/CascadeTarget.php +++ b/tests/Functional/fixtures/Entity/CascadeTarget.php @@ -1,32 +1,24 @@ notSecret = $notSecret; } - } \ No newline at end of file diff --git a/tests/Functional/fixtures/Entity/CascadeTargetArray.php b/tests/Functional/fixtures/Entity/CascadeTargetArray.php new file mode 100755 index 00000000..95e13760 --- /dev/null +++ b/tests/Functional/fixtures/Entity/CascadeTargetArray.php @@ -0,0 +1,63 @@ +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 + */ + public function setNotSecret($notSecret): void + { + $this->notSecret = $notSecret; + } +} \ No newline at end of file diff --git a/tests/Functional/fixtures/Entity/CascadeTargetDateTime.php b/tests/Functional/fixtures/Entity/CascadeTargetDateTime.php new file mode 100755 index 00000000..9a0303bb --- /dev/null +++ b/tests/Functional/fixtures/Entity/CascadeTargetDateTime.php @@ -0,0 +1,63 @@ +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 + */ + public function setNotSecret($notSecret): void + { + $this->notSecret = $notSecret; + } +} \ No newline at end of file diff --git a/tests/Functional/fixtures/Entity/CascadeTargetJSON.php b/tests/Functional/fixtures/Entity/CascadeTargetJSON.php new file mode 100755 index 00000000..d7c9ef94 --- /dev/null +++ b/tests/Functional/fixtures/Entity/CascadeTargetJSON.php @@ -0,0 +1,63 @@ +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 + */ + public function setNotSecret($notSecret): void + { + $this->notSecret = $notSecret; + } +} \ No newline at end of file diff --git a/tests/Functional/fixtures/Entity/CascadeTargetStrtoupper.php b/tests/Functional/fixtures/Entity/CascadeTargetStrtoupper.php index 30b2e99b..24bf2bc6 100644 --- a/tests/Functional/fixtures/Entity/CascadeTargetStrtoupper.php +++ b/tests/Functional/fixtures/Entity/CascadeTargetStrtoupper.php @@ -1,33 +1,24 @@ secret = $secret; + $this->secret = strtoupper ($secret); } /** diff --git a/tests/Functional/fixtures/Entity/ClassTableInheritanceBase.php b/tests/Functional/fixtures/Entity/ClassTableInheritanceBase.php index e5aa089d..19c2e728 100644 --- a/tests/Functional/fixtures/Entity/ClassTableInheritanceBase.php +++ b/tests/Functional/fixtures/Entity/ClassTableInheritanceBase.php @@ -1,34 +1,27 @@ id; - } - - /** - * @return mixed - */ - public function getDate() - { - return $this->date; - } - - /** - * @param mixed $date - */ - public function setDate($date): void - { - $this->date = $date; - } - - /** - * @return mixed - */ - public function getJson() - { - return $this->json; - } - - /** - * @param mixed $json - */ - public function setJson($json): void - { - $this->json = $json; - } - - /** - * @return mixed - */ - public function getArray() - { - return $this->array; - } - - /** - * @param mixed $array - */ - public function setArray($array): void - { - $this->array = $array; - } -} diff --git a/tests/Functional/fixtures/Entity/Owner.php b/tests/Functional/fixtures/Entity/Owner.php index 92aa7341..ff98c0c9 100644 --- a/tests/Functional/fixtures/Entity/Owner.php +++ b/tests/Functional/fixtures/Entity/Owner.php @@ -1,41 +1,29 @@ cascaded = $cascaded; } -} +} \ No newline at end of file diff --git a/tests/Functional/fixtures/Entity/VehicleBicycle.php b/tests/Functional/fixtures/Entity/VehicleBicycle.php index 5865614d..5bf1c0ec 100644 --- a/tests/Functional/fixtures/Entity/VehicleBicycle.php +++ b/tests/Functional/fixtures/Entity/VehicleBicycle.php @@ -1,19 +1,17 @@ createContainer(); $this->extension->load([[]], $container); - $this->assertSame(HaliteEncryptor::class, $container->getParameter('ambta_doctrine_encrypt.encryptor_class_name')); + $this->assertSame(HaliteEncryptor::class, $container->getParameter('doctrine_encrypt.encryptor_class_name')); } public function testConfigLoadHalite(): void @@ -49,7 +49,7 @@ public function testConfigLoadHalite(): void ]; $this->extension->load([$config], $container); - $this->assertSame(HaliteEncryptor::class, $container->getParameter('ambta_doctrine_encrypt.encryptor_class_name')); + $this->assertSame(HaliteEncryptor::class, $container->getParameter('doctrine_encrypt.encryptor_class_name')); } public function testConfigLoadDefuse(): void @@ -61,7 +61,7 @@ public function testConfigLoadDefuse(): void ]; $this->extension->load([$config], $container); - $this->assertSame(DefuseEncryptor::class, $container->getParameter('ambta_doctrine_encrypt.encryptor_class_name')); + $this->assertSame(DefuseEncryptor::class, $container->getParameter('doctrine_encrypt.encryptor_class_name')); } public function testConfigLoadCustomEncryptor(): void @@ -72,7 +72,7 @@ public function testConfigLoadCustomEncryptor(): void ]; $this->extension->load([$config], $container); - $this->assertSame(self::class, $container->getParameter('ambta_doctrine_encrypt.encryptor_class_name')); + $this->assertSame(self::class, $container->getParameter('doctrine_encrypt.encryptor_class_name')); } public function testConfigImpossibleToUseSecretAndSecret_directory_path(): void @@ -96,10 +96,10 @@ public function testConfigUseSecret(): void ]; $this->extension->load([$config], $container); - $this->assertIsString($container->getParameter('ambta_doctrine_encrypt.secret')); - $this->assertStringNotContainsString('Halite',$container->getParameter('ambta_doctrine_encrypt.secret')); - $this->assertStringNotContainsString('.key',$container->getParameter('ambta_doctrine_encrypt.secret')); - $this->assertEquals('my-secret',$container->getParameter('ambta_doctrine_encrypt.secret')); + $this->assertIsString($container->getParameter('doctrine_encrypt.secret')); + $this->assertStringNotContainsString('Halite',$container->getParameter('doctrine_encrypt.secret')); + $this->assertStringNotContainsString('.key',$container->getParameter('doctrine_encrypt.secret')); + $this->assertEquals('my-secret',$container->getParameter('doctrine_encrypt.secret')); } public function testHaliteSecretIsCreatedWhenSecretFileDoesNotExistAndSecretCreationIsEnabled(): void @@ -111,7 +111,7 @@ public function testHaliteSecretIsCreatedWhenSecretFileDoesNotExistAndSecretCrea ]; $this->extension->load([$config], $container); - $secretArgument = $container->getDefinition('ambta_doctrine_encrypt.encryptor')->getArgument(0); + $secretArgument = $container->getDefinition('doctrine_encrypt.encryptor')->getArgument(0); if ($secretArgument instanceof Expression) { $actualSecret = $container->resolveServices($secretArgument); } else { @@ -138,7 +138,7 @@ public function testDefuseSecretIsCreatedWhenSecretFileDoesNotExistAndSecretCrea ]; $this->extension->load([$config], $container); - $secretArgument = $container->getDefinition('ambta_doctrine_encrypt.encryptor')->getArgument(0); + $secretArgument = $container->getDefinition('doctrine_encrypt.encryptor')->getArgument(0); if ($secretArgument instanceof Expression) { $actualSecret = $container->resolveServices($secretArgument); } else { @@ -165,14 +165,12 @@ public function testSecretIsNotCreatedWhenSecretFileDoesNotExistAndSecretCreatio $this->expectException(\RuntimeException::class); if (method_exists($this,'expectExceptionMessageMatches')) { $this->expectExceptionMessageMatches('/DoctrineEncryptBundle: Unable to create secret.*/'); - } elseif(method_exists($this,'expectExceptionMessageRegExp')) { - $this->expectExceptionMessageRegExp('/DoctrineEncryptBundle: Unable to create secret.*/'); } else { $this->markAsRisky('Unable to see if the exception matches the actual message'); } - $secretArgument = $container->getDefinition('ambta_doctrine_encrypt.encryptor')->getArgument(0); + $secretArgument = $container->getDefinition('doctrine_encrypt.encryptor')->getArgument(0); if ($secretArgument instanceof Expression) { $container->resolveServices($secretArgument); } @@ -191,7 +189,7 @@ public function testSecretsAreReadFromFile(): void ]; $this->extension->load([$config], $container); - $secretArgument = $container->getDefinition('ambta_doctrine_encrypt.encryptor')->getArgument(0); + $secretArgument = $container->getDefinition('doctrine_encrypt.encryptor')->getArgument(0); if ($secretArgument instanceof Expression) { $actualSecret = $container->resolveServices($secretArgument); } else { diff --git a/tests/Unit/Encryptors/DefuseEncryptorTest.php b/tests/Unit/Encryptors/DefuseEncryptorTest.php index d2e52d24..14425110 100644 --- a/tests/Unit/Encryptors/DefuseEncryptorTest.php +++ b/tests/Unit/Encryptors/DefuseEncryptorTest.php @@ -1,8 +1,8 @@ createMock(EncryptorInterface::class); + $encryptor + ->expects($this->any()) + ->method('encrypt') + ->willReturnCallback(function (string $arg) { + return 'encrypted-'.$arg; + }) + ; + $encryptor + ->expects($this->any()) + ->method('decrypt') + ->willReturnCallback(function (string $arg) { + return preg_replace('/^encrypted-/', '', $arg); + }) + ; + + $this->service = new Encrypt($encryptor); + } + + public function testEncrypt(): void + { + $string = 'Test'; + $result = $this->service->encrypt ('string', $string); + + $this->assertEquals('encrypted-'.$string.Encrypt::ENCRYPTION_MARKER, $result); + } + + public function testDecrypt(): void + { + $string = 'encrypted-Test'.Encrypt::ENCRYPTION_MARKER; + $result = $this->service->decrypt ('string', $string); + + $this->assertEquals('Test', $result); + } + + public function testEncryptDateTime(): void + { + $datetime = new DateTime(); + $result = $this->service->encrypt ('datetime', $datetime); + + $this->assertEquals('encrypted-'.$datetime->format('Y-m-d H:i:s').Encrypt::ENCRYPTION_MARKER, $result); + } + + public function testDecryptDateTime(): void + { + $datetime = new DateTime(); + $encrypted = $this->service->encrypt ('datetime', $datetime); + + $result = $this->service->decrypt ('datetime', $encrypted); + + $this->assertEquals($datetime->format('Y-m-d H:i:s'), $result->format('Y-m-d H:i:s')); + } + + public function testEncryptJSON(): void + { + $json = '{"test":"value"}'; + $jsonData = json_decode($json, true); + $result = $this->service->encrypt ('json', $jsonData); + + $this->assertEquals('encrypted-'.$json.Encrypt::ENCRYPTION_MARKER, $result); + } + + public function testDecryptJSON(): void + { + $json = '{"test":"value"}'; + $jsonData = json_decode($json, true); + $encrypted = $this->service->encrypt ('json', $jsonData); + + $result = $this->service->decrypt ('json', $encrypted); + + $this->assertEquals($jsonData, $result); + } + + public function testEncryptArray(): void + { + $array = ['test', 'value']; + $result = $this->service->encrypt ('simple_array', $array); + + $this->assertEquals('encrypted-'.implode(',',$array).Encrypt::ENCRYPTION_MARKER, $result); + } + + public function testDecryptArray(): void + { + $array = ['test', 'value']; + $encrypted = $this->service->encrypt ('simple_array', $array); + + $result = $this->service->decrypt ('simple_array', $encrypted); + + $this->assertEquals($array, $result); + } +} diff --git a/tests/Unit/Subscribers/DoctrineEncryptSubscriberTest.php b/tests/Unit/Subscribers/DoctrineEncryptSubscriberTest.php deleted file mode 100644 index 1b550f69..00000000 --- a/tests/Unit/Subscribers/DoctrineEncryptSubscriberTest.php +++ /dev/null @@ -1,358 +0,0 @@ -encryptor = $this->createMock(EncryptorInterface::class); - $this->encryptor - ->expects($this->any()) - ->method('encrypt') - ->willReturnCallback(function (string $arg) { - return 'encrypted-'.$arg; - }) - ; - $this->encryptor - ->expects($this->any()) - ->method('decrypt') - ->willReturnCallback(function (string $arg) { - return preg_replace('/^encrypted-/', '', $arg); - }) - ; - - $this->reader = $this->createMock(Reader::class); - $this->reader->expects($this->any()) - ->method('getPropertyAnnotation') - ->willReturnCallback(function (\ReflectionProperty $reflProperty, string $class) { - if (Encrypted::class === $class) { - if (\in_array($reflProperty->getName(), ['name', 'address', 'extra'])) - { - return new $class(); - } - } - if (Embedded::class === $class) { - return 'user' === $reflProperty->getName(); - } - - return false; - }) - ; - - $this->conn = $this->createMock(Connection::class); - $this->conn->method('getDatabasePlatform') - ->willReturnCallback(function() { - return new MySQL80Platform(); - }); - - $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->em->method('getConnection') - ->willReturnCallback(function() { - return $this->conn; - }); - - $this->subscriber = new DoctrineEncryptSubscriber($this->reader, $this->encryptor); - } - - public function testSetRestorEncryptor(): void - { - $replaceEncryptor = $this->createMock(EncryptorInterface::class); - - $this->assertSame($this->encryptor, $this->subscriber->getEncryptor()); - $this->subscriber->setEncryptor($replaceEncryptor); - $this->assertSame($replaceEncryptor, $this->subscriber->getEncryptor()); - $this->subscriber->restoreEncryptor(); - $this->assertSame($this->encryptor, $this->subscriber->getEncryptor()); - } - - private 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->triggerProcessFields($user,true); - - $this->assertStringStartsWith('encrypted-', $user->name); - $this->assertStringStartsWith('encrypted-', $user->getAddress()); - } - - public function testProcessFieldsEncryptExtend(): void - { - $user = new ExtendedUser('David', 'Switzerland', 'extra'); - - $this->triggerProcessFields($user,true); - - $this->assertStringStartsWith('encrypted-', $user->name); - $this->assertStringStartsWith('encrypted-', $user->getAddress()); - $this->assertStringStartsWith('encrypted-', $user->extra); - } - - public function testProcessFieldsEncryptEmbedded(): void - { - - $withUser = new WithUser('Thing', 'foo', new User('David', 'Switzerland')); - - $this->triggerProcessFields($withUser,true); - - $this->assertStringStartsWith('encrypted-', $withUser->name); - $this->assertSame('foo', $withUser->foo); - $this->assertStringStartsWith('encrypted-', $withUser->user->name); - $this->assertStringStartsWith('encrypted-', $withUser->user->getAddress()); - } - - public function testProcessFieldsEncryptNull(): void - { - $user = new User('David', null); - - $this->triggerProcessFields($user,true); - - $this->assertStringStartsWith('encrypted-', $user->name); - $this->assertNull($user->getAddress()); - } - - public function testProcessFieldsNoEncryptor(): void - { - $user = new User('David', 'Switzerland'); - - $this->subscriber->setEncryptor(null); - $this->triggerProcessFields($user,true); - - $this->assertSame('David', $user->name); - $this->assertSame('Switzerland', $user->getAddress()); - } - - public function testProcessFieldsDecrypt(): void - { - $user = new User('encrypted-David', 'encrypted-Switzerland'); - - $this->triggerProcessFields($user,false); - - $this->assertSame('David', $user->name); - $this->assertSame('Switzerland', $user->getAddress()); - } - - public function testProcessFieldsDecryptExtended(): void - { - $user = new ExtendedUser('encrypted-David', 'encrypted-Switzerland', 'encrypted-extra'); - - $this->triggerProcessFields($user,false); - - $this->assertSame('David', $user->name); - $this->assertSame('Switzerland', $user->getAddress()); - $this->assertSame('extra', $user->extra); - } - - public function testProcessFieldsDecryptEmbedded(): void - { - $withUser = new WithUser('encrypted-Thing', 'foo', new User('encrypted-David', 'encrypted-Switzerland')); - - $this->triggerProcessFields($withUser,false); - - $this->assertSame('Thing', $withUser->name); - $this->assertSame('foo', $withUser->foo); - $this->assertSame('David', $withUser->user->name); - $this->assertSame('Switzerland', $withUser->user->getAddress()); - } - - public function testProcessFieldsDecryptNull(): void - { - $user = new User('encrypted-David', null); - - $this->triggerProcessFields($user,false); - - $this->assertSame('David', $user->name); - $this->assertNull($user->getAddress()); - } - - public function testProcessFieldsDecryptNonEncrypted(): void - { - // no trailing but somethint that our mock decrypt would change if called - $user = new User('encrypted-David', 'encrypted-Switzerland'); - - $this->triggerProcessFields($user,false); - - $this->assertSame('encrypted-David', $user->name); - $this->assertSame('encrypted-Switzerland', $user->getAddress()); - } - - /** - * Test that fields are encrypted before flushing. - */ - public function testOnFlush(): void - { - $user = new User('David', 'Switzerland'); - - $uow = $this->createMock(UnitOfWork::class); - $uow->expects($this->any()) - ->method('getScheduledEntityInsertions') - ->willReturn([$user]) - ; - $em = $this->createMock(EntityManagerInterface::class); - $em->expects($this->any()) - ->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; - }); - $em->method('getConnection') - ->willReturnCallback(function() { - return $this->conn; - }); - $uow->expects($this->any())->method('recomputeSingleEntityChangeSet'); - - $onFlush = new OnFlushEventArgs($em); - - $this->subscriber->onFlush($onFlush); - - $this->assertStringStartsWith('encrypted-', $user->name); - $this->assertStringStartsWith('encrypted-', $user->getAddress()); - } - - /** - * Test that fields are decrypted again after flushing - */ - public function testPostFlush(): void - { - $user = new User('encrypted-David', 'encrypted-Switzerland'); - - $uow = $this->createMock(UnitOfWork::class); - $uow->expects($this->any()) - ->method('getIdentityMap') - ->willReturn([[$user]]) - ; - $em = $this->createMock(EntityManagerInterface::class); - $em->expects($this->any()) - ->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; - }); - $em->method('getConnection') - ->willReturnCallback(function() { - return $this->conn; - }); - - $postFlush = new PostFlushEventArgs($em); - - $this->subscriber->postFlush($postFlush); - - $this->assertSame('David', $user->name); - $this->assertSame('Switzerland', $user->getAddress()); - } - - public function testAnnotationsAreOnlyReadOnce(): void - { - $reader = $this->createMock(Reader::class); - $reader->expects($this->exactly(4)) // 2 properties and test if embedded and encrypted - ->method('getPropertyAnnotation') - ->willReturnCallback(function (\ReflectionProperty $reflProperty, string $class) { - if (Encrypted::class === $class) { - if (\in_array($reflProperty->getName(), ['name', 'address', 'extra'])) - { - return new $class(); - } - } - if (Embedded::class === $class) { - return 'user' === $reflProperty->getName(); - } - - return false; - }) - ; - - $subscriber = new DoctrineEncryptSubscriber($reader, $this->encryptor); - - - $user = new User('David', 'Switzerland'); - - $class = new ReflectionClass(DoctrineEncryptSubscriber::class); - $method = $class->getMethod('processFields'); - $method->setAccessible(true); - $method->invokeArgs($subscriber,[$user,$this->em,true]); - - // Execute second time and see if we once more read the propertyannotation - $method->invokeArgs($subscriber,[$user,$this->em,true]); - } -} diff --git a/tests/Unit/Subscribers/fixtures/ExtendedUser.php b/tests/Unit/Subscribers/fixtures/ExtendedUser.php deleted file mode 100644 index b58424c4..00000000 --- a/tests/Unit/Subscribers/fixtures/ExtendedUser.php +++ /dev/null @@ -1,20 +0,0 @@ -extra = $extra; - } -} diff --git a/tests/Unit/Subscribers/fixtures/User.php b/tests/Unit/Subscribers/fixtures/User.php deleted file mode 100644 index a11053da..00000000 --- a/tests/Unit/Subscribers/fixtures/User.php +++ /dev/null @@ -1,36 +0,0 @@ -name = $name; - $this->address = $address; - } - - public function getAddress(): ?string - { - return $this->address; - } - - public function setAddress(?string $address): void - { - $this->address = $address; - } -} diff --git a/tests/Unit/Subscribers/fixtures/WithUser.php b/tests/Unit/Subscribers/fixtures/WithUser.php deleted file mode 100644 index 35cc179a..00000000 --- a/tests/Unit/Subscribers/fixtures/WithUser.php +++ /dev/null @@ -1,33 +0,0 @@ -name = $name; - $this->foo = $foo; - $this->user = $user; - } -} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index a796b334..9e6ad3aa 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -6,7 +6,3 @@ } $autoload = require $file; - -if (method_exists(Doctrine\Common\Annotations\AnnotationRegistry::class,'registerLoader')) { - Doctrine\Common\Annotations\AnnotationRegistry::registerLoader([$autoload, 'loadClass']); -}