├── .editorconfig ├── .github └── workflows │ ├── lint.yml │ └── tests.yml ├── .gitignore ├── .php-cs-fixer.php ├── DataCollector └── HydrationDataCollector.php ├── DebeshaDoctrineProfileExtraBundle.php ├── DependencyInjection ├── Configuration.php └── DebeshaDoctrineProfileExtraExtension.php ├── LICENSE ├── ORM ├── HydrationLogger.php ├── LoggingArrayHydrator.php ├── LoggingConfiguration.php ├── LoggingEntityManager.php ├── LoggingHydratorTrait.php ├── LoggingObjectHydrator.php ├── LoggingScalarHydrator.php ├── LoggingSimpleObjectHydrator.php └── LoggingSingleScalarHydrator.php ├── README.md ├── Resources ├── config │ └── services.xml └── views │ └── Collector │ └── hydrations.html.twig ├── composer.json ├── phpstan.neon.dist ├── phpunit.xml.dist └── tests ├── Unit ├── DataCollector │ └── HydrationDataCollectorTest.php ├── ORM │ ├── HydrationLoggerTest.php │ ├── LoggingArrayHydratorTest.php │ ├── LoggingHydratorTraitTest.php │ ├── LoggingObjectHydratorTest.php │ ├── LoggingScalarHydratorTest.php │ ├── LoggingSimpleObjectHydratorTest.php │ └── LoggingSingleScalarHydratorTest.php └── bootstrap.php └── integration └── symfony73 ├── .editorconfig ├── .env ├── .env.dev ├── .env.test ├── .gitignore ├── bin └── console ├── composer-orm2.json ├── composer-orm2.lock ├── composer-orm3.json ├── composer-orm3.lock ├── composer.json ├── config ├── bundles.php ├── packages │ ├── cache.yaml │ ├── doctrine.yaml │ ├── doctrine_migrations.yaml │ ├── framework.yaml │ ├── routing.yaml │ ├── twig.yaml │ └── web_profiler.yaml ├── preload.php ├── routes.yaml ├── routes │ ├── framework.yaml │ └── web_profiler.yaml └── services.yaml ├── migrations └── Version20250606132451.php ├── phpunit.xml.dist ├── public └── index.php ├── src ├── Controller │ ├── .gitignore │ └── TestController.php ├── DataFixtures │ └── AppFixtures.php ├── Entity │ ├── .gitignore │ └── TestEntity.php ├── Kernel.php └── Repository │ ├── .gitignore │ └── .gitkeep ├── symfony-orm2.lock ├── symfony-orm3.lock ├── templates └── base.html.twig └── tests ├── TestControllerTest.php └── bootstrap.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | 15 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: [ "**" ] 6 | pull_request: 7 | branches: [ "**" ] 8 | 9 | jobs: 10 | php-lint: 11 | name: PHP Lint and Static Analysis 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | php-version: [ '8.1', '8.2', '8.3' ] 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Setup PHP 24 | uses: shivammathur/setup-php@v2 25 | with: 26 | php-version: ${{ matrix.php-version }} 27 | coverage: none 28 | tools: composer:v2 29 | extensions: mbstring, intl, pdo, sqlite, json 30 | 31 | - name: Validate Composer 32 | run: composer validate --strict 33 | 34 | - name: Get Composer cache directory 35 | id: composer-cache 36 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT 37 | 38 | - name: Cache Composer dependencies 39 | uses: actions/cache@v4 40 | with: 41 | path: ${{ steps.composer-cache.outputs.dir }} 42 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} 43 | restore-keys: | 44 | ${{ runner.os }}-composer- 45 | 46 | - name: Install dependencies 47 | run: composer install --no-interaction --no-progress --prefer-dist 48 | 49 | - name: Run php-cs-fixer (dry-run) 50 | run: | 51 | vendor/bin/php-cs-fixer fix --dry-run --diff --config=.php-cs-fixer.php --allow-unsupported-php-version=yes 52 | 53 | - name: Run PHPStan 54 | run: | 55 | vendor/bin/phpstan analyse --memory-limit=512M 56 | 57 | 58 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: 4 | push: 5 | branches: [ main, master ] 6 | pull_request: 7 | branches: [ main, master ] 8 | 9 | jobs: 10 | unit-tests: 11 | name: Unit Tests 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | php: [8.1, 8.2, 8.3] 16 | 17 | steps: 18 | - name: Checkout code 19 | uses: actions/checkout@v4 20 | 21 | - name: Setup PHP 22 | uses: shivammathur/setup-php@v2 23 | with: 24 | php-version: ${{ matrix.php }} 25 | extensions: mbstring, intl, pdo_sqlite 26 | coverage: none 27 | tools: composer:v2 28 | 29 | - name: Validate composer.json and composer.lock 30 | run: composer validate --strict 31 | 32 | - name: Cache Composer packages 33 | uses: actions/cache@v3 34 | with: 35 | path: vendor 36 | key: ${{ runner.os }}-php-${{ matrix.php }}-composer-${{ hashFiles('**/composer.lock') }} 37 | restore-keys: | 38 | ${{ runner.os }}-php-${{ matrix.php }}-composer- 39 | 40 | - name: Install dependencies 41 | run: composer install --prefer-dist --no-progress 42 | 43 | - name: Run unit tests 44 | run: composer test-unit 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .php_cs.cache 2 | .php-cs-fixer.cache 3 | .phpunit.cache/ 4 | vendor 5 | composer.lock 6 | -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | in(__DIR__) 8 | ->exclude(['vendor', 'var', 'tests/integration']); 9 | 10 | return (new Config()) 11 | ->setRiskyAllowed(true) 12 | ->setUsingCache(true) 13 | ->setCacheFile(__DIR__.'/.php-cs-fixer.cache') 14 | ->setRules([ 15 | '@Symfony' => true, 16 | '@Symfony:risky' => true, 17 | 'php_unit_method_casing' => ['case' => 'snake_case'], 18 | 'ordered_imports' => ['sort_algorithm' => 'alpha'], 19 | 'phpdoc_to_comment' => false, 20 | 'native_function_invocation' => ['include' => ['@compiler_optimized']], 21 | 'declare_strict_types' => false, 22 | 'array_syntax' => ['syntax' => 'short'], 23 | 'yoda_style' => true, 24 | 'control_structure_continuation_position' => ['position' => 'same_line'], 25 | 'no_superfluous_elseif' => true, 26 | 'nullable_type_declaration_for_default_null_value' => true, 27 | ]) 28 | ->setFinder($finder); 29 | 30 | 31 | -------------------------------------------------------------------------------- /DataCollector/HydrationDataCollector.php: -------------------------------------------------------------------------------- 1 | data['hydrations'] = []; 31 | } 32 | 33 | public function collect(Request $request, Response $response, ?\Throwable $exception = null): void 34 | { 35 | $hydrationsPerEntityManager = array_map( 36 | static function (ObjectManager $manager): array { 37 | if (!$manager instanceof LoggingEntityManager) { 38 | return []; 39 | } 40 | 41 | $configuration = $manager->getConfiguration(); 42 | if (!$configuration instanceof LoggingConfiguration) { 43 | return []; 44 | } 45 | 46 | $logger = $configuration->getHydrationLogger(); 47 | if (null === $logger) { 48 | return []; 49 | } 50 | 51 | return $logger->hydrations; 52 | }, 53 | $this->managerRegistry->getManagers(), 54 | ); 55 | 56 | $this->data['hydrations'] = array_merge(...array_values($hydrationsPerEntityManager)); 57 | } 58 | 59 | /** 60 | * @return array> 61 | */ 62 | public function getHydrations(): array 63 | { 64 | return $this->data['hydrations']; 65 | } 66 | 67 | public function getHydrationsCount(): int 68 | { 69 | return \count($this->data['hydrations']); 70 | } 71 | 72 | public function getTime(): float 73 | { 74 | $time = 0; 75 | foreach ($this->data['hydrations'] as $hydration) { 76 | if (isset($hydration['executionMS'])) { 77 | $time += $hydration['executionMS']; 78 | } 79 | } 80 | 81 | return $time; 82 | } 83 | 84 | public function getName(): string 85 | { 86 | return 'hydrations'; 87 | } 88 | 89 | public function reset(): void 90 | { 91 | $this->data = ['hydrations' => []]; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /DebeshaDoctrineProfileExtraBundle.php: -------------------------------------------------------------------------------- 1 | hasParameter('doctrine.orm.entity_manager.class')) { 20 | throw new \InvalidArgumentException('You must include DoctrineBundle/DoctrineBundle before DebeshaDoctrineProfileExtraBundle in your AppKernel.php'); 21 | } 22 | 23 | $configuration = new Configuration(); 24 | $config = $this->processConfiguration($configuration, $configs); 25 | 26 | $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 27 | $loader->load('services.xml'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 debesha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ORM/HydrationLogger.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | class HydrationLogger 19 | { 20 | /** 21 | * Executed hydrations. 22 | * 23 | * @var array> 24 | */ 25 | public array $hydrations = []; 26 | 27 | /** 28 | * If Debug Stack is enabled (log queries) or not. 29 | */ 30 | public bool $enabled = true; 31 | 32 | public ?float $start = null; 33 | 34 | public int $currentHydration = 0; 35 | 36 | /** 37 | * Marks a hydration as started. Timing is started. 38 | * 39 | * @param string $type type of hydration 40 | */ 41 | public function start(string $type): void 42 | { 43 | if ($this->enabled) { 44 | $this->start = microtime(true); 45 | 46 | $this->hydrations[++$this->currentHydration]['type'] = $type; 47 | } 48 | } 49 | 50 | /** 51 | * Marks a hydration as stopped. Number of hydrated entities and alias map is 52 | * passed to method. 53 | */ 54 | /** 55 | * @param array $aliasMap 56 | */ 57 | public function stop(int $resultNum, array $aliasMap): void 58 | { 59 | if ($this->enabled) { 60 | $this->hydrations[$this->currentHydration]['executionMS'] = microtime(true) - $this->start; 61 | $this->hydrations[$this->currentHydration]['resultNum'] = $resultNum; 62 | $this->hydrations[$this->currentHydration]['aliasMap'] = $aliasMap; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ORM/LoggingArrayHydrator.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | class LoggingConfiguration extends \Doctrine\ORM\Configuration 14 | { 15 | public function __construct() 16 | { 17 | $hydrationLogger = new HydrationLogger(); 18 | $this->setHydrationLogger($hydrationLogger); 19 | } 20 | 21 | public function getHydrationLogger(): ?HydrationLogger 22 | { 23 | if (property_exists($this, '_attributes')) { 24 | // ORM 2 25 | return $this->_attributes['hydrationLogger'] ?? null; 26 | } else { 27 | // ORM 3 28 | return $this->attributes['hydrationLogger'] ?? null; 29 | } 30 | } 31 | 32 | public function setHydrationLogger(HydrationLogger $logger): void 33 | { 34 | if (property_exists($this, '_attributes')) { 35 | // ORM 2 36 | $this->_attributes['hydrationLogger'] = $logger; 37 | } else { 38 | // ORM 3 39 | $this->attributes['hydrationLogger'] = $logger; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ORM/LoggingEntityManager.php: -------------------------------------------------------------------------------- 1 | new LoggingObjectHydrator($this), 36 | AbstractQuery::HYDRATE_ARRAY => new LoggingArrayHydrator($this), 37 | AbstractQuery::HYDRATE_SCALAR => new LoggingScalarHydrator($this), 38 | AbstractQuery::HYDRATE_SINGLE_SCALAR => new LoggingSingleScalarHydrator($this), 39 | AbstractQuery::HYDRATE_SIMPLEOBJECT => new LoggingSimpleObjectHydrator($this), 40 | default => parent::newHydrator($hydrationMode), 41 | }; 42 | } 43 | 44 | /** 45 | * @param array|Connection $connection 46 | * 47 | * @throws Exception 48 | */ 49 | public static function create(array|Connection $connection, Configuration $config, ?EventManager $eventManager = null): EntityManager 50 | { 51 | if (!$config->getMetadataDriverImpl()) { 52 | throw new \InvalidArgumentException('Missing mapping driver implementation'); 53 | } 54 | 55 | switch (true) { 56 | case \is_array($connection): 57 | $connection = DriverManager::getConnection( 58 | $connection, 59 | $config 60 | ); 61 | break; 62 | 63 | case $connection instanceof Connection: 64 | if (null !== $eventManager) { 65 | $connectionEventManager = method_exists($connection, 'getEventManager') ? $connection->getEventManager() : null; 66 | if (null !== $connectionEventManager && $connectionEventManager !== $eventManager) { 67 | throw new \InvalidArgumentException('Mismatched event manager'); 68 | } 69 | } 70 | break; 71 | 72 | default: 73 | throw new \InvalidArgumentException('Invalid argument'); 74 | } 75 | 76 | $em = $eventManager; 77 | if (null === $em && method_exists($connection, 'getEventManager')) { 78 | $em = $connection->getEventManager(); 79 | } 80 | 81 | return new self($connection, $config, $em ?: new EventManager()); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ORM/LoggingHydratorTrait.php: -------------------------------------------------------------------------------- 1 | $hints 36 | * 37 | * @phpstan-return mixed 38 | */ 39 | public function hydrateAll(/* Result */ $stmt, /* ResultSetMapping */ $resultSetMapping, /* array */ $hints = [])/* : Countable|array */ 40 | { 41 | // @phpstan-ignore-next-line Access through magic property on ORM2 hydrator 42 | $logger = $this->_em?->getConfiguration()?->getHydrationLogger(); 43 | if ($logger) { 44 | $shortName = (new \ReflectionClass($this))->getShortName(); 45 | $type = preg_replace('/^Logging/', '', $shortName) ?: $shortName; 46 | $logger->start($type); 47 | } 48 | 49 | // @phpstan-ignore-next-line parent::hydrateAll exists in real hydrator subclasses 50 | $result = parent::hydrateAll($stmt, $resultSetMapping, $hints); 51 | 52 | if (isset($logger)) { 53 | if (is_countable($result)) { 54 | $logger->stop(\count($result), $resultSetMapping->getAliasMap()); 55 | } 56 | } 57 | 58 | return $result; 59 | } 60 | } 61 | } else { 62 | // ORM 3 63 | /** 64 | * @phpstan-require-extends \Doctrine\ORM\Internal\Hydration\AbstractHydrator 65 | * 66 | * @property \Doctrine\ORM\EntityManagerInterface $em 67 | */ 68 | trait LoggingHydratorTrait 69 | { 70 | /** 71 | * Hydrates all rows returned by the passed statement instance at once. 72 | * 73 | * @psalm-param array $hints 74 | * 75 | * @phpstan-return mixed 76 | */ 77 | public function hydrateAll(Result $stmt, ResultSetMapping $resultSetMapping, array $hints = []): mixed 78 | { 79 | // For ORM 2.0 and 3.0 compatibility 80 | $logger = $this->em->getConfiguration()->getHydrationLogger(); 81 | if ($logger) { 82 | $shortName = (new \ReflectionClass($this))->getShortName(); 83 | $type = preg_replace('/^Logging/', '', $shortName) ?: $shortName; 84 | $logger->start($type); 85 | } 86 | 87 | // @phpstan-ignore-next-line parent::hydrateAll exists in real hydrator subclasses 88 | $result = parent::hydrateAll($stmt, $resultSetMapping, $hints); 89 | 90 | if (isset($logger)) { 91 | if (is_countable($result)) { 92 | $logger->stop(\count($result), $resultSetMapping->getAliasMap()); 93 | } 94 | } 95 | 96 | return $result; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /ORM/LoggingObjectHydrator.php: -------------------------------------------------------------------------------- 1 | getEnvironment(), array('dev', 'test'))) { 30 | [...] 31 | $bundles[] = new Debesha\DoctrineProfileExtraBundle\DebeshaDoctrineProfileExtraBundle(); 32 | } 33 | ``` 34 | 35 | **Attention! The bundle MUST be included AFTER DoctrineBundle.** 36 | 37 | That's it. 38 | 39 | ## Screenshots 40 | 41 | Your profile gets a new section where you may get an information about how fast was hydrations made and 42 | how many entities was hydrated. 43 | 44 | ![Screenshot](http://i.imgur.com/GsvkIIN.png) 45 | 46 | ![Screenshot](http://i.imgur.com/pkLzlc8.png) 47 | -------------------------------------------------------------------------------- /Resources/config/services.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 10 | Debesha\DoctrineProfileExtraBundle\ORM\LoggingEntityManager 11 | Debesha\DoctrineProfileExtraBundle\ORM\LoggingConfiguration 12 | 13 | 14 | 15 | 17 | 18 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Resources/views/Collector/hydrations.html.twig: -------------------------------------------------------------------------------- 1 | {% extends app.request.isXmlHttpRequest ? '@WebProfiler/Profiler/ajax_layout.html.twig' : '@WebProfiler/Profiler/layout.html.twig' %} 2 | 3 | {% block toolbar %} 4 | {% set icon %} 5 | Database 7 | {{ collector.hydrationsCount }} 8 | {% if collector.hydrationsCount > 0 %} 9 | in {{ '%0.2f'|format(collector.time * 1000) }} ms 10 | {% endif %} 11 | {% endset %} 12 | {% set text %} 13 |
14 | Doctrine Hydrations: 15 |
16 | {% endset %} 17 | {% include '@WebProfiler/Profiler/toolbar_item.html.twig' with { 'link': profiler_url } %} 18 | {% endblock %} 19 | 20 | {% block menu %} 21 | 22 | 25 | Hydrations 26 | 27 | {{ collector.hydrationsCount }} 28 | {{ '%0.0f'|format(collector.time * 1000) }} ms 29 | 30 | 31 | {% endblock %} 32 | 33 | {% block panel %} 34 | {% if 'explain' == page %} 35 | {{ render(controller('DoctrineBundle:Profiler:explain', { 36 | 'token': token, 37 | 'panel': 'db', 38 | 'connectionName': app.request.query.get('connection'), 39 | 'query': app.request.query.get('query') 40 | })) }} 41 | {% else %} 42 |
43 | 52 | 53 | {{ block('hydrations') }} 54 |
55 | {% endif %} 56 | {% endblock %} 57 | 58 | {% block hydrations %} 59 |

Hydrations

60 | 61 | {% if collector.hydrations is empty %} 62 |

63 | No hydrations. 64 |

65 | {% else %} 66 | 67 | 68 | 69 | 71 | 73 | 75 | 77 | 78 | 79 | 80 | 81 | {% for i, hydration in collector.hydrations %} 82 | 83 | 84 | 90 | 91 | 92 | 93 | 102 | 103 | {% endfor %} 104 | 105 |
# 72 | Time 74 | Entities 76 | TypeAlias Map
{{ loop.index }}{% if hydration.executionMS is defined %} 85 | {{ '%0.2f'|format(hydration.executionMS * 1000) }} ms 86 | {% else %} 87 | Unknown 88 | {% endif %} 89 | {% if hydration.resultNum is defined %}{{ hydration.resultNum }}{% else %}Unknown{% endif %}{% if hydration.type is defined %}{{ hydration.type }} {% else %}Unknown{% endif %} 94 | {% if hydration.aliasMap is defined %} 95 |
    96 | {% for alias, class in hydration.aliasMap %} 97 |
  • {{ alias }} => {{ class }}
  • 98 | {% endfor %} 99 |
100 | {% else %}Unknown{% endif %} 101 |
106 | 107 | 145 | 146 | {% endif %} 147 | 148 | {% endblock %} 149 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "debesha/doctrine-hydration-profiler-bundle", 3 | "type": "symfony-bundle", 4 | "description": "Information about hydration of doctrine entities at the profiler", 5 | "keywords": [ 6 | "debesha", 7 | "profiler", 8 | "extra", 9 | "bundle", 10 | "doctrine", 11 | "symfony", 12 | "dev" 13 | ], 14 | "homepage": "https://github.com/debesha/DoctrineProfileExtraBundle", 15 | "license": "MIT", 16 | "authors": [ 17 | { 18 | "name": "Dmytro Malyshenko", 19 | "homepage": "http://malyshenko.com" 20 | } 21 | ], 22 | "require": { 23 | "php": ">=8.0", 24 | "doctrine/orm": "^2.19|^3.0", 25 | "doctrine/doctrine-bundle": "^2.11", 26 | "symfony/framework-bundle": "^5.4|^6.0|^7.0", 27 | "symfony/twig-bundle": "^5.4|^6.0|^7.0", 28 | "twig/twig": "~3.11.2" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "^10.0", 32 | "roave/security-advisories": "dev-latest", 33 | "friendsofphp/php-cs-fixer": "^3.64", 34 | "phpstan/phpstan": "^1.11", 35 | "phpstan/extension-installer": "^1.3", 36 | "phpstan/phpstan-symfony": "^1.4", 37 | "phpstan/phpstan-doctrine": "^1.4" 38 | }, 39 | "autoload": { 40 | "psr-4": { 41 | "Debesha\\DoctrineProfileExtraBundle\\": "." 42 | } 43 | }, 44 | "scripts": { 45 | "test-unit": "vendor/bin/phpunit --testsuite=unit", 46 | "test-integration-symfony73-orm2": [ 47 | "cd tests/integration/symfony73 && rm -rf var/cache", 48 | "cd tests/integration/symfony73 && env COMPOSER=composer-orm2.json composer install", 49 | "cd tests/integration/symfony73 && vendor/bin/phpunit" 50 | ], 51 | "test-integration-symfony73-orm3": [ 52 | "cd tests/integration/symfony73 && rm -rf var/cache", 53 | "cd tests/integration/symfony73 && env COMPOSER=composer-orm3.json composer install", 54 | "cd tests/integration/symfony73 && vendor/bin/phpunit" 55 | ], 56 | "test": [ 57 | "@test-unit", 58 | "@test-integration-symfony73-orm2", 59 | "@test-integration-symfony73-orm3" 60 | ], 61 | "cs:fix": "PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php", 62 | "cs:check": "PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix --dry-run --diff --config=.php-cs-fixer.php", 63 | "stan": "vendor/bin/phpstan analyse --memory-limit=512M" 64 | }, 65 | "archive": { 66 | "exclude": [ 67 | "/tests" 68 | ] 69 | }, 70 | "config": { 71 | "allow-plugins": { 72 | "phpstan/extension-installer": true 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: 8 3 | paths: 4 | - . 5 | excludePaths: 6 | - vendor 7 | - var 8 | - tests/integration 9 | - tests/Unit/ORM/LoggingHydratorTraitTest.php 10 | - ORM/LoggingHydratorTrait.php 11 | checkMissingIterableValueType: true 12 | checkGenericClassInNonGenericObjectType: true 13 | symfony: 14 | container_xml_path: null 15 | doctrine: 16 | objectManagerLoader: null 17 | 18 | ignoreErrors: [] 19 | 20 | 21 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | tests/Unit 13 | 14 | 15 | 16 | 17 | 18 | . 19 | 20 | 21 | tests 22 | vendor 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/Unit/DataCollector/HydrationDataCollectorTest.php: -------------------------------------------------------------------------------- 1 | managerRegistry = $this->createMock(ManagerRegistry::class); 26 | $this->collector = new HydrationDataCollector($this->managerRegistry); 27 | $this->request = new Request(); 28 | $this->response = new Response(); 29 | } 30 | 31 | public function test_constructor(): void 32 | { 33 | $this->assertInstanceOf(HydrationDataCollector::class, $this->collector); 34 | $this->assertEquals([], $this->collector->getHydrations()); 35 | } 36 | 37 | public function test_collect_with_logging_entity_managers(): void 38 | { 39 | // Create mock hydration logger with test data 40 | $hydrationLogger = new HydrationLogger(); 41 | $hydrationLogger->hydrations = [ 42 | [ 43 | 'type' => 'object', 44 | 'executionMS' => 0.5, 45 | 'resultNum' => 10, 46 | 'aliasMap' => ['e' => 'TestEntity'], 47 | ], 48 | [ 49 | 'type' => 'array', 50 | 'executionMS' => 0.3, 51 | 'resultNum' => 5, 52 | 'aliasMap' => ['e' => 'TestEntity'], 53 | ], 54 | ]; 55 | 56 | // Create mock logging configuration 57 | $loggingConfiguration = $this->createMock(LoggingConfiguration::class); 58 | $loggingConfiguration->expects($this->any()) 59 | ->method('getHydrationLogger') 60 | ->willReturn($hydrationLogger); 61 | 62 | // Create mock logging entity manager 63 | $loggingEntityManager = $this->createMock(LoggingEntityManager::class); 64 | $loggingEntityManager->expects($this->any()) 65 | ->method('getConfiguration') 66 | ->willReturn($loggingConfiguration); 67 | 68 | // Create mock regular entity manager (should be ignored) 69 | $regularEntityManager = $this->createMock(ObjectManager::class); 70 | 71 | // Setup manager registry to return both types of managers 72 | $this->managerRegistry->expects($this->any()) 73 | ->method('getManagers') 74 | ->willReturn([ 75 | 'logging' => $loggingEntityManager, 76 | 'regular' => $regularEntityManager, 77 | ]); 78 | 79 | $this->collector->collect($this->request, $this->response); 80 | 81 | $hydrations = $this->collector->getHydrations(); 82 | $this->assertCount(2, $hydrations); 83 | $this->assertEquals('object', $hydrations[0]['type']); 84 | $this->assertEquals('array', $hydrations[1]['type']); 85 | } 86 | 87 | public function test_collect_with_non_logging_entity_managers(): void 88 | { 89 | // Create mock regular entity manager 90 | $regularEntityManager = $this->createMock(ObjectManager::class); 91 | 92 | $this->managerRegistry->expects($this->any()) 93 | ->method('getManagers') 94 | ->willReturn(['regular' => $regularEntityManager]); 95 | 96 | $this->collector->collect($this->request, $this->response); 97 | 98 | $this->assertEquals([], $this->collector->getHydrations()); 99 | } 100 | 101 | public function test_collect_with_logging_entity_manager_but_non_logging_configuration(): void 102 | { 103 | // Create mock logging entity manager 104 | $loggingEntityManager = $this->createMock(LoggingEntityManager::class); 105 | 106 | // But with non-logging configuration 107 | $regularConfiguration = $this->createMock(\Doctrine\ORM\Configuration::class); 108 | $loggingEntityManager->expects($this->any()) 109 | ->method('getConfiguration') 110 | ->willReturn($regularConfiguration); 111 | 112 | $this->managerRegistry->method('getManagers') 113 | ->willReturn(['logging' => $loggingEntityManager]); 114 | 115 | $this->collector->collect($this->request, $this->response); 116 | 117 | $this->assertEquals([], $this->collector->getHydrations()); 118 | } 119 | 120 | public function test_collect_with_empty_hydrations(): void 121 | { 122 | // Create mock hydration logger with empty data 123 | $hydrationLogger = new HydrationLogger(); 124 | $hydrationLogger->hydrations = []; 125 | 126 | $loggingConfiguration = $this->createMock(LoggingConfiguration::class); 127 | $loggingConfiguration->expects($this->any()) 128 | ->method('getHydrationLogger') 129 | ->willReturn($hydrationLogger); 130 | 131 | $loggingEntityManager = $this->createMock(LoggingEntityManager::class); 132 | $loggingEntityManager->expects($this->any()) 133 | ->method('getConfiguration') 134 | ->willReturn($loggingConfiguration); 135 | 136 | $this->managerRegistry->expects($this->any()) 137 | ->method('getManagers') 138 | ->willReturn(['logging' => $loggingEntityManager]); 139 | 140 | $this->collector->collect($this->request, $this->response); 141 | 142 | $this->assertEquals([], $this->collector->getHydrations()); 143 | } 144 | 145 | public function test_get_hydrations(): void 146 | { 147 | $testHydrations = [ 148 | ['type' => 'test', 'executionMS' => 0.1], 149 | ]; 150 | 151 | // Use reflection to set private data 152 | $reflection = new \ReflectionClass($this->collector); 153 | $dataProperty = $reflection->getProperty('data'); 154 | $dataProperty->setAccessible(true); 155 | $dataProperty->setValue($this->collector, ['hydrations' => $testHydrations]); 156 | 157 | $this->assertEquals($testHydrations, $this->collector->getHydrations()); 158 | } 159 | 160 | public function test_get_hydrations_count(): void 161 | { 162 | $testHydrations = [ 163 | ['type' => 'test1'], 164 | ['type' => 'test2'], 165 | ['type' => 'test3'], 166 | ]; 167 | 168 | // Use reflection to set private data 169 | $reflection = new \ReflectionClass($this->collector); 170 | $dataProperty = $reflection->getProperty('data'); 171 | $dataProperty->setAccessible(true); 172 | $dataProperty->setValue($this->collector, ['hydrations' => $testHydrations]); 173 | 174 | $this->assertEquals(3, $this->collector->getHydrationsCount()); 175 | } 176 | 177 | public function test_get_hydrations_count_with_empty_hydrations(): void 178 | { 179 | $this->assertEquals(0, $this->collector->getHydrationsCount()); 180 | } 181 | 182 | public function test_get_time(): void 183 | { 184 | $testHydrations = [ 185 | ['type' => 'test1', 'executionMS' => 0.5], 186 | ['type' => 'test2', 'executionMS' => 0.3], 187 | ['type' => 'test3', 'executionMS' => 0.2], 188 | ['type' => 'test4'], // Missing executionMS 189 | ]; 190 | 191 | // Use reflection to set private data 192 | $reflection = new \ReflectionClass($this->collector); 193 | $dataProperty = $reflection->getProperty('data'); 194 | $dataProperty->setAccessible(true); 195 | $dataProperty->setValue($this->collector, ['hydrations' => $testHydrations]); 196 | 197 | $this->assertEquals(1.0, $this->collector->getTime()); 198 | } 199 | 200 | public function test_get_time_with_no_execution_time(): void 201 | { 202 | $testHydrations = [ 203 | ['type' => 'test1'], 204 | ['type' => 'test2'], 205 | ]; 206 | 207 | // Use reflection to set private data 208 | $reflection = new \ReflectionClass($this->collector); 209 | $dataProperty = $reflection->getProperty('data'); 210 | $dataProperty->setAccessible(true); 211 | $dataProperty->setValue($this->collector, ['hydrations' => $testHydrations]); 212 | 213 | $this->assertEquals(0.0, $this->collector->getTime()); 214 | } 215 | 216 | public function test_get_time_with_empty_hydrations(): void 217 | { 218 | $this->assertEquals(0.0, $this->collector->getTime()); 219 | } 220 | 221 | public function test_get_name(): void 222 | { 223 | $this->assertEquals('hydrations', $this->collector->getName()); 224 | } 225 | 226 | public function test_reset(): void 227 | { 228 | // First, add some test data 229 | $testHydrations = [ 230 | ['type' => 'test1', 'executionMS' => 0.1], 231 | ['type' => 'test2', 'executionMS' => 0.2], 232 | ]; 233 | 234 | // Use reflection to set private data 235 | $reflection = new \ReflectionClass($this->collector); 236 | $dataProperty = $reflection->getProperty('data'); 237 | $dataProperty->setAccessible(true); 238 | $dataProperty->setValue($this->collector, ['hydrations' => $testHydrations]); 239 | 240 | // Verify data was set 241 | $this->assertCount(2, $this->collector->getHydrations()); 242 | 243 | // Reset 244 | $this->collector->reset(); 245 | 246 | // Verify data was reset 247 | $this->assertEquals([], $this->collector->getHydrations()); 248 | $this->assertEquals(0, $this->collector->getHydrationsCount()); 249 | $this->assertEquals(0.0, $this->collector->getTime()); 250 | } 251 | 252 | public function test_collect_with_multiple_entity_managers(): void 253 | { 254 | // Create first hydration logger with test data 255 | $hydrationLogger1 = new HydrationLogger(); 256 | $hydrationLogger1->hydrations = [ 257 | ['type' => 'object', 'executionMS' => 0.5], 258 | ]; 259 | 260 | $loggingConfiguration1 = $this->createMock(LoggingConfiguration::class); 261 | $loggingConfiguration1->expects($this->any()) 262 | ->method('getHydrationLogger') 263 | ->willReturn($hydrationLogger1); 264 | 265 | $loggingEntityManager1 = $this->createMock(LoggingEntityManager::class); 266 | $loggingEntityManager1->expects($this->any()) 267 | ->method('getConfiguration') 268 | ->willReturn($loggingConfiguration1); 269 | 270 | // Create second hydration logger with different test data 271 | $hydrationLogger2 = new HydrationLogger(); 272 | $hydrationLogger2->hydrations = [ 273 | ['type' => 'array', 'executionMS' => 0.3], 274 | ]; 275 | 276 | $loggingConfiguration2 = $this->createMock(LoggingConfiguration::class); 277 | $loggingConfiguration2->expects($this->any()) 278 | ->method('getHydrationLogger') 279 | ->willReturn($hydrationLogger2); 280 | 281 | $loggingEntityManager2 = $this->createMock(LoggingEntityManager::class); 282 | $loggingEntityManager2->expects($this->any()) 283 | ->method('getConfiguration') 284 | ->willReturn($loggingConfiguration2); 285 | 286 | // Setup manager registry to return both managers 287 | $this->managerRegistry->expects($this->any()) 288 | ->method('getManagers') 289 | ->willReturn([ 290 | 'em1' => $loggingEntityManager1, 291 | 'em2' => $loggingEntityManager2, 292 | ]); 293 | 294 | $this->collector->collect($this->request, $this->response); 295 | 296 | $hydrations = $this->collector->getHydrations(); 297 | $this->assertCount(2, $hydrations); 298 | $this->assertEquals('object', $hydrations[0]['type']); 299 | $this->assertEquals('array', $hydrations[1]['type']); 300 | $this->assertEquals(0.8, $this->collector->getTime()); 301 | } 302 | } 303 | -------------------------------------------------------------------------------- /tests/Unit/ORM/HydrationLoggerTest.php: -------------------------------------------------------------------------------- 1 | logger = new HydrationLogger(); 15 | } 16 | 17 | public function test_constructor(): void 18 | { 19 | $this->assertInstanceOf(HydrationLogger::class, $this->logger); 20 | $this->assertEquals([], $this->logger->hydrations); 21 | $this->assertTrue($this->logger->enabled); 22 | $this->assertNull($this->logger->start); 23 | $this->assertEquals(0, $this->logger->currentHydration); 24 | } 25 | 26 | public function test_start_when_enabled(): void 27 | { 28 | $this->logger->enabled = true; 29 | 30 | $this->logger->start('object'); 31 | 32 | $this->assertEquals(1, $this->logger->currentHydration); 33 | $this->assertNotNull($this->logger->start); 34 | $this->assertIsFloat($this->logger->start); 35 | $this->assertArrayHasKey(1, $this->logger->hydrations); 36 | $this->assertEquals('object', $this->logger->hydrations[1]['type']); 37 | } 38 | 39 | public function test_start_when_disabled(): void 40 | { 41 | $this->logger->enabled = false; 42 | $this->logger->start = 123.45; 43 | $this->logger->currentHydration = 5; 44 | 45 | $this->logger->start('array'); 46 | 47 | // Should not change anything when disabled 48 | $this->assertEquals(5, $this->logger->currentHydration); 49 | $this->assertEquals(123.45, $this->logger->start); 50 | $this->assertArrayNotHasKey(6, $this->logger->hydrations); 51 | } 52 | 53 | public function test_start_increments_current_hydration(): void 54 | { 55 | $this->logger->enabled = true; 56 | 57 | $this->logger->start('object'); 58 | $this->assertEquals(1, $this->logger->currentHydration); 59 | 60 | $this->logger->start('array'); 61 | $this->assertEquals(2, $this->logger->currentHydration); 62 | 63 | $this->logger->start('scalar'); 64 | $this->assertEquals(3, $this->logger->currentHydration); 65 | } 66 | 67 | public function test_start_sets_microtime(): void 68 | { 69 | $this->logger->enabled = true; 70 | 71 | $beforeStart = microtime(true); 72 | $this->logger->start('object'); 73 | $afterStart = microtime(true); 74 | 75 | $this->assertGreaterThanOrEqual($beforeStart, $this->logger->start); 76 | $this->assertLessThanOrEqual($afterStart, $this->logger->start); 77 | } 78 | 79 | public function test_stop_when_enabled(): void 80 | { 81 | $this->logger->enabled = true; 82 | $this->logger->start('object'); 83 | $this->logger->start = microtime(true) - 0.1; // Simulate 100ms ago 84 | 85 | $this->logger->stop(15, ['e' => 'TestEntity']); 86 | 87 | $this->assertArrayHasKey(1, $this->logger->hydrations); 88 | $this->assertArrayHasKey('executionMS', $this->logger->hydrations[1]); 89 | $this->assertArrayHasKey('resultNum', $this->logger->hydrations[1]); 90 | $this->assertArrayHasKey('aliasMap', $this->logger->hydrations[1]); 91 | 92 | $this->assertEquals(15, $this->logger->hydrations[1]['resultNum']); 93 | $this->assertEquals(['e' => 'TestEntity'], $this->logger->hydrations[1]['aliasMap']); 94 | 95 | // Execution time should be approximately 100ms 96 | $this->assertGreaterThan(0.09, $this->logger->hydrations[1]['executionMS']); 97 | $this->assertLessThan(0.11, $this->logger->hydrations[1]['executionMS']); 98 | } 99 | 100 | public function test_stop_when_disabled(): void 101 | { 102 | $this->logger->enabled = false; 103 | $this->logger->start('object'); 104 | $this->logger->start = 123.45; 105 | 106 | $this->logger->stop(15, ['e' => 'TestEntity']); 107 | 108 | // Should not change anything when disabled 109 | $this->assertEquals(123.45, $this->logger->start); 110 | // When disabled, no hydration entry should be created 111 | $this->assertEmpty($this->logger->hydrations); 112 | } 113 | 114 | public function test_stop_without_start(): void 115 | { 116 | $this->logger->enabled = true; 117 | $this->logger->currentHydration = 1; 118 | 119 | // This should not cause an error 120 | $this->logger->stop(10, ['e' => 'TestEntity']); 121 | 122 | // Should still record the data even without a proper start 123 | $this->assertArrayHasKey(1, $this->logger->hydrations); 124 | $this->assertArrayHasKey('resultNum', $this->logger->hydrations[1]); 125 | $this->assertArrayHasKey('aliasMap', $this->logger->hydrations[1]); 126 | } 127 | 128 | public function test_multiple_hydrations(): void 129 | { 130 | $this->logger->enabled = true; 131 | 132 | // First hydration 133 | $this->logger->start('object'); 134 | $this->logger->start = microtime(true) - 0.05; 135 | $this->logger->stop(10, ['e' => 'Entity1']); 136 | 137 | // Second hydration 138 | $this->logger->start('array'); 139 | $this->logger->start = microtime(true) - 0.03; 140 | $this->logger->stop(5, ['e' => 'Entity2']); 141 | 142 | // Third hydration 143 | $this->logger->start('scalar'); 144 | $this->logger->start = microtime(true) - 0.01; 145 | $this->logger->stop(1, ['e' => 'Entity3']); 146 | 147 | $this->assertEquals(3, $this->logger->currentHydration); 148 | $this->assertCount(3, $this->logger->hydrations); 149 | 150 | // Check first hydration 151 | $this->assertEquals('object', $this->logger->hydrations[1]['type']); 152 | $this->assertEquals(10, $this->logger->hydrations[1]['resultNum']); 153 | $this->assertEquals(['e' => 'Entity1'], $this->logger->hydrations[1]['aliasMap']); 154 | 155 | // Check second hydration 156 | $this->assertEquals('array', $this->logger->hydrations[2]['type']); 157 | $this->assertEquals(5, $this->logger->hydrations[2]['resultNum']); 158 | $this->assertEquals(['e' => 'Entity2'], $this->logger->hydrations[2]['aliasMap']); 159 | 160 | // Check third hydration 161 | $this->assertEquals('scalar', $this->logger->hydrations[3]['type']); 162 | $this->assertEquals(1, $this->logger->hydrations[3]['resultNum']); 163 | $this->assertEquals(['e' => 'Entity3'], $this->logger->hydrations[3]['aliasMap']); 164 | } 165 | 166 | public function test_enable_disable(): void 167 | { 168 | // Start with enabled 169 | $this->logger->enabled = true; 170 | $this->logger->start('object'); 171 | $this->assertEquals(1, $this->logger->currentHydration); 172 | 173 | // Disable and start another 174 | $this->logger->enabled = false; 175 | $this->logger->start('array'); 176 | $this->assertEquals(1, $this->logger->currentHydration); // Should not increment 177 | 178 | // Re-enable and start another 179 | $this->logger->enabled = true; 180 | $this->logger->start('scalar'); 181 | $this->assertEquals(2, $this->logger->currentHydration); // Should increment again 182 | } 183 | 184 | public function test_stop_with_zero_results(): void 185 | { 186 | $this->logger->enabled = true; 187 | $this->logger->start('object'); 188 | $this->logger->start = microtime(true) - 0.01; 189 | 190 | $this->logger->stop(0, []); 191 | 192 | $this->assertEquals(0, $this->logger->hydrations[1]['resultNum']); 193 | $this->assertEquals([], $this->logger->hydrations[1]['aliasMap']); 194 | } 195 | 196 | public function test_stop_with_empty_alias_map(): void 197 | { 198 | $this->logger->enabled = true; 199 | $this->logger->start('object'); 200 | $this->logger->start = microtime(true) - 0.01; 201 | 202 | $this->logger->stop(5, []); 203 | 204 | $this->assertEquals(5, $this->logger->hydrations[1]['resultNum']); 205 | $this->assertEquals([], $this->logger->hydrations[1]['aliasMap']); 206 | } 207 | 208 | public function test_stop_with_complex_alias_map(): void 209 | { 210 | $this->logger->enabled = true; 211 | $this->logger->start('object'); 212 | $this->logger->start = microtime(true) - 0.01; 213 | 214 | $complexAliasMap = [ 215 | 'e' => 'TestEntity', 216 | 'c' => 'Category', 217 | 'u' => 'User', 218 | 'p' => 'Product', 219 | ]; 220 | 221 | $this->logger->stop(25, $complexAliasMap); 222 | 223 | $this->assertEquals(25, $this->logger->hydrations[1]['resultNum']); 224 | $this->assertEquals($complexAliasMap, $this->logger->hydrations[1]['aliasMap']); 225 | } 226 | 227 | public function test_execution_time_accuracy(): void 228 | { 229 | $this->logger->enabled = true; 230 | 231 | // Start timing 232 | $this->logger->start('object'); 233 | $startTime = $this->logger->start; 234 | 235 | // Simulate some work 236 | usleep(1000); // 1ms 237 | 238 | // Stop timing 239 | $this->logger->stop(1, ['e' => 'TestEntity']); 240 | 241 | $executionTime = $this->logger->hydrations[1]['executionMS']; 242 | 243 | // Execution time should be at least 1ms 244 | $this->assertGreaterThan(0.001, $executionTime); 245 | 246 | // And should be reasonable (not more than 10ms for this simple operation) 247 | $this->assertLessThan(0.01, $executionTime); 248 | } 249 | 250 | public function test_hydration_data_structure(): void 251 | { 252 | $this->logger->enabled = true; 253 | $this->logger->start('object'); 254 | $this->logger->start = microtime(true) - 0.01; 255 | $this->logger->stop(10, ['e' => 'TestEntity']); 256 | 257 | $hydration = $this->logger->hydrations[1]; 258 | 259 | // Check all required keys exist 260 | $this->assertArrayHasKey('type', $hydration); 261 | $this->assertArrayHasKey('executionMS', $hydration); 262 | $this->assertArrayHasKey('resultNum', $hydration); 263 | $this->assertArrayHasKey('aliasMap', $hydration); 264 | 265 | // Check data types 266 | $this->assertIsString($hydration['type']); 267 | $this->assertIsFloat($hydration['executionMS']); 268 | $this->assertIsInt($hydration['resultNum']); 269 | $this->assertIsArray($hydration['aliasMap']); 270 | 271 | // Check values 272 | $this->assertEquals('object', $hydration['type']); 273 | $this->assertEquals(10, $hydration['resultNum']); 274 | $this->assertEquals(['e' => 'TestEntity'], $hydration['aliasMap']); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /tests/Unit/ORM/LoggingArrayHydratorTest.php: -------------------------------------------------------------------------------- 1 | createMock(EntityManagerInterface::class); 19 | $this->hydrator = new LoggingArrayHydrator($entityManager); 20 | } 21 | 22 | public function test_extends_array_hydrator(): void 23 | { 24 | $this->assertInstanceOf(ArrayHydrator::class, $this->hydrator); 25 | } 26 | 27 | public function test_uses_logging_hydrator_trait(): void 28 | { 29 | $reflection = new \ReflectionClass($this->hydrator); 30 | $traits = $reflection->getTraitNames(); 31 | 32 | $this->assertContains( 33 | 'Debesha\DoctrineProfileExtraBundle\ORM\LoggingHydratorTrait', 34 | $traits, 35 | 'LoggingArrayHydrator should use LoggingHydratorTrait' 36 | ); 37 | } 38 | 39 | public function test_class_structure(): void 40 | { 41 | $reflection = new \ReflectionClass($this->hydrator); 42 | 43 | // Check that the class has the expected methods 44 | $this->assertTrue($reflection->hasMethod('hydrateAll')); 45 | 46 | // Check that the method is public 47 | $method = $reflection->getMethod('hydrateAll'); 48 | $this->assertTrue($method->isPublic()); 49 | } 50 | 51 | public function test_inheritance_chain(): void 52 | { 53 | $this->assertInstanceOf(LoggingArrayHydrator::class, $this->hydrator); 54 | $this->assertInstanceOf(ArrayHydrator::class, $this->hydrator); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Unit/ORM/LoggingHydratorTraitTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(trait_exists(LoggingHydratorTrait::class)); 13 | } 14 | 15 | public function test_trait_can_be_used(): void 16 | { 17 | // Test that the trait can be used in a class 18 | $testClass = new class { 19 | use LoggingHydratorTrait; 20 | }; 21 | 22 | $this->assertIsObject($testClass); 23 | $this->assertTrue(method_exists($testClass, 'hydrateAll')); 24 | } 25 | 26 | public function test_trait_reflection(): void 27 | { 28 | $reflection = new \ReflectionClass(LoggingHydratorTrait::class); 29 | 30 | // Check that the trait has the expected method 31 | $this->assertTrue($reflection->hasMethod('hydrateAll')); 32 | 33 | // Check that the method is public 34 | $method = $reflection->getMethod('hydrateAll'); 35 | $this->assertTrue($method->isPublic()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/Unit/ORM/LoggingObjectHydratorTest.php: -------------------------------------------------------------------------------- 1 | createMock(EntityManagerInterface::class); 19 | $this->hydrator = new LoggingObjectHydrator($entityManager); 20 | } 21 | 22 | public function test_extends_object_hydrator(): void 23 | { 24 | $this->assertInstanceOf(ObjectHydrator::class, $this->hydrator); 25 | } 26 | 27 | public function test_uses_logging_hydrator_trait(): void 28 | { 29 | $reflection = new \ReflectionClass($this->hydrator); 30 | $traits = $reflection->getTraitNames(); 31 | 32 | $this->assertContains( 33 | 'Debesha\DoctrineProfileExtraBundle\ORM\LoggingHydratorTrait', 34 | $traits, 35 | 'LoggingObjectHydrator should use LoggingHydratorTrait' 36 | ); 37 | } 38 | 39 | public function test_class_structure(): void 40 | { 41 | $reflection = new \ReflectionClass($this->hydrator); 42 | 43 | // Check that the class has the expected methods 44 | $this->assertTrue($reflection->hasMethod('hydrateAll')); 45 | 46 | // Check that the method is public 47 | $method = $reflection->getMethod('hydrateAll'); 48 | $this->assertTrue($method->isPublic()); 49 | } 50 | 51 | public function test_inheritance_chain(): void 52 | { 53 | $this->assertInstanceOf(LoggingObjectHydrator::class, $this->hydrator); 54 | $this->assertInstanceOf(ObjectHydrator::class, $this->hydrator); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Unit/ORM/LoggingScalarHydratorTest.php: -------------------------------------------------------------------------------- 1 | createMock(EntityManagerInterface::class); 19 | $this->hydrator = new LoggingScalarHydrator($entityManager); 20 | } 21 | 22 | public function test_extends_scalar_hydrator(): void 23 | { 24 | $this->assertInstanceOf(ScalarHydrator::class, $this->hydrator); 25 | } 26 | 27 | public function test_uses_logging_hydrator_trait(): void 28 | { 29 | $reflection = new \ReflectionClass($this->hydrator); 30 | $traits = $reflection->getTraitNames(); 31 | 32 | $this->assertContains( 33 | 'Debesha\DoctrineProfileExtraBundle\ORM\LoggingHydratorTrait', 34 | $traits, 35 | 'LoggingScalarHydrator should use LoggingHydratorTrait' 36 | ); 37 | } 38 | 39 | public function test_class_structure(): void 40 | { 41 | $reflection = new \ReflectionClass($this->hydrator); 42 | 43 | // Check that the class has the expected methods 44 | $this->assertTrue($reflection->hasMethod('hydrateAll')); 45 | 46 | // Check that the method is public 47 | $method = $reflection->getMethod('hydrateAll'); 48 | $this->assertTrue($method->isPublic()); 49 | } 50 | 51 | public function test_inheritance_chain(): void 52 | { 53 | $this->assertInstanceOf(LoggingScalarHydrator::class, $this->hydrator); 54 | $this->assertInstanceOf(ScalarHydrator::class, $this->hydrator); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Unit/ORM/LoggingSimpleObjectHydratorTest.php: -------------------------------------------------------------------------------- 1 | createMock(EntityManagerInterface::class); 19 | $this->hydrator = new LoggingSimpleObjectHydrator($entityManager); 20 | } 21 | 22 | public function test_extends_simple_object_hydrator(): void 23 | { 24 | $this->assertInstanceOf(SimpleObjectHydrator::class, $this->hydrator); 25 | } 26 | 27 | public function test_uses_logging_hydrator_trait(): void 28 | { 29 | $reflection = new \ReflectionClass($this->hydrator); 30 | $traits = $reflection->getTraitNames(); 31 | 32 | $this->assertContains( 33 | 'Debesha\DoctrineProfileExtraBundle\ORM\LoggingHydratorTrait', 34 | $traits, 35 | 'LoggingSimpleObjectHydrator should use LoggingHydratorTrait' 36 | ); 37 | } 38 | 39 | public function test_class_structure(): void 40 | { 41 | $reflection = new \ReflectionClass($this->hydrator); 42 | 43 | // Check that the class has the expected methods 44 | $this->assertTrue($reflection->hasMethod('hydrateAll')); 45 | 46 | // Check that the method is public 47 | $method = $reflection->getMethod('hydrateAll'); 48 | $this->assertTrue($method->isPublic()); 49 | } 50 | 51 | public function test_inheritance_chain(): void 52 | { 53 | $this->assertInstanceOf(LoggingSimpleObjectHydrator::class, $this->hydrator); 54 | $this->assertInstanceOf(SimpleObjectHydrator::class, $this->hydrator); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Unit/ORM/LoggingSingleScalarHydratorTest.php: -------------------------------------------------------------------------------- 1 | createMock(EntityManagerInterface::class); 19 | $this->hydrator = new LoggingSingleScalarHydrator($entityManager); 20 | } 21 | 22 | public function test_extends_single_scalar_hydrator(): void 23 | { 24 | $this->assertInstanceOf(SingleScalarHydrator::class, $this->hydrator); 25 | } 26 | 27 | public function test_uses_logging_hydrator_trait(): void 28 | { 29 | $reflection = new \ReflectionClass($this->hydrator); 30 | $traits = $reflection->getTraitNames(); 31 | 32 | $this->assertContains( 33 | 'Debesha\DoctrineProfileExtraBundle\ORM\LoggingHydratorTrait', 34 | $traits, 35 | 'LoggingSingleScalarHydrator should use LoggingHydratorTrait' 36 | ); 37 | } 38 | 39 | public function test_class_structure(): void 40 | { 41 | $reflection = new \ReflectionClass($this->hydrator); 42 | 43 | // Check that the class has the expected methods 44 | $this->assertTrue($reflection->hasMethod('hydrateAll')); 45 | 46 | // Check that the method is public 47 | $method = $reflection->getMethod('hydrateAll'); 48 | $this->assertTrue($method->isPublic()); 49 | } 50 | 51 | public function test_inheritance_chain(): void 52 | { 53 | $this->assertInstanceOf(LoggingSingleScalarHydrator::class, $this->hydrator); 54 | $this->assertInstanceOf(SingleScalarHydrator::class, $this->hydrator); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/Unit/bootstrap.php: -------------------------------------------------------------------------------- 1 | =1.2). 15 | # https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration 16 | 17 | ###> symfony/framework-bundle ### 18 | APP_ENV=dev 19 | APP_SECRET=abdd36289d7c51b14e9e7bc44cfbf5df 20 | ###< symfony/framework-bundle ### 21 | 22 | ###> doctrine/doctrine-bundle ### 23 | # Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url 24 | # IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml 25 | # 26 | DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db" 27 | # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" 28 | # DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" 29 | #DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8" 30 | ###< doctrine/doctrine-bundle ### 31 | -------------------------------------------------------------------------------- /tests/integration/symfony73/.env.dev: -------------------------------------------------------------------------------- 1 | 2 | ###> symfony/framework-bundle ### 3 | APP_SECRET=cf6345207a17a180807d4012b01ab9f7 4 | ###< symfony/framework-bundle ### 5 | -------------------------------------------------------------------------------- /tests/integration/symfony73/.env.test: -------------------------------------------------------------------------------- 1 | # define your env variables for the test env here 2 | KERNEL_CLASS='App\Kernel' 3 | APP_SECRET='$ecretf0rt3st' 4 | SYMFONY_DEPRECATIONS_HELPER=999999 5 | PANTHER_APP_ENV=panther 6 | PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots 7 | -------------------------------------------------------------------------------- /tests/integration/symfony73/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ###> symfony/framework-bundle ### 3 | /.env.local 4 | /.env.local.php 5 | /.env.*.local 6 | /config/secrets/prod/prod.decrypt.private.php 7 | /public/bundles/ 8 | /var/ 9 | /vendor/ 10 | ###< symfony/framework-bundle ### 11 | 12 | ###> symfony/phpunit-bridge ### 13 | .phpunit.result.cache 14 | /phpunit.xml 15 | ###< symfony/phpunit-bridge ### 16 | 17 | ###> phpunit/phpunit ### 18 | /phpunit.xml 19 | .phpunit.result.cache 20 | ###< phpunit/phpunit ### 21 | -------------------------------------------------------------------------------- /tests/integration/symfony73/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | =8.0", 8 | "ext-ctype": "*", 9 | "ext-iconv": "*", 10 | "debesha/doctrine-hydration-profiler-bundle": "*@dev", 11 | "doctrine/dbal": "^3", 12 | "doctrine/doctrine-bundle": "^2.14", 13 | "doctrine/doctrine-fixtures-bundle": "^4.1", 14 | "doctrine/doctrine-migrations-bundle": "^3.4", 15 | "doctrine/orm": "^2", 16 | "phpunit/phpunit": "^11", 17 | "symfony/browser-kit": "7.3.*", 18 | "symfony/console": "7.3.*", 19 | "symfony/css-selector": "7.3.*", 20 | "symfony/dotenv": "7.3.*", 21 | "symfony/flex": "^2", 22 | "symfony/framework-bundle": "7.3.*", 23 | "symfony/phpunit-bridge": "7.3.*", 24 | "symfony/runtime": "7.3.*", 25 | "symfony/serializer": "7.3.*", 26 | "symfony/stopwatch": "7.3.*", 27 | "symfony/web-profiler-bundle": "7.3.*", 28 | "symfony/yaml": "7.3.*" 29 | }, 30 | "config": { 31 | "allow-plugins": { 32 | "php-http/discovery": true, 33 | "symfony/flex": true, 34 | "symfony/runtime": true 35 | }, 36 | "sort-packages": true 37 | }, 38 | "autoload": { 39 | "psr-4": { 40 | "App\\": "src/" 41 | } 42 | }, 43 | "autoload-dev": { 44 | "psr-4": { 45 | "App\\Tests\\": "tests/" 46 | } 47 | }, 48 | "repositories": [ 49 | { 50 | "type": "path", 51 | "url": "../../../" 52 | } 53 | ], 54 | "replace": { 55 | "symfony/polyfill-ctype": "*", 56 | "symfony/polyfill-iconv": "*", 57 | "symfony/polyfill-php72": "*", 58 | "symfony/polyfill-php73": "*", 59 | "symfony/polyfill-php74": "*", 60 | "symfony/polyfill-php80": "*" 61 | }, 62 | "scripts": { 63 | "auto-scripts": { 64 | "cache:clear": "symfony-cmd", 65 | "assets:install %PUBLIC_DIR%": "symfony-cmd" 66 | }, 67 | "post-install-cmd": [ 68 | "@auto-scripts", 69 | "bin/console --env=test doctrine:migrations:migrate --no-interaction", 70 | "bin/console --env=test doctrine:fixtures:load --no-interaction" 71 | ], 72 | "post-update-cmd": [ 73 | "@auto-scripts", 74 | "bin/console --env=test doctrine:migrations:migrate --no-interaction", 75 | "bin/console --env=test doctrine:fixtures:load --no-interaction" 76 | ] 77 | }, 78 | "conflict": { 79 | "symfony/symfony": "*" 80 | }, 81 | "extra": { 82 | "symfony": { 83 | "allow-contrib": false, 84 | "require": "7.3.*" 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/integration/symfony73/composer-orm3.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "project", 3 | "license": "proprietary", 4 | "minimum-stability": "stable", 5 | "prefer-stable": true, 6 | "require": { 7 | "php": ">=8.0", 8 | "ext-ctype": "*", 9 | "ext-iconv": "*", 10 | "debesha/doctrine-hydration-profiler-bundle": "*@dev", 11 | "doctrine/dbal": "^3", 12 | "doctrine/doctrine-bundle": "^2.14", 13 | "doctrine/doctrine-fixtures-bundle": "^4.1", 14 | "doctrine/doctrine-migrations-bundle": "^3.4", 15 | "doctrine/orm": "^3", 16 | "phpunit/phpunit": "^11", 17 | "symfony/browser-kit": "7.3.*", 18 | "symfony/console": "7.3.*", 19 | "symfony/css-selector": "7.3.*", 20 | "symfony/dotenv": "7.3.*", 21 | "symfony/flex": "^2", 22 | "symfony/framework-bundle": "7.3.*", 23 | "symfony/phpunit-bridge": "7.3.*", 24 | "symfony/runtime": "7.3.*", 25 | "symfony/serializer": "7.3.*", 26 | "symfony/stopwatch": "7.3.*", 27 | "symfony/web-profiler-bundle": "7.3.*", 28 | "symfony/yaml": "7.3.*" 29 | }, 30 | "config": { 31 | "allow-plugins": { 32 | "php-http/discovery": true, 33 | "symfony/flex": true, 34 | "symfony/runtime": true 35 | }, 36 | "sort-packages": true 37 | }, 38 | "autoload": { 39 | "psr-4": { 40 | "App\\": "src/" 41 | } 42 | }, 43 | "autoload-dev": { 44 | "psr-4": { 45 | "App\\Tests\\": "tests/" 46 | } 47 | }, 48 | "repositories": [ 49 | { 50 | "type": "path", 51 | "url": "../../../" 52 | } 53 | ], 54 | "replace": { 55 | "symfony/polyfill-ctype": "*", 56 | "symfony/polyfill-iconv": "*", 57 | "symfony/polyfill-php72": "*", 58 | "symfony/polyfill-php73": "*", 59 | "symfony/polyfill-php74": "*", 60 | "symfony/polyfill-php80": "*" 61 | }, 62 | "scripts": { 63 | "auto-scripts": { 64 | "cache:clear": "symfony-cmd", 65 | "assets:install %PUBLIC_DIR%": "symfony-cmd" 66 | }, 67 | "post-install-cmd": [ 68 | "@auto-scripts", 69 | "bin/console --env=test doctrine:migrations:migrate --no-interaction", 70 | "bin/console --env=test doctrine:fixtures:load --no-interaction" 71 | ], 72 | "post-update-cmd": [ 73 | "@auto-scripts", 74 | "bin/console --env=test doctrine:migrations:migrate --no-interaction", 75 | "bin/console --env=test doctrine:fixtures:load --no-interaction" 76 | ] 77 | }, 78 | "conflict": { 79 | "symfony/symfony": "*" 80 | }, 81 | "extra": { 82 | "symfony": { 83 | "allow-contrib": false, 84 | "require": "7.3.*" 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/integration/symfony73/composer.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /tests/integration/symfony73/config/bundles.php: -------------------------------------------------------------------------------- 1 | ['all' => true], 5 | Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], 6 | Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], 7 | Debesha\DoctrineProfileExtraBundle\DebeshaDoctrineProfileExtraBundle::class => ['all' => true], 8 | Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], 9 | Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], 10 | Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], 11 | ]; 12 | -------------------------------------------------------------------------------- /tests/integration/symfony73/config/packages/cache.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | cache: 3 | # Unique name of your app: used to compute stable namespaces for cache keys. 4 | #prefix_seed: your_vendor_name/app_name 5 | 6 | # The "app" cache stores to the filesystem by default. 7 | # The data in this cache should persist between deploys. 8 | # Other options include: 9 | 10 | # Redis 11 | #app: cache.adapter.redis 12 | #default_redis_provider: redis://localhost 13 | 14 | # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) 15 | #app: cache.adapter.apcu 16 | 17 | # Namespaced pools use the above "app" backend by default 18 | pools: 19 | app.cache.mock: 20 | public: true 21 | adapter: cache.adapter.array 22 | -------------------------------------------------------------------------------- /tests/integration/symfony73/config/packages/doctrine.yaml: -------------------------------------------------------------------------------- 1 | doctrine: 2 | dbal: 3 | url: '%env(resolve:DATABASE_URL)%' 4 | 5 | # IMPORTANT: You MUST configure your server version, 6 | # either here or in the DATABASE_URL env var (see .env file) 7 | #server_version: '16' 8 | 9 | profiling_collect_backtrace: '%kernel.debug%' 10 | use_savepoints: true 11 | orm: 12 | auto_generate_proxy_classes: true 13 | enable_lazy_ghost_objects: true 14 | report_fields_where_declared: true 15 | validate_xml_mapping: true 16 | naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware 17 | identity_generation_preferences: 18 | Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity 19 | auto_mapping: true 20 | mappings: 21 | App: 22 | type: attribute 23 | is_bundle: false 24 | dir: '%kernel.project_dir%/src/Entity' 25 | prefix: 'App\Entity' 26 | alias: App 27 | controller_resolver: 28 | auto_mapping: false 29 | 30 | when@test: 31 | doctrine: 32 | dbal: 33 | # "TEST_TOKEN" is typically set by ParaTest 34 | dbname_suffix: '_test%env(default::TEST_TOKEN)%' 35 | 36 | when@prod: 37 | doctrine: 38 | orm: 39 | auto_generate_proxy_classes: false 40 | proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' 41 | query_cache_driver: 42 | type: pool 43 | pool: doctrine.system_cache_pool 44 | result_cache_driver: 45 | type: pool 46 | pool: doctrine.result_cache_pool 47 | 48 | framework: 49 | cache: 50 | pools: 51 | doctrine.result_cache_pool: 52 | adapter: cache.app 53 | doctrine.system_cache_pool: 54 | adapter: cache.system 55 | -------------------------------------------------------------------------------- /tests/integration/symfony73/config/packages/doctrine_migrations.yaml: -------------------------------------------------------------------------------- 1 | doctrine_migrations: 2 | migrations_paths: 3 | # namespace is arbitrary but should be different from App\Migrations 4 | # as migrations classes should NOT be autoloaded 5 | 'DoctrineMigrations': '%kernel.project_dir%/migrations' 6 | enable_profiler: false 7 | -------------------------------------------------------------------------------- /tests/integration/symfony73/config/packages/framework.yaml: -------------------------------------------------------------------------------- 1 | # see https://symfony.com/doc/current/reference/configuration/framework.html 2 | framework: 3 | secret: '%env(APP_SECRET)%' 4 | #csrf_protection: true 5 | http_method_override: false 6 | handle_all_throwables: true 7 | set_content_language_from_locale: true 8 | 9 | # Enables session support. Note that the session will ONLY be started if you read or write from it. 10 | # Remove or comment this section to explicitly disable session support. 11 | session: 12 | handler_id: null 13 | cookie_secure: auto 14 | cookie_samesite: lax 15 | storage_factory_id: session.storage.factory.native 16 | 17 | #esi: true 18 | #fragments: true 19 | php_errors: 20 | log: true 21 | 22 | when@test: 23 | framework: 24 | test: true 25 | session: 26 | storage_factory_id: session.storage.factory.mock_file 27 | -------------------------------------------------------------------------------- /tests/integration/symfony73/config/packages/routing.yaml: -------------------------------------------------------------------------------- 1 | framework: 2 | router: 3 | utf8: true 4 | 5 | # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. 6 | # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands 7 | #default_uri: http://localhost 8 | 9 | when@prod: 10 | framework: 11 | router: 12 | strict_requirements: null 13 | -------------------------------------------------------------------------------- /tests/integration/symfony73/config/packages/twig.yaml: -------------------------------------------------------------------------------- 1 | twig: 2 | file_name_pattern: '*.twig' 3 | 4 | when@test: 5 | twig: 6 | strict_variables: true 7 | -------------------------------------------------------------------------------- /tests/integration/symfony73/config/packages/web_profiler.yaml: -------------------------------------------------------------------------------- 1 | when@dev: 2 | web_profiler: 3 | toolbar: true 4 | 5 | framework: 6 | profiler: 7 | collect_serializer_data: true 8 | 9 | when@test: 10 | framework: 11 | profiler: { collect: false } 12 | -------------------------------------------------------------------------------- /tests/integration/symfony73/config/preload.php: -------------------------------------------------------------------------------- 1 | addSql(<<<'SQL' 20 | CREATE TABLE test_entity (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, foo VARCHAR(255) NOT NULL) 21 | SQL); 22 | } 23 | 24 | public function down(Schema $schema): void 25 | { 26 | $this->addSql(<<<'SQL' 27 | DROP TABLE test_entity 28 | SQL); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/integration/symfony73/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | tests 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/integration/symfony73/public/index.php: -------------------------------------------------------------------------------- 1 | entityManager->find(TestEntity::class, 1); 27 | $entity2 = $this->entityManager->find(TestEntity::class, 2); 28 | $entity3 = $this->entityManager->find(TestEntity::class, 3); 29 | 30 | return new JsonResponse([$entity1->foo, $entity2->foo, $entity3->foo]); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/integration/symfony73/src/DataFixtures/AppFixtures.php: -------------------------------------------------------------------------------- 1 | persist($entity1); 15 | $entity2 = new TestEntity('baz'); 16 | $manager->persist($entity2); 17 | $entity3 = new TestEntity('qux'); 18 | $manager->persist($entity3); 19 | 20 | $manager->flush(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/integration/symfony73/src/Entity/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/debesha/DoctrineProfileExtraBundle/de7e64de876df2fb1f404bf5eb52f7be78873097/tests/integration/symfony73/src/Entity/.gitignore -------------------------------------------------------------------------------- /tests/integration/symfony73/src/Entity/TestEntity.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {% block title %}Welcome!{% endblock %} 6 | 7 | {% block stylesheets %} 8 | {% endblock %} 9 | 10 | {% block javascripts %} 11 | {% endblock %} 12 | 13 | 14 | {% block body %}{% endblock %} 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/integration/symfony73/tests/TestControllerTest.php: -------------------------------------------------------------------------------- 1 | enableProfiler(); 16 | $client->request('GET', '/test'); 17 | 18 | $this->assertResponseIsSuccessful(); 19 | 20 | $dataCollector = self::getContainer()->get('debesha.doctrine_extra_profiler.data_collector.public'); 21 | \assert($dataCollector instanceof HydrationDataCollector); 22 | 23 | self::assertEquals(3, $dataCollector->getHydrationsCount()); 24 | } 25 | } -------------------------------------------------------------------------------- /tests/integration/symfony73/tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | bootEnv(dirname(__DIR__) . '/.env'); 13 | } 14 | 15 | if ($_SERVER['APP_DEBUG']) { 16 | umask(0000); 17 | } 18 | --------------------------------------------------------------------------------