diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 436d5964..1ccf2ae4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,7 +16,7 @@ jobs: name: PHPStan steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: PHPStan uses: docker://oskarstark/phpstan-ga env: @@ -29,7 +29,7 @@ jobs: name: PHP-CS-Fixer steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Fix CS uses: docker://oskarstark/php-cs-fixer-ga tests: @@ -45,15 +45,17 @@ jobs: php: '8.1' - description: '8.2' php: '8.2' + - description: '8.3' + php: '8.3' - description: 'Dev deps' - php: '8.2' + php: '8.3' dev: true name: PHP ${{ matrix.php }} tests (${{ matrix.description }}) steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.composer/cache/files key: ${{ matrix.php }}-${{ matrix.symfony }}-${{ matrix.composer_option }} diff --git a/composer.json b/composer.json index 7a52fa94..175b6501 100644 --- a/composer.json +++ b/composer.json @@ -27,17 +27,18 @@ }, "require-dev": { "ext-pdo_sqlite": "*", - "doctrine/mongodb-odm": "^2.4", - "doctrine/orm": "^2.12", - "doctrine/phpcr-odm": "^1.6", - "jackalope/jackalope-doctrine-dbal": "^1.8", - "phpunit/phpunit": "^9.5", + "doctrine/dbal": "^3.8 || ^4.0", + "doctrine/mongodb-odm": "^2.5.5", + "doctrine/orm": "^2.13 || ^3.0", + "doctrine/phpcr-odm": "^1.8 || ^2.0", + "jackalope/jackalope-doctrine-dbal": "^1.12 || ^2.0", + "phpunit/phpunit": "^9.6", "propel/propel1": "^1.7", "ruflin/elastica": "^7.0", "solarium/solarium": "^6.0", - "symfony/http-foundation": "^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/http-foundation": "^5.4.38 || ^6.4.4 || ^7.0", + "symfony/http-kernel": "^5.4.38 || ^6.4.4 || ^7.0", + "symfony/property-access": "^5.4.38 || ^6.4.4 || ^7.0" }, "suggest": { "doctrine/common": "to allow usage pagination with Doctrine ArrayCollection", @@ -51,7 +52,7 @@ "symfony/property-access": "to allow sorting arrays" }, "conflict": { - "doctrine/dbal": "<3.1" + "doctrine/dbal": "<3.8" }, "extra": { "branch-alias": { diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 5f150ab0..2a408332 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,5 +1,10 @@ parameters: ignoreErrors: + - + message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Query\\\\QueryBuilder\\:\\:resetQueryParts\\(\\)\\.$#" + count: 1 + path: src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/DBALQueryBuilderSubscriber.php + - message: "#^Method Knp\\\\Component\\\\Pager\\\\Event\\\\Subscriber\\\\Sortable\\\\ArraySubscriber\\:\\:sortFunction\\(\\) has parameter \\$object1 with no value type specified in iterable type array\\.$#" count: 1 @@ -29,4 +34,3 @@ parameters: message: "#^Interface Knp\\\\Component\\\\Pager\\\\Pagination\\\\PaginationInterface extends generic interface ArrayAccess but does not specify its types\\: TKey, TValue$#" count: 1 path: src/Knp/Component/Pager/Pagination/PaginationInterface.php - diff --git a/src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/DBALQueryBuilderSubscriber.php b/src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/DBALQueryBuilderSubscriber.php index 971ce518..ab0460b8 100644 --- a/src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/DBALQueryBuilderSubscriber.php +++ b/src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/DBALQueryBuilderSubscriber.php @@ -22,17 +22,17 @@ public function items(ItemsEvent $event): void $qb = clone $target; //reset count orderBy since it can break query and slow it down - $qb - ->resetQueryPart('orderBy') - ; - + if (method_exists($qb, 'resetOrderBy')) { + $qb->resetOrderBy(); + } else { + $qb->resetQueryParts(['orderBy']); + } + // get the query $sql = $qb->getSQL(); - + $qb - ->resetQueryParts() ->select('count(*) as cnt') - ->from('(' . $sql . ')', 'dbal_count_tbl') ; $compat = $qb->executeQuery(); diff --git a/src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/ODM/PHPCR/QuerySubscriber.php b/src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/ODM/PHPCR/QuerySubscriber.php index eed7a8d4..2b8f639f 100644 --- a/src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/ODM/PHPCR/QuerySubscriber.php +++ b/src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/ODM/PHPCR/QuerySubscriber.php @@ -18,7 +18,7 @@ public function items(ItemsEvent $event): void } $queryCount = clone $event->target; - $event->count = $queryCount->execute(null, Query::HYDRATE_PHPCR)->getRows()->count(); + $event->count = iterator_count($queryCount->execute(null, Query::HYDRATE_PHPCR)->getRows()); $query = $event->target; $query->setMaxResults($event->getLimit()); diff --git a/tests/Test/Fixture/Document/Article.php b/tests/Test/Fixture/Document/Article.php index d97a8a3f..a8988801 100644 --- a/tests/Test/Fixture/Document/Article.php +++ b/tests/Test/Fixture/Document/Article.php @@ -7,26 +7,31 @@ /** * @ODM\Document */ +#[ODM\Document] final class Article { /** * @ODM\Id */ + #[ODM\Id] private $id; /** * @ODM\Field(type="string") */ + #[ODM\Field(type: "string")] private ?string $title = null; /** * @ODM\Field(type="bool", name="status") */ + #[ODM\Field(type: "bool")] private bool $status = false; /** * @ODM\Field(type="date", name="created_at") */ + #[ODM\Field(type: "date", name: "created_at")] private ?\DateTime $createdAt = null; public function getId() diff --git a/tests/Test/Fixture/Document/Image.php b/tests/Test/Fixture/Document/Image.php index 5e6eec6b..51d8a13b 100644 --- a/tests/Test/Fixture/Document/Image.php +++ b/tests/Test/Fixture/Document/Image.php @@ -2,27 +2,32 @@ namespace Test\Fixture\Document; +use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCRAnnotations; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; /** * @ODM\Document */ +#[ODM\Document] final class Image { /** * @ODM\Id */ + #[ODM\Id] private $id; /** * @ODM\Field */ + #[ODM\Field] private ?string $title = null; /** * @ODM\File * @var int|string */ + #[ODM\File] private $file; /** diff --git a/tests/Test/Fixture/Document/PHPCR/Article.php b/tests/Test/Fixture/Document/PHPCR/Article.php index 5868867c..65c1a216 100644 --- a/tests/Test/Fixture/Document/PHPCR/Article.php +++ b/tests/Test/Fixture/Document/PHPCR/Article.php @@ -2,26 +2,31 @@ namespace Test\Fixture\Document\PHPCR; -use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCR; +use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCRAnnotations; +use Doctrine\ODM\PHPCR\Mapping\Attributes as PHPCR; /** - * @PHPCR\Document + * @PHPCRAnnotations\Document */ +#[PHPCR\Document] final class Article { /** - * @PHPCR\Id + * @PHPCRAnnotations\Id */ + #[PHPCR\Id] private $id; /** - * @PHPCR\ParentDocument + * @PHPCRAnnotations\ParentDocument */ + #[PHPCR\ParentDocument] private $parent; /** - * @PHPCR\Field(type="string") + * @PHPCRAnnotations\Field(type="string") */ + #[PHPCR\Field(type: "string")] private $title; public function getId() diff --git a/tests/Test/Fixture/Entity/Article.php b/tests/Test/Fixture/Entity/Article.php index c95e95cb..abfb1cb8 100644 --- a/tests/Test/Fixture/Entity/Article.php +++ b/tests/Test/Fixture/Entity/Article.php @@ -7,6 +7,7 @@ /** * @ORM\Entity */ +#[ORM\Entity] class Article { /** @@ -14,16 +15,19 @@ class Article * @ORM\GeneratedValue * @ORM\Column(type="integer") */ + #[ORM\Id, ORM\GeneratedValue, ORM\Column(type: "integer")] private $id; /** * @ORM\Column(length=64) */ + #[ORM\Column(length: 64)] private ?string $title = null; /** * @ORM\Column(type="boolean") */ + #[ORM\Column(type: "boolean")] private bool $enabled = true; public function getId() diff --git a/tests/Test/Fixture/Entity/Composite.php b/tests/Test/Fixture/Entity/Composite.php index 4cd8aaeb..cce66c1f 100644 --- a/tests/Test/Fixture/Entity/Composite.php +++ b/tests/Test/Fixture/Entity/Composite.php @@ -7,23 +7,27 @@ /** * @ORM\Entity */ +#[ORM\Entity] class Composite { /** * @ORM\Id * @ORM\Column(type="integer") */ + #[ORM\Id, ORM\Column(type: "integer")] private $id; /** * @ORM\Column(length=64) */ + #[ORM\Column(length: 64)] private ?string $title = null; /** * @ORM\Id * @ORM\Column(type="string") */ + #[ORM\Id, ORM\Column(type: "string")] private ?string $uid = null; public function setUid(?string $uid): void diff --git a/tests/Test/Fixture/Entity/Shop/Product.php b/tests/Test/Fixture/Entity/Shop/Product.php index 6abf4214..a3facc15 100644 --- a/tests/Test/Fixture/Entity/Shop/Product.php +++ b/tests/Test/Fixture/Entity/Shop/Product.php @@ -9,6 +9,7 @@ /** * @ORM\Entity */ +#[ORM\Entity] class Product { /** @@ -16,31 +17,37 @@ class Product * @ORM\GeneratedValue * @ORM\Column(type="integer") */ + #[ORM\Id, ORM\GeneratedValue, ORM\Column(type: "integer")] private $id; /** * @ORM\Column(length=64) */ + #[ORM\Column(length: 64)] private ?string $title = null; /** * @ORM\Column(length=255, nullable=true) */ + #[ORM\Column(length: 255, nullable: true)] private ?string $description = null; /** * @ORM\Column(type="float", nullable=false) */ + #[ORM\Column(type: "float", nullable: true)] private ?float $price = null; /** * @ORM\ManyToMany(targetEntity="Tag") */ + #[ORM\ManyToMany(targetEntity: "Tag")] private Collection $tags; /** * @ORM\Column(type="integer") */ + #[ORM\Column(type: "integer")] private int $numTags = 0; public function __construct() diff --git a/tests/Test/Fixture/Entity/Shop/Tag.php b/tests/Test/Fixture/Entity/Shop/Tag.php index ff03718f..8655c9ad 100644 --- a/tests/Test/Fixture/Entity/Shop/Tag.php +++ b/tests/Test/Fixture/Entity/Shop/Tag.php @@ -7,6 +7,7 @@ /** * @ORM\Entity */ +#[ORM\Entity] class Tag { /** @@ -14,11 +15,13 @@ class Tag * @ORM\GeneratedValue * @ORM\Column(type="integer") */ + #[ORM\Id, ORM\GeneratedValue, ORM\Column(type: "integer")] private $id; /** * @ORM\Column(length=64) */ + #[ORM\Column(length: 64)] private ?string $name = null; public function getId() diff --git a/tests/Test/Pager/Subscriber/Filtration/Doctrine/ORM/QueryTest.php b/tests/Test/Pager/Subscriber/Filtration/Doctrine/ORM/QueryTest.php index e5cdf63e..726175a1 100644 --- a/tests/Test/Pager/Subscriber/Filtration/Doctrine/ORM/QueryTest.php +++ b/tests/Test/Pager/Subscriber/Filtration/Doctrine/ORM/QueryTest.php @@ -2,6 +2,7 @@ namespace Test\Pager\Subscriber\Filtration\Doctrine\ORM; +use Doctrine\DBAL\DriverManager; use Doctrine\ORM\EntityManagerInterface; use Knp\Component\Pager\ArgumentAccess\RequestArgumentAccess; use Knp\Component\Pager\Event\Subscriber\Filtration\Doctrine\ORM\Query\WhereWalker; @@ -39,8 +40,9 @@ public function shouldHandleApcQueryCache(): void 'memory' => true, ]; - $em = \Doctrine\ORM\EntityManager::create($conn, $config); - $schema = \array_map(static function ($class) use ($em) { + $connection = DriverManager::getConnection($conn, $config); + $em = new \Doctrine\ORM\EntityManager($connection, $config); + $schema = \array_map(static function (string $class) use ($em) { return $em->getClassMetadata($class); }, $this->getUsedEntityFixtures()); @@ -756,7 +758,7 @@ public function shouldFilterCaseInsensitiveWhenAsked(): void } /** - * @return Article[] + * @return string[] */ protected function getUsedEntityFixtures(): array { diff --git a/tests/Test/Pager/Subscriber/Sortable/Doctrine/ORM/QueryTest.php b/tests/Test/Pager/Subscriber/Sortable/Doctrine/ORM/QueryTest.php index 4e687e67..b94345e0 100644 --- a/tests/Test/Pager/Subscriber/Sortable/Doctrine/ORM/QueryTest.php +++ b/tests/Test/Pager/Subscriber/Sortable/Doctrine/ORM/QueryTest.php @@ -2,6 +2,7 @@ namespace Test\Pager\Subscriber\Sortable\Doctrine\ORM; +use Doctrine\DBAL\DriverManager; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Tools\SchemaTool; use Knp\Component\Pager\ArgumentAccess\RequestArgumentAccess; @@ -38,7 +39,8 @@ public function shouldHandleApcQueryCache(): void 'memory' => true, ]; - $em = EntityManager::create($conn, $config); + $connection = DriverManager::getConnection($conn, $config); + $em = new EntityManager($connection, $config); $schema = \array_map(static function ($class) use ($em) { return $em->getClassMetadata($class); }, $this->getUsedEntityFixtures()); diff --git a/tests/Test/Tool/BaseTestCaseMongoODM.php b/tests/Test/Tool/BaseTestCaseMongoODM.php index b999950e..055bdc8d 100644 --- a/tests/Test/Tool/BaseTestCaseMongoODM.php +++ b/tests/Test/Tool/BaseTestCaseMongoODM.php @@ -8,6 +8,7 @@ use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory; use Doctrine\ODM\MongoDB\Mapping\Driver\AnnotationDriver; +use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver; /** * Base test case contains common mock objects @@ -69,10 +70,16 @@ protected function getMockMappedDocumentManager(EventManager $evm = null): Docum /** * Creates default mapping driver + * + * @return AnnotationDriver|AttributeDriver */ - protected function getMetadataDriverImplementation(): AnnotationDriver + protected function getMetadataDriverImplementation() { - return new AnnotationDriver($_ENV['annotation_reader']); + if (null !== $_ENV['annotation_reader']) { + return new AnnotationDriver($_ENV['annotation_reader']); + } + + return new AttributeDriver(); } /** @@ -107,7 +114,7 @@ private function getMockAnnotatedConfig() ->method('getHydratorNamespace') ->willReturn('Hydrator'); - $config->expects($this->any()) + $config ->method('getDefaultDB') ->willReturn('knp_pager_tests'); @@ -123,18 +130,17 @@ private function getMockAnnotatedConfig() ->method('getClassMetadataFactoryName') ->willReturn(ClassMetadataFactory::class); - $config->expects($this->any()) + $config ->method('getMongoCmd') ->willReturn('$'); $config - ->expects($this->any()) ->method('getDefaultCommitOptions') ->willReturn(['safe' => true]) ; $mappingDriver = $this->getMetadataDriverImplementation(); - $config->expects($this->any()) + $config ->method('getMetadataDriverImpl') ->willReturn($mappingDriver); diff --git a/tests/Test/Tool/BaseTestCaseORM.php b/tests/Test/Tool/BaseTestCaseORM.php index 12fbdf3d..13a89979 100644 --- a/tests/Test/Tool/BaseTestCaseORM.php +++ b/tests/Test/Tool/BaseTestCaseORM.php @@ -5,15 +5,20 @@ use Doctrine\Common\EventManager; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver; +use Doctrine\DBAL\DriverManager; +use Doctrine\DBAL\Logging\Middleware; use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Mapping\DefaultNamingStrategy; use Doctrine\ORM\Mapping\DefaultQuoteStrategy; use Doctrine\ORM\Mapping\Driver\AnnotationDriver; +use Doctrine\ORM\Mapping\Driver\AttributeDriver; use Doctrine\ORM\Tools\SchemaTool; +use Doctrine\Persistence\Mapping\Driver\MappingDriver; /** * Base test case contains common mock objects @@ -34,6 +39,7 @@ abstract class BaseTestCaseORM extends BaseTestCase protected function setUp(): void { + $this->queryAnalyzer = new QueryAnalyzer(); } /** @@ -41,7 +47,7 @@ protected function setUp(): void * annotation mapping driver and pdo_sqlite * database in memory */ - protected function getMockSqliteEntityManager(EventManager $evm = null): EntityManager + protected function getMockSqliteEntityManager(?EventManager $evm = null): EntityManager { $conn = [ 'driver' => 'pdo_sqlite', @@ -49,27 +55,28 @@ protected function getMockSqliteEntityManager(EventManager $evm = null): EntityM ]; $config = $this->getMockAnnotatedConfig(); - $em = EntityManager::create($conn, $config, $evm ?: $this->getEventManager()); + $connection = DriverManager::getConnection($conn, $config); - $schema = \array_map(static function ($class) use ($em) { - return $em->getClassMetadata($class); - }, $this->getUsedEntityFixtures()); + $em = new EntityManager($connection, $config, $evm ?: $this->getEventManager()); + + $schema = \array_map(static fn (string $class): ClassMetadata => $em->getClassMetadata($class), $this->getUsedEntityFixtures()); $schemaTool = new SchemaTool($em); $schemaTool->dropSchema([]); $schemaTool->createSchema($schema); + return $this->em = $em; } /** * EntityManager mock object together with - * annotation mapping driver and custom - * connection + * annotation mapping driver and custom connection */ - protected function getMockCustomEntityManager(array $conn, EventManager $evm = null): EntityManager + protected function getMockCustomEntityManager(array $conn, ?EventManager $evm = null): EntityManager { $config = $this->getMockAnnotatedConfig(); - $em = EntityManager::create($conn, $config, $evm ?: $this->getEventManager()); + $connection = DriverManager::getConnection($conn, $config); + $em = new EntityManager($connection, $config, $evm ?: $this->getEventManager()); $schema = \array_map(static function ($class) use ($em) { return $em->getClassMetadata($class); @@ -78,6 +85,7 @@ protected function getMockCustomEntityManager(array $conn, EventManager $evm = n $schemaTool = new SchemaTool($em); $schemaTool->dropSchema([]); $schemaTool->createSchema($schema); + return $this->em = $em; } @@ -85,7 +93,7 @@ protected function getMockCustomEntityManager(array $conn, EventManager $evm = n * EntityManager mock object with * annotation mapping driver */ - protected function getMockMappedEntityManager(EventManager $evm = null): EntityManager + protected function getMockMappedEntityManager(?EventManager $evm = null): EntityManager { $driver = $this->createMock(Driver::class); $driver->expects($this->once()) @@ -101,7 +109,7 @@ protected function getMockMappedEntityManager(EventManager $evm = null): EntityM ->willReturn($evm ?: $this->getEventManager()); $config = $this->getMockAnnotatedConfig(); - $this->em = EntityManager::create($connection, $config); + $this->em = new EntityManager($connection, $config); return $this->em; } @@ -116,11 +124,8 @@ protected function startQueryLog(): void if (null === $this->em) { throw new \RuntimeException('EntityManager and database platform must be initialized'); } - $this->queryAnalyzer = new QueryAnalyzer($this->em->getConnection()->getDatabasePlatform()); - $this->em - ->getConfiguration() - ->method('getSQLLogger') - ->willReturn($this->queryAnalyzer); + + $this->queryAnalyzer->enable(); } /** @@ -155,13 +160,19 @@ protected function stopQueryLog(bool $dumpOnlySql = false, bool $writeToLog = fa /** * Creates default mapping driver */ - protected function getMetadataDriverImplementation(): AnnotationDriver + protected function getMetadataDriverImplementation(): MappingDriver { - return new AnnotationDriver($_ENV['annotation_reader']); + if (class_exists(AnnotationDriver::class)) { + return new AnnotationDriver($_ENV['annotation_reader']); + } + + return new AttributeDriver([]); } /** * Get a list of used fixture classes + * + * @return array */ abstract protected function getUsedEntityFixtures(): array; @@ -176,7 +187,7 @@ private function getEventManager(): EventManager /** * Get annotation mapping configuration * - * @return Configuration|\PHPUnit\Framework\MockObject\MockObject + * @return Configuration&\PHPUnit\Framework\MockObject\MockObject */ private function getMockAnnotatedConfig() { @@ -196,7 +207,15 @@ private function getMockAnnotatedConfig() $config ->expects($this->once()) ->method('getAutoGenerateProxyClasses') - ->willReturn(true) + ->willReturn(1) + ; + + $metadata = $this->createMock(ClassMetadata::class); + $factory = $this->createMock(ClassMetadataFactory::class); + + $factory + ->method('getMetadataFor') + ->willReturn($metadata) ; $config @@ -208,41 +227,39 @@ private function getMockAnnotatedConfig() $mappingDriver = $this->getMetadataDriverImplementation(); $config - ->expects($this->any()) ->method('getMetadataDriverImpl') ->willReturn($mappingDriver) ; $config - ->expects($this->any()) ->method('getDefaultRepositoryClassName') ->willReturn(EntityRepository::class) ; $config - ->expects($this->any()) ->method('getQuoteStrategy') ->willReturn(new DefaultQuoteStrategy()) ; $config - ->expects($this->any()) ->method('getNamingStrategy') ->willReturn(new DefaultNamingStrategy()) ; $config - ->expects($this->any()) ->method('getCustomHydrationMode') ->willReturn('Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query\AsIsHydrator') ; $config - ->expects($this->any()) ->method('getDefaultQueryHints') ->willReturn([]) ; + $config + ->method('getMiddlewares') + ->willReturn([new Middleware($this->queryAnalyzer)]); + return $config; } } diff --git a/tests/Test/Tool/BaseTestCasePHPCRODM.php b/tests/Test/Tool/BaseTestCasePHPCRODM.php index a88760c8..52b15e23 100644 --- a/tests/Test/Tool/BaseTestCasePHPCRODM.php +++ b/tests/Test/Tool/BaseTestCasePHPCRODM.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\DriverManager; use Doctrine\ODM\PHPCR\DocumentManager; use Doctrine\ODM\PHPCR\Mapping\Driver\AnnotationDriver; +use Doctrine\ODM\PHPCR\Mapping\Driver\AttributeDriver; use Doctrine\ODM\PHPCR\Query\Query; use Jackalope\RepositoryFactoryDoctrineDBAL; use Jackalope\Transport\DoctrineDBAL\RepositorySchema; @@ -42,9 +43,12 @@ protected function getMockDocumentManager(EventManager $evm = null): DocumentMan return $this->dm; } - protected function getMetadataDriverImplementation(): AnnotationDriver + protected function getMetadataDriverImplementation(): object { - return new AnnotationDriver($_ENV['annotation_reader']); + return class_exists('AnnotationDriver') ? + new AnnotationDriver($_ENV['annotation_reader']) : + new AttributeDriver([]) + ; } private function getSession() diff --git a/tests/Test/Tool/QueryAnalyzer.php b/tests/Test/Tool/QueryAnalyzer.php index d59fb1e7..e2544714 100644 --- a/tests/Test/Tool/QueryAnalyzer.php +++ b/tests/Test/Tool/QueryAnalyzer.php @@ -2,9 +2,7 @@ namespace Test\Tool; -use Doctrine\DBAL\Logging\SQLLogger; -use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Types\Type; +use Psr\Log\AbstractLogger; /** * @author Gediminas Morkevicius @@ -13,19 +11,33 @@ * @link http://www.gediminasm.org * @license MIT License (http://www.opensource.org/licenses/mit-license.php) */ -final class QueryAnalyzer implements SQLLogger +final class QueryAnalyzer extends AbstractLogger { - /** - * Used database platform - */ - protected AbstractPlatform $platform; + /** @var array */ + public array $queries = []; + private bool $logEnabled = false; /** - * Start time of currently executed query - * - * @var int|float|null + * @param mixed $level + * @param string $message + * @param mixed[] $context */ - private $queryStartTime; + public function log($level, $message, array $context = []): void + { + if (!$this->logEnabled) { + return; + } + + $this->queries[] = [ + 'message' => $message, + 'context' => $context, + ]; + } + + public function reset(): void + { + $this->queries = []; + } /** * Total execution time of all queries @@ -34,54 +46,12 @@ final class QueryAnalyzer implements SQLLogger */ private $totalExecutionTime = 0; - /** - * List of queries executed - */ - private array $queries = []; - /** * Query execution times indexed * in same order as queries */ private array $queryExecutionTimes = []; - /** - * Initialize log listener with database - * platform, which is needed for parameter - * conversion - * - * @param AbstractPlatform $platform - */ - public function __construct(AbstractPlatform $platform) - { - $this->platform = $platform; - } - - public function startQuery($sql, array $params = null, array $types = null): void - { - $this->queryStartTime = \microtime(true); - $this->queries[] = $this->generateSql($sql, $params, $types); - } - - public function stopQuery(): void - { - $ms = \round(\microtime(true) - $this->queryStartTime, 4) * 1000; - $this->queryExecutionTimes[] = $ms; - $this->totalExecutionTime += $ms; - } - - /** - * Clean all collected data - */ - public function cleanUp(): QueryAnalyzer - { - $this->queries = []; - $this->queryExecutionTimes = []; - $this->totalExecutionTime = 0; - - return $this; - } - /** * Dump the statistics of executed queries */ @@ -89,131 +59,40 @@ public function getOutput(bool $dumpOnlySql = false): ?string { $output = ''; if (!$dumpOnlySql) { - $output .= 'Platform: ' . $this->platform->getName() . PHP_EOL; + $output .= 'Platform: (unknown) ' . PHP_EOL; $output .= 'Executed queries: ' . \count($this->queries) . ', total time: ' . $this->totalExecutionTime . ' ms' . PHP_EOL; } foreach ($this->queries as $index => $sql) { if (!$dumpOnlySql) { $output .= 'Query(' . ($index+1) . ') - ' . $this->queryExecutionTimes[$index] . ' ms' . PHP_EOL; } - $output .= $sql . ';' . PHP_EOL; + $output .= $sql['message'] . ';' . PHP_EOL; } $output .= PHP_EOL; return $output; } - /** - * Index of the slowest query executed - */ - public function getSlowestQueryIndex(): int - { - $index = 0; - $slowest = 0; - foreach ($this->queryExecutionTimes as $i => $time) { - if ($time > $slowest) { - $slowest = $time; - $index = $i; - } - } - return $index; - } - - /** - * Get total execution time of queries - */ - public function getTotalExecutionTime(): int - { - return $this->totalExecutionTime; - } - - /** - * Get all queries - * - * @return array - */ public function getExecutedQueries(): array { - return $this->queries; + return array_map( + fn (array $data) => $data['context']['sql'], + $this->queries + ); } - /** - * Get number of executed queries - */ public function getNumExecutedQueries(): int { return \count($this->queries); } - /** - * Get all query execution times - * - * @return array - */ - public function getExecutionTimes(): array + public function enable(): void { - return $this->queryExecutionTimes; + $this->logEnabled = true; } - /** - * Create the SQL with mapped parameters - * - * @param array $params - * @param array $types - */ - private function generateSql(string $sql, array $params, array $types): string + public function disable(): void { - if (!\count($params)) { - return $sql; - } - $converted = $this->getConvertedParams($params, $types); - if (\is_int(\key($params))) { - $index = \key($converted); - $sql = \preg_replace_callback('@\?@sm', static function ($match) use (&$index, $converted) { - return \implode(' ', $converted[$index++]); - }, $sql); - } else { - foreach ($converted as $key => $value) { - $sql = \str_replace(':' . $key, $value, $sql); - } - } - return $sql; - } - - /** - * Get the converted parameter list - * - * @param array $params - * @param array $types - * - * @return array - */ - private function getConvertedParams(array $params, array $types): array - { - $result = []; - foreach ($params as $position => $value) { - if (isset($types[$position])) { - $type = $types[$position]; - if (\is_string($type)) { - $type = Type::getType($type); - } - if ($type instanceof Type) { - $value = $type->convertToDatabaseValue($value, $this->platform); - } - } elseif ($value instanceof \DateTime) { - $value = $value->format($this->platform->getDateTimeFormatString()); - } elseif (!\is_null($value)) { - $type = Type::getType(\gettype($value)); - $value = $type->convertToDatabaseValue($value, $this->platform); - } - if (\is_string($value)) { - $value = "'$value'"; - } elseif (\is_null($value)) { - $value = 'NULL'; - } - $result[$position] = $value; - } - - return $result; + $this->logEnabled = false; } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 51a51027..9f76a7ac 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -2,11 +2,16 @@ require __DIR__.'/../vendor/autoload.php'; -if (method_exists(\Doctrine\Common\Annotations\AnnotationRegistry::class, 'class_exists')) { - \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists'); -} -$reader = new \Doctrine\Common\Annotations\AnnotationReader(); -if (class_exists('Doctrine\Common\Cache\ArrayCache')) { - $reader = new \Doctrine\Common\Annotations\CachedReader($reader, new \Doctrine\Common\Cache\ArrayCache()); +if (class_exists('Doctrine\Common\Annotations\AnnotationReader')) { + if (method_exists(\Doctrine\Common\Annotations\AnnotationRegistry::class, 'class_exists')) { + \Doctrine\Common\Annotations\AnnotationRegistry::registerLoader('class_exists'); + } + $reader = new \Doctrine\Common\Annotations\AnnotationReader(); + if (class_exists('Doctrine\Common\Cache\ArrayCache')) { + $reader = new \Doctrine\Common\Annotations\CachedReader($reader, new \Doctrine\Common\Cache\ArrayCache()); + } +} else { + $reader = null; } + $_ENV['annotation_reader'] = $reader;