diff --git a/.doctrine-project.json b/.doctrine-project.json index c102e96d9b..61c211eec7 100644 --- a/.doctrine-project.json +++ b/.doctrine-project.json @@ -6,11 +6,22 @@ "docsSlug": "doctrine-mongodb-odm", "versions": [ { - "name": "2.9", - "branchName": "2.9.x", + "name": "2.10", + "branchName": "2.10.x", "slug": "latest", "upcoming": true, "aliases": [ + "2.10.x" + ] + }, + { + "name": "2.9", + "branchName": "2.9.x", + "slug": "2.9", + "current": true, + "aliases": [ + "current", + "stable", "2.9.x" ] }, @@ -18,10 +29,8 @@ "name": "2.8", "branchName": "2.8.x", "slug": "2.8", - "current": true, + "maintained": false, "aliases": [ - "current", - "stable", "2.8.x" ] }, diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index c5dd572907..6a34dc2a86 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -11,4 +11,4 @@ on: jobs: coding-standards: name: "Coding Standards" - uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.0.1" + uses: "doctrine/.github/.github/workflows/coding-standards.yml@7.1.0" diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index cf9b106063..ab64f52002 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -21,6 +21,7 @@ jobs: - "8.1" - "8.2" - "8.3" + - "8.4" mongodb-version: - "7.0" - "6.0" @@ -105,8 +106,6 @@ jobs: if: "${{ matrix.symfony-version == '7' }}" run: | composer config minimum-stability dev - # not yet ready for v7 - composer remove --no-update --dev vimeo/psalm # update symfony deps composer require --no-update symfony/console:^7@dev composer require --no-update symfony/var-dumper:^7@dev diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml index 3cac620a37..0c4ee4a59b 100644 --- a/.github/workflows/release-on-milestone-closed.yml +++ b/.github/workflows/release-on-milestone-closed.yml @@ -8,7 +8,7 @@ on: jobs: release: name: "Git tag, release & create merge-up PR" - uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.0.1" + uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@7.1.0" secrets: GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }} GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 5cd5ac4e31..bcd44649cf 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -59,36 +59,3 @@ jobs: - name: "Run a static analysis with phpstan/phpstan" run: "vendor/bin/phpstan analyse --error-format=github" - - static-analysis-psalm: - name: "Static Analysis with Psalm" - runs-on: "ubuntu-22.04" - - strategy: - matrix: - php-version: - - "8.2" - - steps: - - name: "Checkout code" - uses: "actions/checkout@v4" - - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - extensions: "mongodb" - php-version: "${{ matrix.php-version }}" - - - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v3" - - - name: "Upload composer.lock as build artifact" - uses: actions/upload-artifact@v4 - with: - name: "composer-lock-static-analysis-psalm" - path: composer.lock - overwrite: true - - - name: "Run a static analysis with vimeo/psalm" - run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)" diff --git a/.gitignore b/.gitignore index ef4553f5df..68a3971619 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,3 @@ vendor/ .phpunit.cache .phpunit.result.cache phpcs.xml -psalm.xml diff --git a/composer.json b/composer.json index fdcf7c9a2d..1bdaf6c08d 100644 --- a/composer.json +++ b/composer.json @@ -47,8 +47,7 @@ "phpstan/phpstan-phpunit": "^1.0", "phpunit/phpunit": "^10.4", "squizlabs/php_codesniffer": "^3.5", - "symfony/cache": "^5.4 || ^6.0 || ^7.0", - "vimeo/psalm": "~5.24.0" + "symfony/cache": "^5.4 || ^6.0 || ^7.0" }, "conflict": { "doctrine/annotations": "<1.12 || >=3.0" diff --git a/docs/en/cookbook/simple-search-engine.rst b/docs/en/cookbook/simple-search-engine.rst index b265957666..267e860954 100644 --- a/docs/en/cookbook/simple-search-engine.rst +++ b/docs/en/cookbook/simple-search-engine.rst @@ -3,7 +3,7 @@ Simple Search Engine It is very easy to implement a simple keyword search engine with MongoDB. Because of its flexible schema less nature we can store the keywords we want to search through directly -on the document. MongoDB is capable of indexing the embedded documents so the results are fast +on the document. MongoDB is capable of indexing an array field, so the results are fast and scalable. Sample Model: Product @@ -25,15 +25,9 @@ setup a document like the following with a ``$keywords`` property that is mapped #[Field(type: 'string')] public string $title; - /** @var Collection */ #[Field(type: 'collection')] #[Index] - public Collection $keywords; - - public function __construct() - { - $this->keywords = new ArrayCollection(); - } + public array $keywords = []; } Working with Keywords @@ -47,11 +41,11 @@ Now, create a product and add some keywords: $product = new Product(); $product->title = 'Nike Air Jordan 2011'; - $product->keywords->add('nike shoes'); - $product->keywords->add('jordan shoes'); - $product->keywords->add('air jordan'); - $product->keywords->add('shoes'); - $product->keywords->add('2011'); + $product->keywords[] = 'nike shoes'; + $product->keywords[] = 'jordan shoes'; + $product->keywords[] = 'air jordan'; + $product->keywords[] = 'shoes'; + $product->keywords[] = '2011'; $dm->persist($product); $dm->flush(); diff --git a/docs/en/cookbook/time-series-data.rst b/docs/en/cookbook/time-series-data.rst new file mode 100644 index 0000000000..4bc32b43bf --- /dev/null +++ b/docs/en/cookbook/time-series-data.rst @@ -0,0 +1,132 @@ +Storing Time Series Data +======================== + +.. note:: + + Support for mapping time series data was added in ODM 2.10. + +`time-series data `__ +is a sequence of data points in which insights are gained by analyzing changes +over time. + +Time series data is generally composed of these components: + +- + Time when the data point was recorded + +- + Metadata, which is a label, tag, or other data that identifies a data series + and rarely changes + +- + Measurements, which are the data points tracked at increments in time. + +A time series document always contains a time value, and one or more measurement +fields. Metadata is optional, but cannot be added to a time series collection +after creating it. When using an embedded document for metadata, fields can be +added to this document after creating the collection. + +.. note:: + + Support for time series collections was added in MongoDB 5.0. Attempting to + use this functionality on older server versions will result in an error on + schema creation. + +Creating The Model +------------------ + +For this example, we'll be storing data from multiple sensors measuring +temperature and humidity. Other examples for time series include stock data, +price information, website visitors, or vehicle telemetry (speed, position, +etc.). + +First, we define the model for our data: + +.. code-block:: php + + id = (string) new ObjectId(); + } + } + +Note that we defined the entire model as readonly. While we could theoretically +change values in the document, in this example we'll assume that the data will +not change. + +Now we can mark the document as a time series document. To do so, we use the +``TimeSeries`` attribute, configuring appropriate values for the time and +metadata field, which in our case stores the ID of the sensor reporting the +measurement: + +.. code-block:: php + + persist($measurement); + $documentManager->flush(); + +Note that other functionality such as querying, using aggregation pipelines, or +removing data works the same as with other collections. + +Considerations +-------------- + +With the mapping above, data is stored with a granularity of seconds. Depending +on how often measurements come in, we can reduce the granularity to minutes or +hours. This changes how the data is stored internally by changing the bucket +size. This affects storage requirements and query performance. + +For example, with the default ``seconds`` granularity, each bucket groups +documents for one hour. If each sensor only reports data every few minutes, we'd +do well to configure ``minute`` granularity. This reduces the +number of buckets created, reducing storage and making queries more efficient. +However, if we were to choose ``hours`` for granularity, readings for a whole +month would be grouped into one bucket, resulting in slower queries as more +entries have to be traversed when reading data. + +More details on granularity and other consideration scan be found in the +`MongoDB documentation `__. diff --git a/docs/en/reference/attributes-reference.rst b/docs/en/reference/attributes-reference.rst index b6d8a616e9..dedaf906aa 100644 --- a/docs/en/reference/attributes-reference.rst +++ b/docs/en/reference/attributes-reference.rst @@ -1144,6 +1144,64 @@ for sharding the document collection. //... } +#[TimeSeries] +------------- + +This attribute may be used at the class level to mark a collection as containing +:doc:`time-series data <../cookbook/time-series-data>`. + +.. code-block:: php + + + @@ -634,4 +635,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Aggregation.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Aggregation.php index 06a5fc90ce..cf722aa86a 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Aggregation.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Aggregation.php @@ -18,13 +18,13 @@ use function array_merge; use function assert; -/** @psalm-import-type PipelineExpression from Builder */ +/** @phpstan-import-type PipelineExpression from Builder */ final class Aggregation implements IteratorAggregate { /** * @param array $pipeline * @param array $options - * @psalm-param PipelineExpression $pipeline + * @phpstan-param PipelineExpression $pipeline */ public function __construct(private DocumentManager $dm, private ?ClassMetadata $classMetadata, private Collection $collection, private array $pipeline, private array $options = [], private bool $rewindable = true) { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php index 4631ae633a..8ec6ad5cb0 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php @@ -28,9 +28,9 @@ /** * Fluent interface for building aggregation pipelines. * - * @psalm-import-type SortShape from Sort - * @psalm-import-type StageExpression from Stage - * @psalm-type PipelineExpression = list + * @phpstan-import-type SortShape from Sort + * @phpstan-type StageExpression array + * @phpstan-type PipelineExpression list */ class Builder { @@ -44,7 +44,7 @@ class Builder */ private ClassMetadata $class; - /** @psalm-var class-string */ + /** @var class-string */ private ?string $hydrationClass = null; /** @@ -60,7 +60,7 @@ class Builder /** * Create a new aggregation builder. * - * @psalm-param class-string $documentName + * @param class-string $documentName */ public function __construct(DocumentManager $dm, string $documentName) { @@ -270,8 +270,8 @@ public function getAggregation(array $options = []): Aggregation * you should not apply filters as this may cause wrong results to be * given. * - * @return array> - * @psalm-return PipelineExpression + * @return list> + * @phpstan-return PipelineExpression */ // phpcs:enable Squiz.Commenting.FunctionComment.ExtraParamComment public function getPipeline(/* bool $applyFilters = true */): array @@ -609,7 +609,7 @@ public function skip(int $skip): Stage\Skip * * @param array>|string $fieldName Field name or array of field/order pairs * @param int|string|null $order Field order (if one field is specified) - * @psalm-param SortShape|string $fieldName Field name or array of field/order pairs + * @phpstan-param SortShape|string $fieldName Field name or array of field/order pairs */ public function sort($fieldName, $order = null): Stage\Sort { @@ -699,7 +699,7 @@ public function addStage(Stage $stage): Stage */ private function applyFilters(array $query): array { - $documentPersister = $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name); + $documentPersister = $this->getDocumentPersister(); $query = $documentPersister->addDiscriminatorToPreparedQuery($query); $query = $documentPersister->addFilterToPreparedQuery($query); diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Expr.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Expr.php index 732c583c1a..fe84d3107e 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Expr.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Expr.php @@ -45,7 +45,7 @@ /** * Fluent interface for building aggregation pipelines. * - * @psalm-type OperatorExpression = array|object + * @phpstan-type OperatorExpression array|object */ class Expr implements AccumulatorOperators, diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage.php index 15395cf52b..9de942e1b6 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage.php @@ -14,8 +14,8 @@ * * @internal * - * @psalm-import-type PipelineExpression from Builder - * @psalm-type StageExpression = array + * @phpstan-import-type PipelineExpression from Builder + * @phpstan-import-type StageExpression from Builder */ abstract class Stage { @@ -31,7 +31,7 @@ public function __construct(Builder $builder) * Assembles the aggregation stage * * @return array - * @psalm-return StageExpression + * @phpstan-return StageExpression */ abstract public function getExpression(): array; @@ -189,8 +189,8 @@ public function geoNear($x, $y = null): Stage\GeoNear /** * Returns the assembled aggregation pipeline * - * @return array> - * @psalm-return PipelineExpression + * @return list> + * @phpstan-return PipelineExpression */ public function getPipeline(): array { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AddFields.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AddFields.php index b658656bb9..3315c9ee46 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AddFields.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AddFields.php @@ -9,8 +9,8 @@ /** * Fluent interface for adding a $addFields stage to an aggregation pipeline. * - * @psalm-import-type OperatorExpression from Expr - * @psalm-type AddFieldsStageExpression = array{'$addFields': array} + * @phpstan-import-type OperatorExpression from Expr + * @phpstan-type AddFieldsStageExpression array{'$addFields': array} */ final class AddFields extends Operator { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket.php index 67e9c80d3f..47328371f6 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket.php @@ -63,7 +63,6 @@ public function output(): Bucket\BucketOutput /** @return array{boundaries: mixed[], default: mixed} */ protected function getExtraPipelineFields(): array { - /** @psalm-suppress RedundantPropertyInitializationCheck because the property might not be set yet */ $fields = ['boundaries' => $this->boundaries ?? null]; if ($this->default !== null) { $fields['default'] = $this->default; diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/CollStats.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/CollStats.php index 688e40c612..881c067215 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/CollStats.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/CollStats.php @@ -10,7 +10,7 @@ /** * Fluent interface for adding a $collStats stage to an aggregation pipeline. * - * @psalm-type CollStatsStageExpression = array{ + * @phpstan-type CollStatsStageExpression array{ * '$collStats': array{ * latencyStats?: array{histograms?: bool}, * storageStats?: array{}, @@ -52,7 +52,7 @@ public function showStorageStats(): static return $this; } - /** @psalm-return CollStatsStageExpression */ + /** @phpstan-return CollStatsStageExpression */ public function getExpression(): array { $collStats = []; diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Count.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Count.php index 7a9e55e3f5..008e6a52fb 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Count.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Count.php @@ -10,7 +10,7 @@ /** * Fluent interface for adding a $count stage to an aggregation pipeline. * - * @psalm-type CountStageExpression = array{'$count': string} + * @phpstan-type CountStageExpression array{'$count': string} */ class Count extends Stage { @@ -19,7 +19,7 @@ public function __construct(Builder $builder, private string $fieldName) parent::__construct($builder); } - /** @psalm-return CountStageExpression */ + /** @phpstan-return CountStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Densify.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Densify.php index 0e054433d2..50f01ac755 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Densify.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Densify.php @@ -13,9 +13,9 @@ /** * Fluent interface for adding a $densify stage to an aggregation pipeline. * - * @psalm-type BoundsType = 'full'|'partition'|array{0: int|float|UTCDateTime, 1: int|float|UTCDateTime} - * @psalm-type UnitType = 'year'|'month'|'week'|'day'|'hour'|'minute'|'second'|'millisecond' - * @psalm-type DensifyStageExpression = array{ + * @phpstan-type BoundsType 'full'|'partition'|array{0: int|float|UTCDateTime, 1: int|float|UTCDateTime} + * @phpstan-type UnitType 'year'|'month'|'week'|'day'|'hour'|'minute'|'second'|'millisecond' + * @phpstan-type DensifyStageExpression array{ * '$densify': object{ * field: string, * partitionByFields?: list, @@ -54,8 +54,8 @@ public function partitionByFields(string ...$fields): static /** * @param array|string $bounds * @param int|float $step - * @psalm-param BoundsType $bounds - * @psalm-param ''|UnitType $unit + * @phpstan-param BoundsType $bounds + * @phpstan-param ''|UnitType $unit */ public function range($bounds, $step, string $unit = ''): static { @@ -71,7 +71,7 @@ public function range($bounds, $step, string $unit = ''): static return $this; } - /** @psalm-return DensifyStageExpression */ + /** @phpstan-return DensifyStageExpression */ public function getExpression(): array { $params = (object) [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Facet.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Facet.php index 1096f0d040..a8a963ab0a 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Facet.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Facet.php @@ -14,8 +14,8 @@ /** * Fluent interface for adding a $facet stage to an aggregation pipeline. * - * @psalm-import-type PipelineExpression from Builder - * @psalm-type FacetStageExpression = array{'$facet': array} + * @phpstan-import-type PipelineExpression from Builder + * @phpstan-type FacetStageExpression array{'$facet': array} */ class Facet extends Stage { @@ -48,7 +48,6 @@ public function field(string $field): static */ public function pipeline($builder): static { - /** @psalm-suppress RedundantPropertyInitializationCheck because the property might not be set yet */ if (! isset($this->field)) { throw new LogicException(__METHOD__ . ' requires setting a current field using field().'); } diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill.php index df1069166c..211a5b9a48 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill.php @@ -17,11 +17,11 @@ /** * Fluent interface for adding a $fill stage to an aggregation pipeline. * - * @psalm-import-type SortDirectionKeywords from Sort - * @psalm-import-type OperatorExpression from Expr - * @psalm-type SortDirection = int|SortDirectionKeywords - * @psalm-type SortShape = array - * @psalm-type FillStageExpression = array{ + * @phpstan-import-type SortDirectionKeywords from Sort + * @phpstan-import-type OperatorExpression from Expr + * @phpstan-type SortDirection int|SortDirectionKeywords + * @phpstan-type SortShape array + * @phpstan-type FillStageExpression array{ * '$fill': array{ * partitionBy?: string|OperatorExpression, * partitionByFields?: list, @@ -67,8 +67,8 @@ public function partitionByFields(string ...$fields): static /** * @param array|string $fieldName Field name or array of field/order pairs * @param int|string $order Field order (if one field is specified) - * @psalm-param SortShape|string $fieldName - * @psalm-param SortDirection|null $order + * @phpstan-param SortShape|string $fieldName + * @phpstan-param SortDirection|null $order */ public function sortBy($fieldName, $order = null): static { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill/Output.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill/Output.php index f12ce9f62c..4e28d6015e 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill/Output.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill/Output.php @@ -16,7 +16,7 @@ /** * Fluent builder for output param of $fill stage * - * @psalm-import-type SortShape from Fill + * @phpstan-import-type SortShape from Fill */ class Output extends Stage { @@ -44,7 +44,7 @@ public function partitionByFields(string ...$fields): Fill /** * @param array|string $fieldName Field name or array of field/order pairs * @param int|string $order Field order (if one field is specified) - * @psalm-param SortShape|string $fieldName + * @phpstan-param SortShape|string $fieldName */ public function sortBy($fieldName, $order = null): Fill { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup.php index 962da0b50a..e58809f2a0 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup.php @@ -20,7 +20,7 @@ use function is_string; use function substr; -/** @psalm-import-type FieldMapping from ClassMetadata */ +/** @phpstan-import-type FieldMapping from ClassMetadata */ class GraphLookup extends Stage { private ?string $from; @@ -130,7 +130,7 @@ public function depthField(string $depthField): static * Target collection for the $graphLookup operation to search, recursively * matching the connectFromField to the connectToField. * - * @psalm-param class-string|string $from + * @param class-string|string $from */ public function from(string $from): static { @@ -281,7 +281,7 @@ private function getDocumentPersister(ClassMetadata $class): DocumentPersister return $this->dm->getUnitOfWork()->getDocumentPersister($class->name); } - /** @psalm-param FieldMapping $mapping */ + /** @phpstan-param FieldMapping $mapping */ private function getReferencedFieldName(string $fieldName, array $mapping): string { if (! $this->targetClass) { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Group.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Group.php index f57ef9a32b..b6bc71a5f5 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Group.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Group.php @@ -12,7 +12,7 @@ /** * Fluent interface for adding a $group stage to an aggregation pipeline. * - * @psalm-type GroupStageExpression = array{'$group': array} + * @phpstan-type GroupStageExpression array{'$group': array} */ class Group extends Operator implements GroupAccumulatorOperators { @@ -28,7 +28,7 @@ public function __construct(Builder $builder) $this->expr = $builder->expr(); } - /** @psalm-return GroupStageExpression */ + /** @phpstan-return GroupStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/IndexStats.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/IndexStats.php index f59e7732fb..f253ee462a 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/IndexStats.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/IndexStats.php @@ -10,11 +10,11 @@ /** * Fluent interface for adding a $indexStats stage to an aggregation pipeline. * - * @psalm-type IndexStatsStageExpression = array{'$indexStats': object} + * @phpstan-type IndexStatsStageExpression array{'$indexStats': object} */ class IndexStats extends Stage { - /** @psalm-return IndexStatsStageExpression */ + /** @phpstan-return IndexStatsStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Limit.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Limit.php index 7081749470..61df12b398 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Limit.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Limit.php @@ -10,7 +10,7 @@ /** * Fluent interface for adding a $limit stage to an aggregation pipeline. * - * @psalm-type LimitStageExpression = array{'$limit': int} + * @phpstan-type LimitStageExpression array{'$limit': int} */ class Limit extends Stage { @@ -19,7 +19,7 @@ public function __construct(Builder $builder, private int $limit) parent::__construct($builder); } - /** @psalm-return LimitStageExpression */ + /** @phpstan-return LimitStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php index 7b6fcde709..49a93c0fb7 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php @@ -16,10 +16,10 @@ /** * Fluent interface for building aggregation pipelines. * - * @psalm-import-type PipelineExpression from Builder - * @psalm-type PipelineParamType = Builder|Stage|PipelineExpression - * @psalm-type LookupStageExpression = array{ - * $lookup: array{ + * @phpstan-import-type PipelineExpression from Builder + * @phpstan-type PipelineParamType Builder|Stage|PipelineExpression + * @phpstan-type LookupStageExpression array{ + * '$lookup': array{ * from: string, * 'as'?: string, * localField?: string, @@ -46,7 +46,7 @@ class Lookup extends Stage /** * @var Builder|array>|null - * @psalm-var Builder|PipelineExpression|null + * @phpstan-var Builder|PipelineExpression|null */ private Builder|array|null $pipeline = null; @@ -101,7 +101,7 @@ public function from(string $from): static return $this; } - /** @psalm-return LookupStageExpression */ + /** @phpstan-return LookupStageExpression */ public function getExpression(): array { $lookup = [ @@ -193,7 +193,7 @@ public function let(array $let): static * and then reference the variables in the pipeline stages. * * @param Builder|Stage|array> $pipeline - * @psalm-param PipelineParamType $pipeline + * @phpstan-param PipelineParamType $pipeline */ public function pipeline($pipeline): static { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php index 356efa3eb9..36b4a0616a 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php @@ -16,12 +16,12 @@ use function is_array; /** - * @psalm-import-type PipelineExpression from Builder - * @psalm-type OutputCollection = string|array{db: string, coll: string} - * @psalm-type WhenMatchedType = 'replace'|'keepExisting'|'merge'|'fail'|PipelineExpression - * @psalm-type WhenMatchedParamType = Builder|Stage|WhenMatchedType - * @psalm-type WhenNotMatchedType = 'insert'|'discard'|'fail' - * @psalm-type MergeStageExpression = array{ + * @phpstan-import-type PipelineExpression from Builder + * @phpstan-type OutputCollection string|array{db: string, coll: string} + * @phpstan-type WhenMatchedType 'replace'|'keepExisting'|'merge'|'fail'|PipelineExpression + * @phpstan-type WhenMatchedParamType Builder|Stage|WhenMatchedType + * @phpstan-type WhenNotMatchedType 'insert'|'discard'|'fail' + * @phpstan-type MergeStageExpression array{ * '$merge': object{ * into: OutputCollection, * on?: string|list, @@ -33,7 +33,7 @@ */ class Merge extends Stage { - /** @psalm-var OutputCollection */ + /** @phpstan-var OutputCollection */ private string|array $into; /** @var list */ @@ -42,7 +42,7 @@ class Merge extends Stage /** @var array */ private array $let = []; - /** @psalm-var WhenMatchedParamType */ + /** @phpstan-var WhenMatchedParamType */ private string|array|Builder|Stage|null $whenMatched = null; private ?string $whenNotMatched = null; @@ -52,7 +52,7 @@ public function __construct(Builder $builder, private DocumentManager $dm) parent::__construct($builder); } - /** @psalm-return MergeStageExpression */ + /** @phpstan-return MergeStageExpression */ public function getExpression(): array { $params = (object) [ @@ -82,7 +82,7 @@ public function getExpression(): array /** * @param string|array $collection - * @psalm-param OutputCollection $collection + * @phpstan-param OutputCollection $collection */ public function into($collection): static { @@ -126,7 +126,7 @@ public function on(string ...$fields): static /** * @param string|array|Builder|Stage $whenMatched - * @psalm-param WhenMatchedParamType $whenMatched + * @phpstan-param WhenMatchedParamType $whenMatched */ public function whenMatched($whenMatched): static { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php index aa6a335012..2021e6d1e6 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php @@ -14,12 +14,12 @@ use function is_array; /** - * @psalm-import-type OutputCollection from Merge - * @psalm-type OutStageExpression = array{'$out': OutputCollection} + * @phpstan-import-type OutputCollection from Merge + * @phpstan-type OutStageExpression array{'$out': OutputCollection} */ class Out extends Stage { - /** @psalm-var OutputCollection */ + /** @phpstan-var OutputCollection */ private array|string $out; public function __construct(Builder $builder, string $collection, private DocumentManager $dm) @@ -38,7 +38,7 @@ public function getExpression(): array /** * @param string|array $collection - * @psalm-param OutputCollection $collection + * @phpstan-param OutputCollection $collection */ public function out($collection): Stage\Out { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Project.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Project.php index c108e5f3e4..8b77ec68e4 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Project.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Project.php @@ -9,12 +9,12 @@ /** * Fluent interface for adding a $project stage to an aggregation pipeline. * - * @psalm-import-type OperatorExpression from Expr - * @psalm-type ProjectStageExpression = array{'$project': array} + * @phpstan-import-type OperatorExpression from Expr + * @phpstan-type ProjectStageExpression array{'$project': array} */ class Project extends Operator { - /** @psalm-return ProjectStageExpression */ + /** @phpstan-return ProjectStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Redact.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Redact.php index 2ac792bdba..c8b0614072 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Redact.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Redact.php @@ -9,8 +9,8 @@ /** * Fluent interface for adding a $redact stage to an aggregation pipeline. * - * @psalm-import-type OperatorExpression from Expr - * @psalm-type SetStageExpression = array{'$redact': array} + * @phpstan-import-type OperatorExpression from Expr + * @phpstan-type SetStageExpression array{'$redact': array} */ class Redact extends Operator { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceRoot.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceRoot.php index 3d25a2a74a..4ffa90f1f1 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceRoot.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceRoot.php @@ -7,8 +7,8 @@ use Doctrine\ODM\MongoDB\Aggregation\Expr; /** - * @psalm-import-type OperatorExpression from Expr - * @psalm-type ReplaceRootStageExpression = array{ + * @phpstan-import-type OperatorExpression from Expr + * @phpstan-type ReplaceRootStageExpression array{ * '$replaceRoot': array{ * newRoot: OperatorExpression|string, * } diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceWith.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceWith.php index 317cde3c28..c1341eed40 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceWith.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceWith.php @@ -7,8 +7,8 @@ use Doctrine\ODM\MongoDB\Aggregation\Expr; /** - * @psalm-import-type OperatorExpression from Expr - * @psalm-type ReplaceWithStageExpression = array{'$replaceWith': OperatorExpression|string} + * @phpstan-import-type OperatorExpression from Expr + * @phpstan-type ReplaceWithStageExpression array{'$replaceWith': OperatorExpression|string} */ class ReplaceWith extends AbstractReplace { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sample.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sample.php index 24c8d6b275..7d87bb1eca 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sample.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sample.php @@ -10,7 +10,7 @@ /** * Fluent interface for adding a $sample stage to an aggregation pipeline. * - * @psalm-type SampleStageExpression = array{'$sample': array{size: int}} + * @phpstan-type SampleStageExpression array{'$sample': array{size: int}} */ class Sample extends Stage { @@ -19,7 +19,7 @@ public function __construct(Builder $builder, private int $size) parent::__construct($builder); } - /** @psalm-return SampleStageExpression */ + /** @phpstan-return SampleStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php index 86cf1dc6b6..e1eea694c5 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php @@ -16,12 +16,12 @@ use function strtolower; /** - * @psalm-import-type SortDirectionKeywords from Sort - * @psalm-type CountType = 'lowerBound'|'total' - * @psalm-type SortMetaKeywords = 'searchScore' - * @psalm-type SortMeta = array{'$meta': SortMetaKeywords} - * @psalm-type SortShape = array - * @psalm-type SearchStageExpression = array{ + * @phpstan-import-type SortDirectionKeywords from Sort + * @phpstan-type CountType 'lowerBound'|'total' + * @phpstan-type SortMetaKeywords 'searchScore' + * @phpstan-type SortMeta array{'$meta': SortMetaKeywords} + * @phpstan-type SortShape array + * @phpstan-type SearchStageExpression array{ * '$search': object{ * index?: string, * count?: object{ @@ -71,7 +71,7 @@ public function __construct(Builder $builder) parent::__construct($builder); } - /** @psalm-return SearchStageExpression */ + /** @phpstan-return SearchStageExpression */ public function getExpression(): array { $params = (object) []; @@ -111,7 +111,7 @@ public function index(string $name): static return $this; } - /** @psalm-param CountType $type */ + /** @phpstan-param CountType $type */ public function countDocuments(string $type, ?int $threshold = null): static { $this->count = (object) ['type' => $type]; @@ -148,8 +148,8 @@ public function returnStoredSource(bool $returnStoredSource = true): static /** * @param array|string $fieldName Field name or array of field/order pairs * @param int|string $order Field order (if one field is specified) - * @psalm-param SortShape|string $fieldName - * @psalm-param int|SortMeta|SortDirectionKeywords|null $order + * @phpstan-param SortShape|string $fieldName + * @phpstan-param int|SortMeta|SortDirectionKeywords|null $order */ public function sort($fieldName, $order = null): static { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/AbstractSearchOperator.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/AbstractSearchOperator.php index eb115ab9cc..c7dede7c4c 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/AbstractSearchOperator.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/AbstractSearchOperator.php @@ -11,10 +11,10 @@ /** * @internal * - * @psalm-import-type SortDirectionKeywords from Sort - * @psalm-import-type SortMetaKeywords from Search - * @psalm-import-type SortMeta from Search - * @psalm-import-type SortShape from Search + * @phpstan-import-type SortDirectionKeywords from Sort + * @phpstan-import-type SortMetaKeywords from Search + * @phpstan-import-type SortMeta from Search + * @phpstan-import-type SortShape from Search */ abstract class AbstractSearchOperator extends Stage implements SearchOperator { @@ -46,18 +46,15 @@ public function returnStoredSource(bool $returnStoredSource): Search /** * @param array|string $fieldName Field name or array of field/order pairs * @param int|string $order Field order (if one field is specified) - * @psalm-param SortShape|string $fieldName - * @psalm-param int|SortMeta|SortDirectionKeywords|null $order + * @phpstan-param SortShape|string $fieldName + * @phpstan-param int|SortMeta|SortDirectionKeywords|null $order */ public function sort($fieldName, $order = null): Search { return $this->search->sort($fieldName, $order); } - /** - * @return array - * @psalm-return non-empty-array - */ + /** @return non-empty-array */ final public function getExpression(): array { return [$this->getOperatorName() => $this->getOperatorParams()]; diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Set.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Set.php index cca5aed345..09c1fe85b8 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Set.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Set.php @@ -9,12 +9,12 @@ /** * Fluent interface for adding a $set stage to an aggregation pipeline. * - * @psalm-import-type OperatorExpression from Expr - * @psalm-type SetStageExpression = array{'$set': array} + * @phpstan-import-type OperatorExpression from Expr + * @phpstan-type SetStageExpression array{'$set': array} */ final class Set extends Operator { - /** @psalm-return SetStageExpression */ + /** @phpstan-return SetStageExpression */ public function getExpression(): array { return ['$set' => $this->expr->getExpression()]; diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SetWindowFields.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SetWindowFields.php index 9d8b436568..0d36a7eaa8 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SetWindowFields.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SetWindowFields.php @@ -14,11 +14,11 @@ use function strtolower; /** - * @psalm-import-type SortDirectionKeywords from Sort - * @psalm-import-type OperatorExpression from Expr - * @psalm-type SortDirection = int|SortDirectionKeywords - * @psalm-type SortShape = array - * @psalm-type SetWindowFieldsStageExpression = array{ + * @phpstan-import-type SortDirectionKeywords from Sort + * @phpstan-import-type OperatorExpression from Expr + * @phpstan-type SortDirection int|SortDirectionKeywords + * @phpstan-type SortShape array + * @phpstan-type SetWindowFieldsStageExpression array{ * '$setWindowFields': object{ * partitionBy?: string|OperatorExpression, * sortBy?: SortShape, @@ -53,8 +53,8 @@ public function partitionBy($expression): static /** * @param array|string $fieldName Field name or array of field/order pairs * @param int|string $order Field order (if one field is specified) - * @psalm-param SortShape|string $fieldName - * @psalm-param SortDirection|null $order + * @phpstan-param SortShape|string $fieldName + * @phpstan-param SortDirection|null $order */ public function sortBy($fieldName, $order = null): static { @@ -76,7 +76,7 @@ public function output(): Output return $this->output; } - /** @psalm-return SetWindowFieldsStageExpression */ + /** @phpstan-return SetWindowFieldsStageExpression */ public function getExpression(): array { $params = (object) [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SetWindowFields/Output.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SetWindowFields/Output.php index 386d9ff30e..640610ad1f 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SetWindowFields/Output.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SetWindowFields/Output.php @@ -21,11 +21,11 @@ /** * Fluent builder for output param of $setWindowFields stage * - * @psalm-import-type SortShape from SetWindowFields - * @psalm-type WindowBound = 'current'|'unbounded'|int - * @psalm-type WindowBounds = array{0: WindowBound, 1: WindowBound} - * @psalm-type WindowUnit = 'year'|'quarter'|'month'|'week'|'day'|'hour'|'minute'|'second'|'millisecond' - * @psalm-type Window = object{ + * @phpstan-import-type SortShape from SetWindowFields + * @phpstan-type WindowBound 'current'|'unbounded'|int + * @phpstan-type WindowBounds array{0: WindowBound, 1: WindowBound} + * @phpstan-type WindowUnit 'year'|'quarter'|'month'|'week'|'day'|'hour'|'minute'|'second'|'millisecond' + * @phpstan-type Window object{ * document?: WindowBounds, * range?: WindowBounds, * unit?: WindowUnit, @@ -37,7 +37,7 @@ class Output extends Operator implements WindowOperators private string $currentField = ''; - /** @psalm-var array */ + /** @phpstan-var array */ private array $windows = []; public function __construct(Builder $builder, private SetWindowFields $setWindowFields) @@ -54,7 +54,7 @@ public function partitionBy($expression): SetWindowFields /** * @param array|string $fieldName Field name or array of field/order pairs * @param int|string $order Field order (if one field is specified) - * @psalm-param SortShape|string $fieldName + * @phpstan-param SortShape|string $fieldName */ public function sortBy($fieldName, $order = null): SetWindowFields { @@ -75,9 +75,9 @@ public function field(string $fieldName): static /** * Specifies the window boundaries and parameters. * - * @psalm-param WindowBounds|null $documents - * @psalm-param WindowBounds|null $range - * @psalm-param WindowUnit|null $unit + * @phpstan-param WindowBounds|null $documents + * @phpstan-param WindowBounds|null $range + * @phpstan-param WindowUnit|null $unit */ public function window(?array $documents = null, ?array $range = null, ?string $unit = null): static { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Skip.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Skip.php index a08fd1167c..440b04ed90 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Skip.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Skip.php @@ -10,7 +10,7 @@ /** * Fluent interface for adding a $skip stage to an aggregation pipeline. * - * @psalm-type SkipStageExpression = array{'$skip': int} + * @phpstan-type SkipStageExpression array{'$skip': int} */ class Skip extends Stage { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sort.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sort.php index 235d753a6a..952597b07b 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sort.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sort.php @@ -15,11 +15,11 @@ /** * Fluent interface for adding a $sort stage to an aggregation pipeline. * - * @psalm-type SortMetaKeywords = 'textScore'|'indexKey' - * @psalm-type SortDirectionKeywords = 'asc'|'desc' - * @psalm-type SortMeta = array{'$meta': SortMetaKeywords} - * @psalm-type SortShape = array - * @psalm-type SortStageExpression = array{ + * @phpstan-type SortMetaKeywords 'textScore'|'indexKey' + * @phpstan-type SortDirectionKeywords 'asc'|'desc' + * @phpstan-type SortMeta array{'$meta': SortMetaKeywords} + * @phpstan-type SortShape array + * @phpstan-type SortStageExpression array{ * '$sort': array * } */ @@ -31,8 +31,8 @@ class Sort extends Stage /** * @param array>|string $fieldName Field name or array of field/order pairs * @param int|string $order Field order (if one field is specified) - * @psalm-param SortShape|string $fieldName - * @psalm-param int|SortMeta|SortDirectionKeywords|null $order + * @phpstan-param SortShape|string $fieldName + * @phpstan-param int|SortMeta|SortDirectionKeywords|null $order */ public function __construct(Builder $builder, $fieldName, $order = null) { @@ -55,7 +55,7 @@ public function __construct(Builder $builder, $fieldName, $order = null) } } - /** @psalm-return SortStageExpression */ + /** @phpstan-return SortStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SortByCount.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SortByCount.php index e37fd46689..76f1347deb 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SortByCount.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SortByCount.php @@ -11,7 +11,7 @@ use function substr; -/** @psalm-type SortByCountStageExpression = array{'$sortByCount': string} */ +/** @phpstan-type SortByCountStageExpression array{'$sortByCount': string} */ class SortByCount extends Stage { private string $fieldName; @@ -29,7 +29,7 @@ public function __construct(Builder $builder, string $fieldName, DocumentManager $this->fieldName = '$' . $documentPersister->prepareFieldName(substr($fieldName, 1)); } - /** @psalm-return SortByCountStageExpression */ + /** @phpstan-return SortByCountStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php index 6c468c9427..0f388a8f43 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php @@ -13,9 +13,9 @@ /** * Fluent interface for adding a $unionWith stage to an aggregation pipeline. * - * @psalm-import-type PipelineExpression from Builder - * @psalm-type PipelineParamType = array|Builder|Stage|PipelineExpression - * @psalm-type UnionWithStageExpression = array{ + * @phpstan-import-type PipelineExpression from Builder + * @phpstan-type PipelineParamType array|Builder|Stage|PipelineExpression + * @phpstan-type UnionWithStageExpression array{ * '$unionWith': object{ * coll: string, * pipeline?: PipelineExpression, @@ -24,7 +24,7 @@ */ class UnionWith extends Stage { - /** @psalm-var ?PipelineParamType */ + /** @phpstan-var ?PipelineParamType */ private array|Builder|Stage|null $pipeline = null; public function __construct(Builder $builder, private DocumentManager $dm, private string $collection) @@ -40,7 +40,7 @@ public function __construct(Builder $builder, private DocumentManager $dm, priva /** * @param array|Builder|Stage $pipeline - * @psalm-param PipelineParamType $pipeline + * @phpstan-param PipelineParamType $pipeline */ public function pipeline($pipeline): static { @@ -57,7 +57,7 @@ public function pipeline($pipeline): static return $this; } - /** @psalm-return UnionWithStageExpression */ + /** @phpstan-return UnionWithStageExpression */ public function getExpression(): array { $params = (object) ['coll' => $this->collection]; diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnsetStage.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnsetStage.php index c6e6bd7acb..8e02c90596 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnsetStage.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnsetStage.php @@ -14,7 +14,7 @@ /** * Fluent interface for adding an $unset stage to an aggregation pipeline. * - * @psalm-type UnsetStageExpression = array{'$unset': list} + * @phpstan-type UnsetStageExpression array{'$unset': list} */ class UnsetStage extends Stage { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Unwind.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Unwind.php index 66596d398f..060ab558e6 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Unwind.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Unwind.php @@ -10,7 +10,7 @@ /** * Fluent interface for adding a $unwind stage to an aggregation pipeline. * - * @psalm-type UnwindStageExpression = array{ + * @phpstan-type UnwindStageExpression array{ * '$unwind': string|array{ * path: string, * includeArrayIndex?: string, @@ -29,7 +29,7 @@ public function __construct(Builder $builder, private string $fieldName) parent::__construct($builder); } - /** @psalm-return UnwindStageExpression */ + /** @phpstan-return UnwindStageExpression */ public function getExpression(): array { // Fallback behavior for MongoDB < 3.2 diff --git a/lib/Doctrine/ODM/MongoDB/Configuration.php b/lib/Doctrine/ODM/MongoDB/Configuration.php index aef7fa05b7..adc486991f 100644 --- a/lib/Doctrine/ODM/MongoDB/Configuration.php +++ b/lib/Doctrine/ODM/MongoDB/Configuration.php @@ -47,7 +47,7 @@ * $config = new Configuration(); * $dm = DocumentManager::create(new Connection(), $config); * - * @psalm-import-type CommitOptions from UnitOfWork + * @phpstan-import-type CommitOptions from UnitOfWork */ class Configuration { @@ -85,7 +85,7 @@ class Configuration /** * Array of attributes for this configuration instance. * - * @psalm-var array{ + * @phpstan-var array{ * autoGenerateHydratorClasses?: self::AUTOGENERATE_*, * autoGeneratePersistentCollectionClasses?: self::AUTOGENERATE_*, * classMetadataFactoryName?: class-string, @@ -326,7 +326,7 @@ public function getHydratorDir(): ?string * Gets an int flag that indicates whether hydrator classes should always be regenerated * during each script execution. * - * @psalm-return self::AUTOGENERATE_* + * @return self::AUTOGENERATE_* */ public function getAutoGenerateHydratorClasses(): int { @@ -337,7 +337,7 @@ public function getAutoGenerateHydratorClasses(): int * Sets an int flag that indicates whether hydrator classes should always be regenerated * during each script execution. * - * @psalm-param self::AUTOGENERATE_* $mode + * @param self::AUTOGENERATE_* $mode */ public function setAutoGenerateHydratorClasses(int $mode): void { @@ -368,7 +368,7 @@ public function getPersistentCollectionDir(): ?string * Gets a integer flag that indicates how and when persistent collection * classes should be generated. * - * @psalm-return self::AUTOGENERATE_* + * @return self::AUTOGENERATE_* */ public function getAutoGeneratePersistentCollectionClasses(): int { @@ -379,7 +379,7 @@ public function getAutoGeneratePersistentCollectionClasses(): int * Sets a integer flag that indicates how and when persistent collection * classes should be generated. * - * @psalm-param self::AUTOGENERATE_* $mode + * @param self::AUTOGENERATE_* $mode */ public function setAutoGeneratePersistentCollectionClasses(int $mode): void { @@ -414,7 +414,7 @@ public function getDefaultDB(): ?string } /** - * @psalm-param class-string $cmfName + * @param class-string $cmfName * * @throws MongoDBException If is not a ClassMetadataFactoryInterface. */ @@ -429,7 +429,7 @@ public function setClassMetadataFactoryName(string $cmfName): void $this->attributes['classMetadataFactoryName'] = $cmfName; } - /** @psalm-return class-string */ + /** @return class-string */ public function getClassMetadataFactoryName(): string { if (! isset($this->attributes['classMetadataFactoryName'])) { @@ -439,7 +439,7 @@ public function getClassMetadataFactoryName(): string return $this->attributes['classMetadataFactoryName']; } - /** @psalm-return CommitOptions */ + /** @phpstan-return CommitOptions */ public function getDefaultCommitOptions(): array { if (! isset($this->attributes['defaultCommitOptions'])) { @@ -449,7 +449,7 @@ public function getDefaultCommitOptions(): array return $this->attributes['defaultCommitOptions']; } - /** @psalm-param CommitOptions $defaultCommitOptions */ + /** @phpstan-param CommitOptions $defaultCommitOptions */ public function setDefaultCommitOptions(array $defaultCommitOptions): void { foreach (UnitOfWork::DEPRECATED_WRITE_OPTIONS as $deprecatedOption) { @@ -470,7 +470,7 @@ public function setDefaultCommitOptions(array $defaultCommitOptions): void * Add a filter to the list of possible filters. * * @param array $parameters - * @psalm-param class-string $className + * @param class-string $className */ public function addFilter(string $name, string $className, array $parameters = []): void { @@ -480,7 +480,7 @@ public function addFilter(string $name, string $className, array $parameters = [ ]; } - /** @psalm-return class-string|null */ + /** @return class-string|null */ public function getFilterClassName(string $name): ?string { return isset($this->attributes['filters'][$name]) @@ -497,7 +497,7 @@ public function getFilterParameters(string $name): array } /** - * @psalm-param class-string> $className + * @param class-string> $className * * @throws MongoDBException If is not an ObjectRepository. */ @@ -512,14 +512,14 @@ public function setDefaultDocumentRepositoryClassName(string $className): void $this->attributes['defaultDocumentRepositoryClassName'] = $className; } - /** @psalm-return class-string> */ + /** @return class-string> */ public function getDefaultDocumentRepositoryClassName(): string { return $this->attributes['defaultDocumentRepositoryClassName'] ?? DocumentRepository::class; } /** - * @psalm-param class-string> $className + * @param class-string> $className * * @throws MongoDBException If the class does not implement the GridFSRepository interface. */ @@ -534,7 +534,7 @@ public function setDefaultGridFSRepositoryClassName(string $className): void $this->attributes['defaultGridFSRepositoryClassName'] = $className; } - /** @psalm-return class-string> */ + /** @return class-string> */ public function getDefaultGridFSRepositoryClassName(): string { return $this->attributes['defaultGridFSRepositoryClassName'] ?? DefaultGridFSRepository::class; diff --git a/lib/Doctrine/ODM/MongoDB/DocumentManager.php b/lib/Doctrine/ODM/MongoDB/DocumentManager.php index 706b23ed86..b8c40aea3b 100644 --- a/lib/Doctrine/ODM/MongoDB/DocumentManager.php +++ b/lib/Doctrine/ODM/MongoDB/DocumentManager.php @@ -49,8 +49,8 @@ * $config = new Configuration(); * $dm = DocumentManager::create(new Connection(), $config); * - * @psalm-import-type CommitOptions from UnitOfWork - * @psalm-import-type FieldMapping from ClassMetadata + * @phpstan-import-type CommitOptions from UnitOfWork + * @phpstan-import-type FieldMapping from ClassMetadata */ class DocumentManager implements ObjectManager { @@ -286,14 +286,11 @@ public function getClassNameResolver(): ClassNameResolver /** * Returns the metadata for a class. * - * @param string $className The class name. - * @psalm-param class-string $className + * @param class-string $className The class name. * - * @psalm-return ClassMetadata + * @return ClassMetadata * * @template T of object - * - * @psalm-suppress InvalidReturnType, InvalidReturnStatement see https://github.com/vimeo/psalm/issues/5788 */ public function getClassMetadata($className): ClassMetadata { @@ -303,7 +300,7 @@ public function getClassMetadata($className): ClassMetadata /** * Returns the MongoDB instance for a class. * - * @psalm-param class-string $className + * @param class-string $className */ public function getDocumentDatabase(string $className): Database { @@ -553,11 +550,9 @@ public function unlock(object $document): void /** * Gets the repository for a document class. * - * @param string $className The name of the Document. - * @psalm-param class-string $className + * @param class-string $className The name of the Document. * - * @return DocumentRepository|GridFSRepository|ViewRepository The repository. - * @psalm-return DocumentRepository|GridFSRepository|ViewRepository + * @return DocumentRepository|GridFSRepository|ViewRepository The repository. * * @template T of object */ @@ -572,7 +567,7 @@ public function getRepository($className) * database. * * @param array $options Array of options to be used with batchInsert(), update() and remove() - * @psalm-param CommitOptions $options + * @phpstan-param CommitOptions $options * * @throws MongoDBException * @throws Throwable From event listeners. @@ -591,19 +586,19 @@ public function flush(array $options = []) * has its identifier populated. Otherwise a proxy is returned that automatically * loads itself on first access. * - * @param mixed $identifier - * @psalm-param class-string $documentName + * @param mixed $identifier + * @param class-string $documentName * - * @psalm-return T|(T&GhostObjectInterface) + * @return T|(T&GhostObjectInterface) * * @template T of object */ public function getReference(string $documentName, $identifier): object { - /** @psalm-var ClassMetadata $class */ + /** @var ClassMetadata $class */ $class = $this->metadataFactory->getMetadataFor(ltrim($documentName, '\\')); assert($class instanceof ClassMetadata); - /** @psalm-var T|false $document */ + /** @phpstan-var T|false $document */ $document = $this->unitOfWork->tryGetById($identifier, $class); // Check identity map first, if its already in there just return it. @@ -611,7 +606,7 @@ public function getReference(string $documentName, $identifier): object return $document; } - /** @psalm-var T&GhostObjectInterface $document */ + /** @var T&GhostObjectInterface $document */ $document = $this->proxyFactory->getProxy($class, $identifier); $this->unitOfWork->registerManaged($document, $identifier, []); @@ -658,13 +653,12 @@ public function getPartialReference(string $documentName, $identifier): object * * This is just a convenient shortcut for getRepository($documentName)->find($id). * - * @param string $className - * @param mixed $id - * @param int $lockMode - * @param int $lockVersion - * @psalm-param class-string $className + * @param class-string $className + * @param mixed $id + * @param int $lockMode + * @param int $lockVersion * - * @psalm-return T|null + * @return T|null * * @template T of object */ @@ -672,7 +666,6 @@ public function find($className, $id, $lockMode = LockMode::NONE, $lockVersion = { $repository = $this->getRepository($className); if ($repository instanceof DocumentRepository) { - /** @psalm-var DocumentRepository $repository */ return $repository->find($id, $lockMode, $lockVersion); } @@ -745,7 +738,7 @@ public function getConfiguration(): Configuration /** * Returns a reference to the supplied document. * - * @psalm-param FieldMapping $referenceMapping + * @phpstan-param FieldMapping $referenceMapping * * @return mixed The reference for the document in question, according to the desired mapping * @@ -803,10 +796,9 @@ public function createReference(object $document, array $referenceMapping) * * @param array $referenceMapping Mappings of reference for which discriminator data is created. * @param ClassMetadata $class Metadata of reference document class. - * @psalm-param FieldMapping $referenceMapping + * @phpstan-param FieldMapping $referenceMapping * - * @return array with next structure [{discriminator field} => {discriminator value}] - * @psalm-return array + * @return array with next structure [{discriminator field} => {discriminator value}] * * @throws MappingException When discriminator map is present and reference class in not registered in it. */ @@ -892,7 +884,7 @@ public function getFilterCollection(): FilterCollection * @param FieldMapping $mapping * @param array|null $data * - * @psalm-return class-string + * @return class-string */ public function getClassNameForAssociation(array $mapping, $data): string { diff --git a/lib/Doctrine/ODM/MongoDB/Event/OnClassMetadataNotFoundEventArgs.php b/lib/Doctrine/ODM/MongoDB/Event/OnClassMetadataNotFoundEventArgs.php index 8119097143..f90f2079ba 100644 --- a/lib/Doctrine/ODM/MongoDB/Event/OnClassMetadataNotFoundEventArgs.php +++ b/lib/Doctrine/ODM/MongoDB/Event/OnClassMetadataNotFoundEventArgs.php @@ -18,7 +18,7 @@ final class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs /** @var ClassMetadata|null */ private ?ClassMetadata $foundMetadata = null; - /** @psalm-param class-string $className */ + /** @param class-string $className */ public function __construct(private string $className, DocumentManager $dm) { parent::__construct($dm); @@ -39,7 +39,7 @@ public function getFoundMetadata(): ?ClassMetadata /** * Retrieve class name for which a failed metadata fetch attempt was executed * - * @psalm-return class-string + * @return class-string */ public function getClassName(): string { diff --git a/lib/Doctrine/ODM/MongoDB/Event/PreUpdateEventArgs.php b/lib/Doctrine/ODM/MongoDB/Event/PreUpdateEventArgs.php index f01dff1a56..a82a0fb1e5 100644 --- a/lib/Doctrine/ODM/MongoDB/Event/PreUpdateEventArgs.php +++ b/lib/Doctrine/ODM/MongoDB/Event/PreUpdateEventArgs.php @@ -15,11 +15,11 @@ /** * Class that holds event arguments for a preUpdate event. * - * @psalm-import-type ChangeSet from UnitOfWork + * @phpstan-import-type ChangeSet from UnitOfWork */ final class PreUpdateEventArgs extends LifecycleEventArgs { - /** @psalm-param array $changeSet */ + /** @param array $changeSet */ public function __construct( object $document, DocumentManager $dm, diff --git a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php index 52c1f3943e..2b91220d5a 100644 --- a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php @@ -37,7 +37,7 @@ * The HydratorFactory class is responsible for instantiating a correct hydrator * type based on document's ClassMetadata * - * @psalm-import-type Hints from UnitOfWork + * @phpstan-import-type Hints from UnitOfWork */ final class HydratorFactory { @@ -69,7 +69,7 @@ final class HydratorFactory /** * Array of instantiated document hydrators. * - * @psalm-var array + * @var array */ private array $hydrators = []; @@ -94,7 +94,7 @@ public function __construct(DocumentManager $dm, EventManager $evm, ?string $hyd /** * Gets the hydrator object for the given document class. * - * @psalm-param class-string $className + * @param class-string $className */ public function getHydratorFor(string $className): HydratorInterface { @@ -420,7 +420,7 @@ public function hydrate(object \$document, array \$data, array \$hints = []): ar * Hydrate array of MongoDB document data into the given document object. * * @param array $data - * @psalm-param Hints $hints Any hints to account for during reconstitution/lookup of the document. + * @phpstan-param Hints $hints Any hints to account for during reconstitution/lookup of the document. * * @return array */ diff --git a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php index 58fc21c07b..f804b783d5 100644 --- a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php +++ b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorInterface.php @@ -9,7 +9,7 @@ /** * The HydratorInterface defines methods all hydrator need to implement * - * @psalm-import-type Hints from UnitOfWork + * @phpstan-import-type Hints from UnitOfWork */ interface HydratorInterface { @@ -17,7 +17,7 @@ interface HydratorInterface * Hydrate array of MongoDB document data into the given document object. * * @param array $data - * @psalm-param Hints $hints + * @phpstan-param Hints $hints * * @return array */ diff --git a/lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php b/lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php index 1e1433cdb1..7e3fc075c2 100644 --- a/lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php +++ b/lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php @@ -17,7 +17,7 @@ * * @internal * - * @psalm-import-type Hints from UnitOfWork + * @phpstan-import-type Hints from UnitOfWork * * @template TDocument of object * @template-implements Iterator @@ -30,7 +30,7 @@ final class HydratingIterator implements Iterator /** * @param Traversable> $traversable * @param ClassMetadata $class - * @psalm-param Hints $unitOfWorkHints + * @phpstan-param Hints $unitOfWorkHints */ public function __construct(Traversable $traversable, private UnitOfWork $unitOfWork, private ClassMetadata $class, private array $unitOfWorkHints = []) { diff --git a/lib/Doctrine/ODM/MongoDB/Iterator/Iterator.php b/lib/Doctrine/ODM/MongoDB/Iterator/Iterator.php index 59561a2f68..bedeacd495 100644 --- a/lib/Doctrine/ODM/MongoDB/Iterator/Iterator.php +++ b/lib/Doctrine/ODM/MongoDB/Iterator/Iterator.php @@ -10,6 +10,6 @@ */ interface Iterator extends \Iterator { - /** @psalm-return array */ + /** @return array */ public function toArray(): array; } diff --git a/lib/Doctrine/ODM/MongoDB/Iterator/PrimingIterator.php b/lib/Doctrine/ODM/MongoDB/Iterator/PrimingIterator.php index 43c21f2787..86c4920b83 100644 --- a/lib/Doctrine/ODM/MongoDB/Iterator/PrimingIterator.php +++ b/lib/Doctrine/ODM/MongoDB/Iterator/PrimingIterator.php @@ -13,7 +13,7 @@ use function iterator_to_array; /** - * @psalm-import-type Hints from UnitOfWork + * @phpstan-import-type Hints from UnitOfWork * @template TValue * @template TDocument of object * @template-implements Iterator @@ -26,7 +26,7 @@ final class PrimingIterator implements Iterator * @param \Iterator $iterator * @param ClassMetadata $class * @param array $primers - * @psalm-param Hints $unitOfWorkHints + * @phpstan-param Hints $unitOfWorkHints */ public function __construct(private \Iterator $iterator, private ClassMetadata $class, private ReferencePrimer $referencePrimer, private array $primers, private array $unitOfWorkHints = []) { diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/SearchIndex.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/SearchIndex.php index cbf3ab52a7..cb5bc2a11f 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/SearchIndex.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/SearchIndex.php @@ -13,8 +13,8 @@ * * @Annotation * @NamedArgumentConstructor - * @psalm-import-type SearchIndexStoredSource from ClassMetadata - * @psalm-import-type SearchIndexSynonym from ClassMetadata + * @phpstan-import-type SearchIndexStoredSource from ClassMetadata + * @phpstan-import-type SearchIndexSynonym from ClassMetadata */ #[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] class SearchIndex implements Annotation diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/TimeSeries.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/TimeSeries.php new file mode 100644 index 0000000000..e4fe593eb4 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/TimeSeries.php @@ -0,0 +1,29 @@ +, * } - * @psalm-type FieldMapping = array{ + * @phpstan-type FieldMapping array{ * type: string, * fieldName: string, * name: string, @@ -152,7 +153,7 @@ * enumType?: class-string, * storeEmptyArray?: bool, * } - * @psalm-type AssociationFieldMapping = array{ + * @phpstan-type AssociationFieldMapping array{ * type?: string, * fieldName: string, * name: string, @@ -197,8 +198,8 @@ * alsoLoadFields?: list, * storeEmptyArray?: bool, * } - * @psalm-type IndexKeys = array - * @psalm-type IndexOptions = array{ + * @phpstan-type IndexKeys array + * @phpstan-type IndexOptions array{ * background?: bool, * bits?: int, * default_language?: string, @@ -214,21 +215,21 @@ * unique?: bool, * weights?: array{string, int}, * } - * @psalm-type IndexMapping = array{ + * @phpstan-type IndexMapping array{ * keys: IndexKeys, * options: IndexOptions * } - * @psalm-type SearchIndexStoredSourceInclude = array{include: list} - * @psalm-type SearchIndexStoredSourceExclude = array{exclude: list} - * @psalm-type SearchIndexStoredSource = bool|SearchIndexStoredSourceInclude|SearchIndexStoredSourceExclude - * @psalm-type SearchIndexSynonym = array{ + * @phpstan-type SearchIndexStoredSourceInclude array{include: list} + * @phpstan-type SearchIndexStoredSourceExclude array{exclude: list} + * @phpstan-type SearchIndexStoredSource bool|SearchIndexStoredSourceInclude|SearchIndexStoredSourceExclude + * @phpstan-type SearchIndexSynonym array{ * analyzer: string, * name: string, * source: array{ * collection: string, * }, * } - * @psalm-type SearchIndexDefinition = array{ + * @phpstan-type SearchIndexDefinition array{ * mappings: array{ * dynamic?: bool, * fields?: array, @@ -239,13 +240,13 @@ * storedSource?: SearchIndexStoredSource, * synonyms?: list, * } - * @psalm-type SearchIndexMapping = array{ + * @phpstan-type SearchIndexMapping array{ * name: string, * definition: SearchIndexDefinition * } - * @psalm-type ShardKeys = array - * @psalm-type ShardOptions = array - * @psalm-type ShardKey = array{ + * @phpstan-type ShardKeys array + * @phpstan-type ShardOptions array + * @phpstan-type ShardKey array{ * keys?: ShardKeys, * options?: ShardOptions * } @@ -517,7 +518,7 @@ * READ-ONLY: The array of indexes for the document collection. * * @var array> - * @psalm-var array + * @phpstan-var array */ public $indexes = []; @@ -532,14 +533,14 @@ * READ-ONLY: Keys and options describing shard key. Only for sharded collections. * * @var array - * @psalm-var ShardKey + * @phpstan-var ShardKey */ public $shardKey = []; /** * Allows users to specify a validation schema for the collection. * - * @psalm-var array|object|null + * @phpstan-var array|object|null */ private array|object|null $validator = null; @@ -556,8 +557,7 @@ /** * READ-ONLY: The name of the document class. * - * @var string - * @psalm-var class-string + * @var class-string */ public $name; @@ -566,8 +566,7 @@ * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same * as {@link $documentName}. * - * @var string - * @psalm-var class-string + * @var class-string */ public $rootDocumentName; @@ -575,24 +574,21 @@ * The name of the custom repository class used for the document class. * (Optional). * - * @var string|null - * @psalm-var class-string|null + * @var class-string|null */ public $customRepositoryClassName; /** * READ-ONLY: The names of the parent classes (ancestors). * - * @var array - * @psalm-var list + * @var list */ public $parentClasses = []; /** * READ-ONLY: The names of all subclasses (descendants). * - * @var array - * @psalm-var list + * @var list */ public $subClasses = []; @@ -645,7 +641,7 @@ * document can have the id attribute, forming a composite key. * * @var array - * @psalm-var array + * @phpstan-var array */ public $fieldMappings = []; @@ -654,7 +650,7 @@ * Keys are field names and values are mapping definitions. * * @var array - * @psalm-var array + * @phpstan-var array */ public $associationMappings = []; @@ -680,8 +676,7 @@ * * @see discriminatorField * - * @var string|null - * @psalm-var class-string|null + * @var class-string|null */ public $discriminatorValue; @@ -693,7 +688,7 @@ * * @see discriminatorField * - * @psalm-var array + * @var array */ public $discriminatorMap = []; @@ -794,8 +789,7 @@ /** * The ReflectionClass instance of the mapped class. * - * @var ReflectionClass - * @psalm-var ReflectionClass + * @var ReflectionClass */ public $reflClass; @@ -806,6 +800,9 @@ */ public $isReadOnly; + /** READ ONLY: stores metadata about the time series collection */ + public ?TimeSeries $timeSeriesOptions = null; + private InstantiatorInterface $instantiator; private ReflectionService $reflectionService; @@ -817,7 +814,7 @@ * Initializes a new ClassMetadata instance that will hold the object-document mapping * metadata of the class with the given name. * - * @psalm-param class-string $documentName + * @param class-string $documentName */ public function __construct(string $documentName) { @@ -939,7 +936,7 @@ public function isInheritedField(string $fieldName): bool /** * Registers a custom repository class for the document class. * - * @psalm-param class-string|null $repositoryClassName + * @param class-string|null $repositoryClassName */ public function setCustomRepositoryClass(?string $repositoryClassName): void { @@ -1058,8 +1055,7 @@ public function setAlsoLoadMethods(array $methods): void * are only used to discern the hydration class and are not mapped to class * properties. * - * @param array|string|null $discriminatorField - * @psalm-param array{name?: string, fieldName?: string}|string|null $discriminatorField + * @param array{name?: string, fieldName?: string}|string|null $discriminatorField * * @throws MappingException If the discriminator field conflicts with the * "name" attribute of a mapped field. @@ -1175,8 +1171,8 @@ public function setDiscriminatorValue(string $value): void * Add a index for this Document. * * @param array $keys - * @psalm-param IndexKeys $keys - * @psalm-param IndexOptions $options + * @phpstan-param IndexKeys $keys + * @phpstan-param IndexOptions $options */ public function addIndex(array $keys, array $options = []): void { @@ -1206,7 +1202,7 @@ public function addIndex(array $keys, array $options = []): void /** * Returns the array of indexes for this Document. * - * @psalm-return array + * @phpstan-return array */ public function getIndexes(): array { @@ -1224,20 +1220,26 @@ public function hasIndexes(): bool /** * Add a search index for this Document. * - * @psalm-param SearchIndexDefinition $definition + * @phpstan-param SearchIndexDefinition $definition */ public function addSearchIndex(array $definition, ?string $name = null): void { + $name ??= self::DEFAULT_SEARCH_INDEX_NAME; + + if (empty($definition['mappings']['dynamic']) && empty($definition['mappings']['fields'])) { + throw MappingException::emptySearchIndexDefinition($this->name, $name); + } + $this->searchIndexes[] = [ 'definition' => $definition, - 'name' => $name ?? self::DEFAULT_SEARCH_INDEX_NAME, + 'name' => $name, ]; } /** * Returns the array of search indexes for this Document. * - * @psalm-return list + * @phpstan-return list */ public function getSearchIndexes(): array { @@ -1257,8 +1259,8 @@ public function hasSearchIndexes(): bool * * @param array $keys * @param array $options - * @psalm-param ShardKeys $keys - * @psalm-param ShardOptions $options + * @phpstan-param ShardKeys $keys + * @phpstan-param ShardOptions $options * * @throws MappingException */ @@ -1309,7 +1311,7 @@ public function setShardKey(array $keys, array $options = []): void ]; } - /** @psalm-return ShardKey */ + /** @phpstan-return ShardKey */ public function getShardKey(): array { return $this->shardKey; @@ -1323,19 +1325,13 @@ public function isSharded(): bool return $this->shardKey !== []; } - /** - * @return array|object|null - * @psalm-return array|object|null - */ + /** @return array|object|null */ public function getValidator() { return $this->validator; } - /** - * @param array|object|null $validator - * @psalm-param array|object|null $validator - */ + /** @param array|object|null $validator */ public function setValidator($validator): void { $this->validator = $validator; @@ -1449,7 +1445,7 @@ public function getReflectionProperty(string $name): ReflectionProperty return $this->reflFields[$name]; } - /** @psalm-return class-string */ + /** @return class-string */ public function getName(): string { return $this->name; @@ -1482,8 +1478,7 @@ public function getCollection(): string /** * Sets the collection this Document is mapped to. * - * @param array|string $name - * @psalm-param array{name: string, capped?: bool, size?: int, max?: int}|string $name + * @param array{name: string, capped?: bool, size?: int, max?: int}|string $name * * @throws InvalidArgumentException */ @@ -1583,7 +1578,7 @@ public function isMappedToCollection(): bool /** * Validates the storage strategy of a mapping for consistency * - * @psalm-param FieldMappingConfig $mapping + * @phpstan-param FieldMappingConfig $mapping * * @throws MappingException */ @@ -1639,7 +1634,7 @@ private function applyStorageStrategy(array &$mapping): void /** * Map a single embedded document. * - * @psalm-param FieldMappingConfig $mapping + * @phpstan-param FieldMappingConfig $mapping */ public function mapOneEmbedded(array $mapping): void { @@ -1652,7 +1647,7 @@ public function mapOneEmbedded(array $mapping): void /** * Map a collection of embedded documents. * - * @psalm-param FieldMappingConfig $mapping + * @phpstan-param FieldMappingConfig $mapping */ public function mapManyEmbedded(array $mapping): void { @@ -1665,7 +1660,7 @@ public function mapManyEmbedded(array $mapping): void /** * Map a single document reference. * - * @psalm-param FieldMappingConfig $mapping + * @phpstan-param FieldMappingConfig $mapping */ public function mapOneReference(array $mapping): void { @@ -1678,7 +1673,7 @@ public function mapOneReference(array $mapping): void /** * Map a collection of document references. * - * @psalm-param FieldMappingConfig $mapping + * @phpstan-param FieldMappingConfig $mapping */ public function mapManyReference(array $mapping): void { @@ -1694,7 +1689,7 @@ public function mapManyReference(array $mapping): void * * @internal * - * @psalm-param FieldMapping $fieldMapping + * @phpstan-param FieldMapping $fieldMapping */ public function addInheritedFieldMapping(array $fieldMapping): void { @@ -1713,7 +1708,7 @@ public function addInheritedFieldMapping(array $fieldMapping): void * * @internal * - * @psalm-param AssociationFieldMapping $mapping + * @phpstan-param AssociationFieldMapping $mapping * * @throws MappingException */ @@ -1924,7 +1919,7 @@ public function getFieldValue(object $document, string $field) /** * Gets the mapping of a field. * - * @psalm-return FieldMapping + * @phpstan-return FieldMapping * * @throws MappingException If the $fieldName is not found in the fieldMappings array. */ @@ -1940,7 +1935,7 @@ public function getFieldMapping(string $fieldName): array /** * Gets mappings of fields holding embedded document(s). * - * @psalm-return array + * @return array */ public function getEmbeddedFieldsMappings(): array { @@ -1954,7 +1949,7 @@ public function getEmbeddedFieldsMappings(): array * Gets the field mapping by its DB name. * E.g. it returns identifier's mapping when called with _id. * - * @psalm-return FieldMapping + * @phpstan-return FieldMapping * * @throws MappingException */ @@ -2029,8 +2024,7 @@ public function isInheritanceTypeCollectionPerClass(): bool /** * Sets the mapped subclasses of this class. * - * @param string[] $subclasses The names of all mapped subclasses. - * @psalm-param class-string[] $subclasses + * @param class-string[] $subclasses The names of all mapped subclasses. */ public function setSubclasses(array $subclasses): void { @@ -2044,8 +2038,7 @@ public function setSubclasses(array $subclasses): void * Assumes that the class names in the passed array are in the order: * directParent -> directParentParent -> directParentParentParent ... -> root. * - * @param string[] $classNames - * @psalm-param list $classNames + * @param list $classNames */ public function setParentClasses(array $classNames): void { @@ -2094,7 +2087,7 @@ public function isIdGeneratorNone(): bool * Sets the version field mapping used for versioning. Sets the default * value to use depending on the column type. * - * @psalm-param FieldMapping $mapping + * @phpstan-param FieldMapping $mapping * * @throws LockException */ @@ -2129,7 +2122,7 @@ public function setVersionField(?string $versionField): void * Sets the version field mapping used for versioning. Sets the default * value to use depending on the column type. * - * @psalm-param FieldMapping $mapping + * @phpstan-param FieldMapping $mapping * * @throws LockException */ @@ -2178,13 +2171,20 @@ public function isView(): bool return $this->isView; } - /** @psalm-param class-string $rootClass */ + /** @param class-string $rootClass */ public function markViewOf(string $rootClass): void { $this->isView = true; $this->rootClass = $rootClass; } + public function markAsTimeSeries(TimeSeries $options): void + { + $this->validateTimeSeriesOptions($options); + + $this->timeSeriesOptions = $options; + } + public function getFieldNames(): array { return array_keys($this->fieldMappings); @@ -2205,7 +2205,7 @@ public function getTypeOfField($fieldName): ?string /** * @param string $assocName * - * @psalm-return class-string|null + * @return class-string|null */ public function getAssociationTargetClass($assocName): ?string { @@ -2219,7 +2219,7 @@ public function getAssociationTargetClass($assocName): ?string /** * Retrieve the collectionClass associated with an association * - * @psalm-return class-string + * @return class-string */ public function getAssociationCollectionClass(string $assocName): string { @@ -2249,9 +2249,9 @@ public function getAssociationMappedByTargetField($assocName) /** * Map a field. * - * @psalm-param FieldMappingConfig $mapping + * @phpstan-param FieldMappingConfig $mapping * - * @psalm-return FieldMapping + * @phpstan-return FieldMapping * * @throws MappingException */ @@ -2538,6 +2538,7 @@ public function __sleep() 'idGenerator', 'indexes', 'shardKey', + 'timeSeriesOptions', ]; // The rest of the metadata is only serialized if necessary. @@ -2640,11 +2641,11 @@ public function __wakeup() /** * Creates a new instance of the mapped class, without invoking the constructor. * - * @psalm-return T + * @phpstan-return T */ public function newInstance(): object { - /** @psalm-var T */ + /** @phpstan-var T */ return $this->instantiator->instantiate($this->name); } @@ -2653,7 +2654,7 @@ private function isAllowedGridFSField(string $name): bool return in_array($name, self::ALLOWED_GRIDFS_FIELDS, true); } - /** @psalm-param FieldMapping $mapping */ + /** @phpstan-param FieldMapping $mapping */ private function typeRequirementsAreMet(array $mapping): void { if ($mapping['type'] === Type::DECIMAL128 && ! extension_loaded('bcmath')) { @@ -2661,7 +2662,7 @@ private function typeRequirementsAreMet(array $mapping): void } } - /** @psalm-param FieldMapping $mapping */ + /** @phpstan-param FieldMapping $mapping */ private function checkDuplicateMapping(array $mapping): void { if ($mapping['notSaved'] ?? false) { @@ -2697,7 +2698,7 @@ private function isTypedProperty(string $name): bool /** * Validates & completes the given field mapping based on typed property. * - * @psalm-param FieldMappingConfig $mapping + * @phpstan-param FieldMappingConfig $mapping * * @return FieldMappingConfig */ @@ -2751,7 +2752,7 @@ private function validateAndCompleteTypedFieldMapping(array $mapping): array /** * Validates & completes the basic mapping information based on typed property. * - * @psalm-param FieldMappingConfig $mapping + * @phpstan-param FieldMappingConfig $mapping * * @return FieldMappingConfig */ @@ -2769,4 +2770,15 @@ private function validateAndCompleteTypedManyAssociationMapping(array $mapping): return $mapping; } + + private function validateTimeSeriesOptions(TimeSeries $options): void + { + if (! $this->hasField($options->timeField)) { + throw MappingException::timeSeriesFieldNotFound($this->name, $options->timeField, 'time'); + } + + if ($options->metaField !== null && ! $this->hasField($options->metaField)) { + throw MappingException::timeSeriesFieldNotFound($this->name, $options->metaField, 'metadata'); + } + } } diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AttributeDriver.php b/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AttributeDriver.php index f0099c28ed..fb3b005431 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AttributeDriver.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AttributeDriver.php @@ -10,11 +10,13 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations\AbstractIndex; use Doctrine\ODM\MongoDB\Mapping\Annotations\SearchIndex; use Doctrine\ODM\MongoDB\Mapping\Annotations\ShardKey; +use Doctrine\ODM\MongoDB\Mapping\Annotations\TimeSeries; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\MappingException; use Doctrine\Persistence\Mapping\ClassMetadata as PersistenceClassMetadata; use Doctrine\Persistence\Mapping\Driver\ColocatedMappingDriver; use Doctrine\Persistence\Mapping\Driver\MappingDriver; +use MongoDB\BSON\Document; use MongoDB\Driver\Exception\UnexpectedValueException; use ReflectionClass; use ReflectionMethod; @@ -27,8 +29,6 @@ use function constant; use function count; use function is_array; -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\toPHP; use function trigger_deprecation; /** @@ -133,12 +133,12 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad } elseif ($attribute instanceof ODM\Validation) { if (isset($attribute->validator)) { try { - $validatorBson = fromJSON($attribute->validator); + $validatorBson = Document::fromJSON($attribute->validator); } catch (UnexpectedValueException $e) { throw MappingException::schemaValidationError($e->getCode(), $e->getMessage(), $className, 'validator'); } - $validator = toPHP($validatorBson, []); + $validator = $validatorBson->toPHP(); $metadata->setValidator($validator); } @@ -289,6 +289,12 @@ public function loadMetadataForClass($className, PersistenceClassMetadata $metad $this->setShardKey($metadata, $classAttributes[ShardKey::class]); } + // Mark as time series only after mapping all fields + if (isset($classAttributes[TimeSeries::class])) { + assert($classAttributes[TimeSeries::class] instanceof TimeSeries); + $metadata->markAsTimeSeries($classAttributes[TimeSeries::class]); + } + foreach ($reflClass->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { /* Filter for the declaring class only. Callbacks from parent * classes will already be registered. diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php b/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php index 6c288a2d99..8b093dd2b6 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php @@ -4,13 +4,16 @@ namespace Doctrine\ODM\MongoDB\Mapping\Driver; +use Doctrine\ODM\MongoDB\Mapping\Annotations\TimeSeries; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\MappingException; +use Doctrine\ODM\MongoDB\Mapping\TimeSeries\Granularity; use Doctrine\ODM\MongoDB\Utility\CollectionHelper; use Doctrine\Persistence\Mapping\Driver\FileDriver; use DOMDocument; use InvalidArgumentException; use LibXMLError; +use MongoDB\BSON\Document; use MongoDB\Driver\Exception\UnexpectedValueException; use SimpleXMLElement; @@ -31,8 +34,6 @@ use function libxml_clear_errors; use function libxml_get_errors; use function libxml_use_internal_errors; -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\toPHP; use function next; use function preg_match; use function simplexml_load_file; @@ -43,7 +44,7 @@ /** * XmlDriver is a metadata driver that enables mapping through XML files. * - * @psalm-import-type FieldMappingConfig from ClassMetadata + * @phpstan-import-type FieldMappingConfig from ClassMetadata * @template-extends FileDriver */ class XmlDriver extends FileDriver @@ -79,6 +80,7 @@ public function __construct($locator, $fileExtension = self::DEFAULT_FILE_EXTENS parent::__construct($locator, $fileExtension); } + // phpcs:disable SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed public function loadMetadataForClass($className, \Doctrine\Persistence\Mapping\ClassMetadata $metadata) { assert($metadata instanceof ClassMetadata); @@ -215,12 +217,12 @@ public function loadMetadataForClass($className, \Doctrine\Persistence\Mapping\C $validatorJson = (string) $xmlSchemaValidation; try { - $validatorBson = fromJSON($validatorJson); + $validatorBson = Document::fromJSON($validatorJson); } catch (UnexpectedValueException $e) { throw MappingException::schemaValidationError($e->getCode(), $e->getMessage(), $className, 'schema-validation'); } - $validator = toPHP($validatorBson, []); + $validator = $validatorBson->toPHP(); $metadata->setValidator($validator); } @@ -336,18 +338,37 @@ public function loadMetadataForClass($className, \Doctrine\Persistence\Mapping\C } } - if (! isset($xmlRoot->{'also-load-methods'})) { - return; + if (isset($xmlRoot->{'also-load-methods'})) { + foreach ($xmlRoot->{'also-load-methods'}->{'also-load-method'} as $alsoLoadMethod) { + $metadata->registerAlsoLoadMethod((string) $alsoLoadMethod['method'], (string) $alsoLoadMethod['field']); + } } - foreach ($xmlRoot->{'also-load-methods'}->{'also-load-method'} as $alsoLoadMethod) { - $metadata->registerAlsoLoadMethod((string) $alsoLoadMethod['method'], (string) $alsoLoadMethod['field']); + if (isset($xmlRoot->{'time-series'})) { + $attributes = $xmlRoot->{'time-series'}->attributes(); + + $metaField = isset($attributes['meta-field']) ? (string) $attributes['meta-field'] : null; + $granularity = isset($attributes['granularity']) ? Granularity::from((string) $attributes['granularity']) : null; + $expireAfterSeconds = isset($attributes['expire-after-seconds']) ? (int) $attributes['expire-after-seconds'] : null; + $bucketMaxSpanSeconds = isset($attributes['bucket-max-span-seconds']) ? (int) $attributes['bucket-max-span-seconds'] : null; + $bucketRoundingSeconds = isset($attributes['bucket-rounding-seconds']) ? (int) $attributes['bucket-rounding-seconds'] : null; + + $metadata->markAsTimeSeries(new TimeSeries( + timeField: (string) $attributes['time-field'], + metaField: $metaField, + granularity: $granularity, + expireAfterSeconds: $expireAfterSeconds, + bucketMaxSpanSeconds: $bucketMaxSpanSeconds, + bucketRoundingSeconds: $bucketRoundingSeconds, + )); } } + // phpcs:enable SlevomatCodingStandard.ControlStructures.EarlyExit.EarlyExitNotUsed + /** * @param ClassMetadata $class - * @psalm-param FieldMappingConfig $mapping + * @phpstan-param FieldMappingConfig $mapping */ private function addFieldMapping(ClassMetadata $class, array $mapping): void { @@ -801,7 +822,7 @@ private function setShardKey(ClassMetadata $class, SimpleXMLElement $xmlShardkey * * list($readPreference, $tags) = $this->transformReadPreference($xml->{read-preference}); * - * @psalm-return array{string, array>|null} + * @return array{string, array>|null} */ private function transformReadPreference(SimpleXMLElement $xmlReadPreference): array { diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php b/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php index af7d22a28f..e51e21c0af 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/MappingException.php @@ -291,4 +291,19 @@ public static function nonBackedEnumMapped(string $className, string $fieldName, $fieldName, )); } + + public static function emptySearchIndexDefinition(string $className, string $indexName): self + { + return new self(sprintf('%s search index "%s" must be dynamic or specify a field mapping', $className, $indexName)); + } + + public static function timeSeriesFieldNotFound(string $className, string $fieldName, string $field): self + { + return new self(sprintf( + 'The %s field %s::%s was not found', + $field, + $className, + $fieldName, + )); + } } diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/TimeSeries/Granularity.php b/lib/Doctrine/ODM/MongoDB/Mapping/TimeSeries/Granularity.php new file mode 100644 index 0000000000..3727686f63 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Mapping/TimeSeries/Granularity.php @@ -0,0 +1,12 @@ +|null $coll + * @param BaseCollection|null $coll + * @phpstan-param FieldMapping $mapping * - * @psalm-return PersistentCollectionInterface + * @return PersistentCollectionInterface */ public function create(DocumentManager $dm, array $mapping, ?BaseCollection $coll = null): PersistentCollectionInterface; } diff --git a/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionInterface.php b/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionInterface.php index e7012cbfd1..c71b5d17b7 100644 --- a/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionInterface.php +++ b/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionInterface.php @@ -15,8 +15,8 @@ * * @internal * - * @psalm-import-type FieldMapping from \Doctrine\ODM\MongoDB\Mapping\ClassMetadata - * @psalm-import-type Hints from UnitOfWork + * @phpstan-import-type FieldMapping from \Doctrine\ODM\MongoDB\Mapping\ClassMetadata + * @phpstan-import-type Hints from UnitOfWork * * @template TKey of array-key * @template T of object @@ -92,7 +92,7 @@ public function setDirty($dirty); * Sets the collection's owning document together with the AssociationMapping that * describes the association between the owner and the elements of the collection. * - * @psalm-param FieldMapping $mapping + * @phpstan-param FieldMapping $mapping * * @return void */ @@ -150,13 +150,13 @@ public function getOwner(): ?object; /** * @return array - * @psalm-return FieldMapping + * @phpstan-return FieldMapping */ public function getMapping(); /** * @return ClassMetadata - * @psalm-return ClassMetadata + * @phpstan-return ClassMetadata * * @throws MongoDBException */ diff --git a/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionTrait.php b/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionTrait.php index 1ccb2aab0f..79dae29566 100644 --- a/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionTrait.php +++ b/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionTrait.php @@ -28,8 +28,8 @@ /** * Trait with methods needed to implement PersistentCollectionInterface. * - * @psalm-import-type Hints from UnitOfWork - * @psalm-import-type FieldMapping from ClassMetadata + * @phpstan-import-type Hints from UnitOfWork + * @phpstan-import-type FieldMapping from ClassMetadata * @template TKey of array-key * @template T of object */ @@ -50,7 +50,7 @@ trait PersistentCollectionTrait /** * @var array|null - * @psalm-var FieldMapping|null + * @phpstan-var FieldMapping|null */ private ?array $mapping = null; @@ -93,7 +93,7 @@ trait PersistentCollectionTrait * Any hints to account for during reconstitution/lookup of the documents. * * @var array - * @psalm-var Hints + * @phpstan-var Hints */ private array $hints = []; @@ -129,7 +129,7 @@ public function initialize() return; } - /** @psalm-var array $newObjects */ + /** @var array $newObjects */ $newObjects = []; if ($this->isDirty) { @@ -363,7 +363,7 @@ public function exists(Closure $p) } /** - * @psalm-return (TMaybeContained is T ? TKey|false : false) + * @phpstan-return (TMaybeContained is T ? TKey|false : false) * * @template TMaybeContained */ @@ -422,7 +422,7 @@ public function isEmpty() /** * @return Traversable - * @psalm-return Traversable + * @phpstan-return Traversable */ #[ReturnTypeWillChange] public function getIterator() @@ -535,7 +535,7 @@ public function offsetExists($offset) * @param mixed $offset * * @return mixed - * @psalm-return T|null + * @phpstan-return T|null */ #[ReturnTypeWillChange] public function offsetGet($offset) @@ -656,7 +656,7 @@ private function doAdd($value, $arrayAccess) * @param mixed $offset * * @return bool|T|null - * @psalm-return ( + * @phpstan-return ( * $arrayAccess is false * ? T|null * : T|true|null @@ -728,9 +728,9 @@ private function needsSchedulingForSynchronization(): bool } /** - * @psalm-param Closure(TKey, T):bool $p + * @phpstan-param Closure(TKey, T):bool $p * - * @psalm-return T|null + * @phpstan-return T|null */ public function findFirst(Closure $p) { @@ -742,13 +742,13 @@ public function findFirst(Closure $p) } /** - * @psalm-param Closure(TReturn|TInitial|null, T):(TInitial|TReturn) $func - * @psalm-param TInitial|null $initial + * @phpstan-param Closure(TReturn|TInitial|null, T):(TInitial|TReturn) $func + * @phpstan-param TInitial|null $initial * - * @psalm-return TReturn|TInitial|null + * @phpstan-return TReturn|TInitial|null * - * @psalm-template TReturn - * @psalm-template TInitial + * @phpstan-template TReturn + * @phpstan-template TInitial */ public function reduce(Closure $func, $initial = null) { diff --git a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php index 780c0c6004..e2ddfcca7d 100644 --- a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php +++ b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php @@ -74,7 +74,7 @@ * * @template T of object * - * @psalm-type CommitOptions array{ + * @phpstan-type CommitOptions array{ * fsync?: bool, * safe?: int, * session?: ?Session, @@ -82,10 +82,10 @@ * withTransaction?: bool, * writeConcern?: WriteConcern * } - * @psalm-import-type Hints from UnitOfWork - * @psalm-import-type FieldMapping from ClassMetadata - * @psalm-import-type SortMeta from Sort - * @psalm-import-type SortShape from Sort + * @phpstan-import-type Hints from UnitOfWork + * @phpstan-import-type FieldMapping from ClassMetadata + * @phpstan-import-type SortMeta from Sort + * @phpstan-import-type SortShape from Sort */ final class DocumentPersister { @@ -111,7 +111,7 @@ final class DocumentPersister private CollectionPersister $cp; - /** @psalm-param ClassMetadata $class */ + /** @phpstan-param ClassMetadata $class */ public function __construct( private PersistenceBuilder $pb, private DocumentManager $dm, @@ -193,7 +193,7 @@ public function getClassMetadata(): ClassMetadata * * If no inserts are queued, invoking this method is a NOOP. * - * @psalm-param CommitOptions $options + * @phpstan-param CommitOptions $options * * @throws DriverException */ @@ -252,7 +252,7 @@ public function executeInserts(array $options = []): void * * If no upserts are queued, invoking this method is a NOOP. * - * @psalm-param CommitOptions $options + * @phpstan-param CommitOptions $options */ public function executeUpserts(array $options = []): void { @@ -351,7 +351,7 @@ private function executeUpsert(object $document, array $options): void /** * Updates the already persisted document if it has any new changesets. * - * @psalm-param CommitOptions $options + * @phpstan-param CommitOptions $options * * @throws LockException */ @@ -416,7 +416,7 @@ public function update(object $document, array $options = []): void /** * Removes document from mongo * - * @psalm-param CommitOptions $options + * @phpstan-param CommitOptions $options * * @throws LockException */ @@ -471,11 +471,11 @@ public function refresh(object $document): void * * @param array|scalar|ObjectId|null $criteria Query criteria * @param array>|null $sort - * @psalm-param SortShape|null $sort - * @psalm-param Hints $hints - * @psalm-param T|null $document + * @param T|null $document + * @phpstan-param SortShape|null $sort + * @phpstan-param Hints $hints * - * @psalm-return T|null + * @return T|null * * @throws LockException * @@ -638,11 +638,11 @@ public function unlock(object $document): void * @param array $result The query result. * @param object|null $document The document object to fill, if any. * @param array $hints Hints for document creation. - * @psalm-param Hints $hints - * @psalm-param T|null $document + * @phpstan-param Hints $hints + * @phpstan-param T|null $document * * @return object The filled and managed document object. - * @psalm-return T + * @phpstan-return T */ private function createDocument(array $result, ?object $document = null, array $hints = []): object { @@ -947,9 +947,9 @@ private function getSortDirection($sort) * names and changing direction strings to int. * * @param array> $fields - * @psalm-param SortShape $fields + * @phpstan-param SortShape $fields * - * @psalm-return array + * @phpstan-return array */ public function prepareSort(array $fields): array { @@ -1458,7 +1458,7 @@ private function hasQueryOperators($value): bool /** * Returns the list of discriminator values for the given ClassMetadata * - * @psalm-return list + * @return list */ private function getClassDiscriminatorValues(ClassMetadata $metadata): array { @@ -1561,9 +1561,9 @@ private function getQueryForDocument(object $document): array } /** - * @psalm-param CommitOptions $options + * @phpstan-param CommitOptions $options * - * @psalm-return CommitOptions + * @phpstan-return CommitOptions */ private function getWriteOptions(array $options = []): array { @@ -1605,9 +1605,9 @@ private function isInTransaction(array $options): bool } /** - * @psalm-param FieldMapping $mapping + * @phpstan-param FieldMapping $mapping * - * @psalm-return array * }> diff --git a/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php b/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php index 6ceaae37d8..80c257d66c 100644 --- a/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php +++ b/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php @@ -26,7 +26,7 @@ * * @internal * - * @psalm-import-type FieldMapping from ClassMetadata + * @phpstan-import-type FieldMapping from ClassMetadata */ final class PersistenceBuilder { @@ -322,7 +322,7 @@ public function prepareUpsertData($document) * simple reference, null may be returned. * * @param object $document - * @psalm-param FieldMapping $referenceMapping + * @phpstan-param FieldMapping $referenceMapping * * @return array|null */ @@ -347,7 +347,7 @@ public function prepareReferencedDocumentValue(array $referenceMapping, $documen * * @param object $embeddedDocument * @param bool $includeNestedCollections - * @psalm-param FieldMapping $embeddedMapping + * @phpstan-param FieldMapping $embeddedMapping * * @return array|object * @@ -462,7 +462,7 @@ public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDo * * @param object $document * @param bool $includeNestedCollections - * @psalm-param FieldMapping $mapping + * @phpstan-param FieldMapping $mapping * * @return mixed[]|object|null * diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php index 35b292281e..1540e40e9e 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/ProxyFactory.php @@ -17,9 +17,9 @@ public function generateProxyClasses(array $classes): int; * the given identifier. * * @param mixed $identifier - * @psalm-param ClassMetadata $metadata + * @phpstan-param ClassMetadata $metadata * - * @psalm-return T&GhostObjectInterface + * @return T&GhostObjectInterface * * @template T of object */ diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php index 5d7afba233..e13bbfb14f 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php @@ -38,9 +38,9 @@ public function __construct(DocumentManager $documentManager) /** * @param mixed $identifier - * @psalm-param ClassMetadata $metadata + * @phpstan-param ClassMetadata $metadata * - * @psalm-return T&GhostObjectInterface + * @return T&GhostObjectInterface * * @template T of object */ @@ -86,7 +86,7 @@ public function generateProxyClasses(array $classes): int * @param ClassMetadata $metadata * @param DocumentPersister $documentPersister * - * @psalm-return Closure( + * @phpstan-return Closure( * TDocument&GhostObjectInterface=, * string=, * array=, diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ClassNameResolver.php b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ClassNameResolver.php index 36ffaafc59..f869de0eb5 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ClassNameResolver.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ClassNameResolver.php @@ -10,7 +10,7 @@ interface ClassNameResolver /** * Gets the real class name of a class name that could be a proxy. * - * @psalm-param class-string $class + * @param class-string $class */ public function getRealClass(string $class): string; } diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ProxyManagerClassNameResolver.php b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ProxyManagerClassNameResolver.php index c281cfc120..08c9f704fe 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ProxyManagerClassNameResolver.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ProxyManagerClassNameResolver.php @@ -22,11 +22,11 @@ public function getRealClass(string $class): string } /** - * @psalm-param class-string|class-string> $className + * @param class-string|class-string> $className * - * @psalm-return class-string + * @return class-string * - * @psalm-template RealClassName of object + * @phpstan-template RealClassName of object */ public function resolveClassName(string $className): string { diff --git a/lib/Doctrine/ODM/MongoDB/Query/Builder.php b/lib/Doctrine/ODM/MongoDB/Query/Builder.php index 7e249a5a92..7be346ea88 100644 --- a/lib/Doctrine/ODM/MongoDB/Query/Builder.php +++ b/lib/Doctrine/ODM/MongoDB/Query/Builder.php @@ -30,8 +30,8 @@ /** * Query builder for ODM. * - * @psalm-import-type QueryShape from Query - * @psalm-import-type SortMetaKeywords from Sort + * @phpstan-import-type QueryShape from Query + * @phpstan-import-type SortMetaKeywords from Sort */ class Builder { @@ -85,7 +85,7 @@ class Builder /** * Array containing the query data. * - * @psalm-var QueryShape + * @phpstan-var QueryShape */ private array $query = ['type' => Query::TYPE_FIND]; @@ -1488,7 +1488,7 @@ public function sort($fieldName, $order = 1): self * * @see https://docs.mongodb.com/manual/reference/operator/projection/meta/#sort * - * @psalm-param SortMetaKeywords $metaDataKeyword + * @phpstan-param SortMetaKeywords $metaDataKeyword */ public function sortMeta(string $fieldName, string $metaDataKeyword): self { @@ -1596,7 +1596,7 @@ public function where($javascript): self /** * Get Discriminator Values * - * @psalm-param class-string[] $classNames + * @param class-string[] $classNames * * @return array * @@ -1620,10 +1620,7 @@ private function getDiscriminatorValues(array $classNames): array return $discriminatorValues; } - /** - * @param string[]|string|null $documentName an array of document names or just one. - * @psalm-param class-string[]|class-string|null $documentName - */ + /** @param class-string[]|class-string|null $documentName an array of document names or just one. */ private function setDocumentName($documentName): void { if (is_array($documentName)) { diff --git a/lib/Doctrine/ODM/MongoDB/Query/Expr.php b/lib/Doctrine/ODM/MongoDB/Query/Expr.php index ca96ca4fe5..f2e6bbb3e3 100644 --- a/lib/Doctrine/ODM/MongoDB/Query/Expr.php +++ b/lib/Doctrine/ODM/MongoDB/Query/Expr.php @@ -34,7 +34,7 @@ /** * Query expression builder for ODM. * - * @psalm-import-type FieldMapping from ClassMetadata + * @phpstan-import-type FieldMapping from ClassMetadata */ class Expr { diff --git a/lib/Doctrine/ODM/MongoDB/Query/Query.php b/lib/Doctrine/ODM/MongoDB/Query/Query.php index 267fb1134b..bf3372bb60 100644 --- a/lib/Doctrine/ODM/MongoDB/Query/Query.php +++ b/lib/Doctrine/ODM/MongoDB/Query/Query.php @@ -43,7 +43,7 @@ * ODM Query wraps the raw Doctrine MongoDB queries to add additional functionality * and to hydrate the raw arrays of data to Doctrine document objects. * - * @psalm-type QueryShape = array{ + * @phpstan-type QueryShape array{ * distinct?: string, * hint?: string|array, * limit?: int, @@ -60,8 +60,8 @@ * type: Query::TYPE_*, * upsert?: bool, * } - * @psalm-import-type Hints from UnitOfWork - * @psalm-import-type SortMeta from Sort + * @phpstan-import-type Hints from UnitOfWork + * @phpstan-import-type SortMeta from Sort */ final class Query implements IteratorAggregate { @@ -106,7 +106,7 @@ final class Query implements IteratorAggregate /** * Hints for UnitOfWork behavior. * - * @psalm-var Hints + * @phpstan-var Hints */ private array $unitOfWorkHints = []; @@ -118,7 +118,7 @@ final class Query implements IteratorAggregate /** * Query structure generated by the Builder class. * - * @psalm-var QueryShape + * @phpstan-var QueryShape */ private array $query; diff --git a/lib/Doctrine/ODM/MongoDB/Query/QueryExpressionVisitor.php b/lib/Doctrine/ODM/MongoDB/Query/QueryExpressionVisitor.php index 599ebb0ba2..83ff341825 100644 --- a/lib/Doctrine/ODM/MongoDB/Query/QueryExpressionVisitor.php +++ b/lib/Doctrine/ODM/MongoDB/Query/QueryExpressionVisitor.php @@ -38,7 +38,7 @@ final class QueryExpressionVisitor extends ExpressionVisitor /** * Map Criteria API composite types to query builder methods * - * @psalm-var array + * @var array */ private static array $compositeMethods = [ CompositeExpression::TYPE_AND => 'addAnd', diff --git a/lib/Doctrine/ODM/MongoDB/Query/ReferencePrimer.php b/lib/Doctrine/ODM/MongoDB/Query/ReferencePrimer.php index ab39bff8ac..ff3478cf8a 100644 --- a/lib/Doctrine/ODM/MongoDB/Query/ReferencePrimer.php +++ b/lib/Doctrine/ODM/MongoDB/Query/ReferencePrimer.php @@ -37,8 +37,8 @@ * * @internal * - * @psalm-import-type FieldMapping from ClassMetadata - * @psalm-import-type Hints from UnitOfWork + * @phpstan-import-type FieldMapping from ClassMetadata + * @phpstan-import-type Hints from UnitOfWork */ final class ReferencePrimer { @@ -70,7 +70,7 @@ public function __construct(DocumentManager $dm, UnitOfWork $uow) * @param string $fieldName Field name containing references to prime * @param array $hints UnitOfWork hints for priming queries * @param callable|null $primer Optional primer callable - * @psalm-param Hints $hints + * @phpstan-param Hints $hints * * @throws InvalidArgumentException If the mapped field is not the owning * side of a reference relationship. @@ -121,7 +121,7 @@ public function primeReferences(ClassMetadata $class, $documents, string $fieldN } } - /** @psalm-var class-string $className */ + /** @var class-string $className */ foreach ($groupedIds as $className => $ids) { $refClass = $this->dm->getClassMetadata($className); call_user_func($primer, $this->dm, $refClass, array_values($ids), $hints); @@ -216,7 +216,7 @@ private function parseDotSyntaxForPrimer(string $fieldName, ClassMetadata $class * infer the class of the referenced documents. * * @param PersistentCollectionInterface $persistentCollection - * @psalm-param array> $groupedIds + * @param array> $groupedIds */ private function addManyReferences(PersistentCollectionInterface $persistentCollection, array &$groupedIds): void { diff --git a/lib/Doctrine/ODM/MongoDB/Repository/AbstractRepositoryFactory.php b/lib/Doctrine/ODM/MongoDB/Repository/AbstractRepositoryFactory.php index 065ba26356..9d500dcd75 100644 --- a/lib/Doctrine/ODM/MongoDB/Repository/AbstractRepositoryFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Repository/AbstractRepositoryFactory.php @@ -26,9 +26,9 @@ abstract class AbstractRepositoryFactory implements RepositoryFactory private array $repositoryList = []; /** - * @psalm-param class-string $documentName + * @param class-string $documentName * - * @psalm-return DocumentRepository|GridFSRepository|ViewRepository + * @phpstan-return DocumentRepository|GridFSRepository|ViewRepository * * @template T of object */ @@ -51,10 +51,10 @@ public function getRepository(DocumentManager $documentManager, string $document /** * Create a new repository instance for a document class. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @return DocumentRepository|GridFSRepository|ViewRepository - * @psalm-return DocumentRepository|GridFSRepository|ViewRepository + * @phpstan-return DocumentRepository|GridFSRepository|ViewRepository * * @template T of object */ @@ -104,7 +104,7 @@ protected function createRepository(DocumentManager $documentManager, string $do * Instantiates requested repository. * * @param ClassMetadata $metadata - * @psalm-param class-string $repositoryClassName + * @param class-string $repositoryClassName * * @return ObjectRepository * diff --git a/lib/Doctrine/ODM/MongoDB/Repository/DefaultGridFSRepository.php b/lib/Doctrine/ODM/MongoDB/Repository/DefaultGridFSRepository.php index 586c56ab16..030fb112f8 100644 --- a/lib/Doctrine/ODM/MongoDB/Repository/DefaultGridFSRepository.php +++ b/lib/Doctrine/ODM/MongoDB/Repository/DefaultGridFSRepository.php @@ -86,7 +86,7 @@ private function getDocumentBucket(): Bucket } /** - * @psalm-return array{ + * @return array{ * _id?: mixed, * chunkSizeBytes?: int, * metadata?: object diff --git a/lib/Doctrine/ODM/MongoDB/Repository/DocumentRepository.php b/lib/Doctrine/ODM/MongoDB/Repository/DocumentRepository.php index 93cf4e453d..7fcc37429b 100644 --- a/lib/Doctrine/ODM/MongoDB/Repository/DocumentRepository.php +++ b/lib/Doctrine/ODM/MongoDB/Repository/DocumentRepository.php @@ -38,10 +38,7 @@ */ class DocumentRepository implements ObjectRepository, Selectable { - /** - * @var string - * @psalm-var class-string - */ + /** @var class-string */ protected $documentName; /** @var DocumentManager */ @@ -50,10 +47,7 @@ class DocumentRepository implements ObjectRepository, Selectable /** @var UnitOfWork */ protected $uow; - /** - * @var ClassMetadata - * @psalm-var ClassMetadata - */ + /** @var ClassMetadata */ protected $class; /** @@ -62,7 +56,7 @@ class DocumentRepository implements ObjectRepository, Selectable * @param DocumentManager $dm The DocumentManager to use. * @param UnitOfWork $uow The UnitOfWork to use. * @param ClassMetadata $classMetadata The class metadata. - * @psalm-param ClassMetadata $classMetadata The class metadata. + * @phpstan-param ClassMetadata $classMetadata The class metadata. */ public function __construct(DocumentManager $dm, UnitOfWork $uow, ClassMetadata $classMetadata) { @@ -111,7 +105,7 @@ public function clear(): void * * @param mixed $id Identifier. * - * @psalm-return T|null + * @return T|null * * @throws MappingException * @throws LockException @@ -190,15 +184,14 @@ public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $ * @param array|null $sort * @param array $criteria * - * @return object|null The object. - * @psalm-return T|null + * @return T|null The object. */ public function findOneBy(array $criteria, ?array $sort = null): ?object { return $this->getDocumentPersister()->load($criteria, null, [], 0, $sort); } - /** @psalm-return class-string */ + /** @return class-string */ public function getDocumentName(): string { return $this->documentName; @@ -209,13 +202,13 @@ public function getDocumentManager(): DocumentManager return $this->dm; } - /** @psalm-return ClassMetadata */ + /** @return ClassMetadata */ public function getClassMetadata(): ClassMetadata { return $this->class; } - /** @psalm-return class-string */ + /** @return class-string */ public function getClassName(): string { return $this->getDocumentName(); @@ -257,7 +250,7 @@ public function matching(Criteria $criteria): ArrayCollection return new ArrayCollection($iterator->toArray()); } - /** @psalm-return DocumentPersister */ + /** @return DocumentPersister */ protected function getDocumentPersister(): DocumentPersister { return $this->uow->getDocumentPersister($this->documentName); diff --git a/lib/Doctrine/ODM/MongoDB/Repository/RepositoryFactory.php b/lib/Doctrine/ODM/MongoDB/Repository/RepositoryFactory.php index ee5e681ffa..24036aa77d 100644 --- a/lib/Doctrine/ODM/MongoDB/Repository/RepositoryFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Repository/RepositoryFactory.php @@ -15,9 +15,9 @@ interface RepositoryFactory /** * Gets the repository for a document class. * - * @psalm-param class-string $documentName + * @param class-string $documentName * - * @psalm-return ObjectRepository + * @return ObjectRepository * * @template T of object */ diff --git a/lib/Doctrine/ODM/MongoDB/SchemaManager.php b/lib/Doctrine/ODM/MongoDB/SchemaManager.php index 160be23285..f7b6c2ad84 100644 --- a/lib/Doctrine/ODM/MongoDB/SchemaManager.php +++ b/lib/Doctrine/ODM/MongoDB/SchemaManager.php @@ -36,8 +36,8 @@ use function str_contains; /** - * @psalm-import-type IndexMapping from ClassMetadata - * @psalm-import-type IndexOptions from ClassMetadata + * @phpstan-import-type IndexMapping from ClassMetadata + * @phpstan-import-type IndexOptions from ClassMetadata */ final class SchemaManager { @@ -103,7 +103,7 @@ public function updateIndexes(?int $maxTimeMs = null, ?WriteConcern $writeConcer * Indexes that exist in MongoDB but not the document metadata will be * deleted. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws InvalidArgumentException */ @@ -150,9 +150,9 @@ public function updateDocumentIndexes(string $documentName, ?int $maxTimeMs = nu } /** - * @psalm-param class-string $documentName + * @param class-string $documentName * - * @psalm-return IndexMapping[] + * @phpstan-return IndexMapping[] */ public function getDocumentIndexes(string $documentName): array { @@ -162,10 +162,10 @@ public function getDocumentIndexes(string $documentName): array } /** - * @psalm-param class-string $documentName - * @psalm-param array $visited + * @param class-string $documentName + * @param array $visited * - * @psalm-return IndexMapping[] + * @phpstan-return IndexMapping[] */ private function doGetDocumentIndexes(string $documentName, array &$visited): array { @@ -233,7 +233,7 @@ private function doGetDocumentIndexes(string $documentName, array &$visited): ar /** * @param ClassMetadata $class * - * @psalm-return IndexMapping[] + * @phpstan-return IndexMapping[] */ private function prepareIndexes(ClassMetadata $class): array { @@ -266,7 +266,7 @@ private function prepareIndexes(ClassMetadata $class): array /** * Ensure the given document's indexes are created. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws InvalidArgumentException */ @@ -310,7 +310,7 @@ public function deleteIndexes(?int $maxTimeMs = null, ?WriteConcern $writeConcer /** * Delete the given document's indexes. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws InvalidArgumentException */ @@ -341,7 +341,7 @@ public function createSearchIndexes(): void /** * Create search indexes for the given document class. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws InvalidArgumentException */ @@ -396,7 +396,7 @@ public function updateSearchIndexes(): void * Search indexes will be updated using the definitions in the document * metadata. Search indexes not defined in the metadata will be deleted. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws InvalidArgumentException */ @@ -453,7 +453,7 @@ public function deleteSearchIndexes(): void /** * Delete search indexes for the given document class. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws InvalidArgumentException */ @@ -501,7 +501,7 @@ public function updateValidators(?int $maxTimeMs = null, ?WriteConcern $writeCon /** * Ensure collection validators are up to date for the mapped document class. * - * @psalm-param class-string $documentName + * @param class-string $documentName */ public function updateDocumentValidator(string $documentName, ?int $maxTimeMs = null, ?WriteConcern $writeConcern = null): void { @@ -570,7 +570,7 @@ public function createCollections(?int $maxTimeMs = null, ?WriteConcern $writeCo /** * Create the document collection for a mapped class. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws InvalidArgumentException */ @@ -625,6 +625,24 @@ public function createDocumentCollection(string $documentName, ?int $maxTimeMs = $options['validationLevel'] = $class->getValidationLevel(); } + if ($class->timeSeriesOptions !== null) { + $options['timeseries'] = array_filter( + [ + 'timeField' => $class->timeSeriesOptions->timeField, + 'metaField' => $class->timeSeriesOptions->metaField, + // ext-mongodb will automatically encode backed enums, so we can use the value directly here + 'granularity' => $class->timeSeriesOptions->granularity, + 'bucketMaxSpanSeconds' => $class->timeSeriesOptions->bucketMaxSpanSeconds, + 'bucketRoundingSeconds' => $class->timeSeriesOptions->bucketRoundingSeconds, + ], + static fn (mixed $value): bool => $value !== null, + ); + + if ($class->timeSeriesOptions->expireAfterSeconds) { + $options['expireAfterSeconds'] = $class->timeSeriesOptions->expireAfterSeconds; + } + } + $this->dm->getDocumentDatabase($documentName)->createCollection( $class->getCollection(), $this->getWriteOptions($maxTimeMs, $writeConcern, $options), @@ -648,7 +666,7 @@ public function dropCollections(?int $maxTimeMs = null, ?WriteConcern $writeConc /** * Drop the document collection for a mapped class. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws InvalidArgumentException */ @@ -687,7 +705,7 @@ public function dropDatabases(?int $maxTimeMs = null, ?WriteConcern $writeConcer /** * Drop the document database for a mapped class. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws InvalidArgumentException */ @@ -701,7 +719,7 @@ public function dropDocumentDatabase(string $documentName, ?int $maxTimeMs = nul $this->dm->getDocumentDatabase($documentName)->drop($this->getWriteOptions($maxTimeMs, $writeConcern)); } - /** @psalm-param IndexMapping $documentIndex */ + /** @phpstan-param IndexMapping $documentIndex */ public function isMongoIndexEquivalentToDocumentIndex(IndexInfo $mongoIndex, array $documentIndex): bool { return $this->isEquivalentIndexKeys($mongoIndex, $documentIndex) && $this->isEquivalentIndexOptions($mongoIndex, $documentIndex); @@ -711,7 +729,7 @@ public function isMongoIndexEquivalentToDocumentIndex(IndexInfo $mongoIndex, arr * Determine if the keys for a MongoDB index can be considered equivalent to * those for an index in class metadata. * - * @psalm-param IndexMapping $documentIndex + * @phpstan-param IndexMapping $documentIndex */ private function isEquivalentIndexKeys(IndexInfo $mongoIndex, array $documentIndex): bool { @@ -739,7 +757,7 @@ private function isEquivalentIndexKeys(IndexInfo $mongoIndex, array $documentInd $mongoIndexKeys == $documentIndexKeys; } - /** @psalm-param IndexMapping $documentIndex */ + /** @phpstan-param IndexMapping $documentIndex */ private function hasTextIndexesAtSamePosition(IndexInfo $mongoIndex, array $documentIndex): bool { $mongoIndexKeys = $mongoIndex['key']; @@ -775,7 +793,7 @@ private function hasTextIndexesAtSamePosition(IndexInfo $mongoIndex, array $docu * The background option is only relevant to index creation and is not * considered. * - * @psalm-param IndexMapping $documentIndex + * @phpstan-param IndexMapping $documentIndex */ private function isEquivalentIndexOptions(IndexInfo $mongoIndex, array $documentIndex): bool { @@ -840,8 +858,8 @@ private function isEquivalentIndexOptions(IndexInfo $mongoIndex, array $document * Options added to the ALLOWED_MISSING_INDEX_OPTIONS constant are ignored * and are expected to be checked later * - * @psalm-param IndexOptions $mongoIndexOptions - * @psalm-param IndexOptions $documentIndexOptions + * @phpstan-param IndexOptions $mongoIndexOptions + * @phpstan-param IndexOptions $documentIndexOptions */ private function indexOptionsAreMissing(array $mongoIndexOptions, array $documentIndexOptions): bool { @@ -856,7 +874,7 @@ private function indexOptionsAreMissing(array $mongoIndexOptions, array $documen * Determine if the text index weights for a MongoDB index can be considered * equivalent to those for an index in class metadata. * - * @psalm-param IndexMapping $documentIndex + * @phpstan-param IndexMapping $documentIndex */ private function isEquivalentTextIndexWeights(IndexInfo $mongoIndex, array $documentIndex): bool { @@ -904,7 +922,7 @@ public function ensureSharding(?WriteConcern $writeConcern = null): void /** * Ensure sharding for collection by document name. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws MongoDBException */ @@ -931,7 +949,7 @@ public function ensureDocumentSharding(string $documentName, ?WriteConcern $writ /** * Enable sharding for database which contains documents with given name. * - * @psalm-param class-string $documentName + * @param class-string $documentName * * @throws MongoDBException */ @@ -952,7 +970,7 @@ public function enableShardingForDbByDocumentName(string $documentName): void } } - /** @psalm-param class-string $documentName */ + /** @param class-string $documentName */ private function runShardCollectionCommand(string $documentName, ?WriteConcern $writeConcern = null): void { $class = $this->dm->getClassMetadata($documentName); @@ -1023,7 +1041,7 @@ private function ensureFilesIndex(ClassMetadata $class, ?int $maxTimeMs = null, $filesCollection->createIndex(self::GRIDFS_CHUNKS_COLLECTION_INDEX, $this->getWriteOptions($maxTimeMs, $writeConcern, ['background' => $background])); } - /** @psalm-param class-string $documentName */ + /** @param class-string $documentName */ private function collectionIsSharded(string $documentName): bool { $class = $this->dm->getClassMetadata($documentName); diff --git a/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/AbstractCommand.php b/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/AbstractCommand.php index 2bd59a14e0..8b6481b803 100644 --- a/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/AbstractCommand.php +++ b/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/AbstractCommand.php @@ -103,7 +103,7 @@ protected function processSearchIndex(SchemaManager $sm): void } /** - * @psalm-param class-string $document + * @param class-string $document * * @throws BadMethodCallException */ diff --git a/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/ShardCommand.php b/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/ShardCommand.php index 6394064d49..25bc6ba07f 100644 --- a/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/ShardCommand.php +++ b/lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/ShardCommand.php @@ -53,7 +53,7 @@ private function doExecute(InputInterface $input, OutputInterface $output): int return $isErrored ? 255 : 0; } - /** @psalm-param class-string $document */ + /** @param class-string $document */ private function processDocumentSharding(SchemaManager $sm, string $document, ?WriteConcern $writeConcern = null): void { $sm->ensureDocumentSharding($document, $writeConcern); diff --git a/lib/Doctrine/ODM/MongoDB/Tools/ResolveTargetDocumentListener.php b/lib/Doctrine/ODM/MongoDB/Tools/ResolveTargetDocumentListener.php index 45269dcc60..d2e74fcee2 100644 --- a/lib/Doctrine/ODM/MongoDB/Tools/ResolveTargetDocumentListener.php +++ b/lib/Doctrine/ODM/MongoDB/Tools/ResolveTargetDocumentListener.php @@ -20,11 +20,11 @@ * * Mechanism to overwrite document interfaces or classes specified as association targets. * - * @psalm-import-type AssociationFieldMapping from ClassMetadata + * @phpstan-import-type AssociationFieldMapping from ClassMetadata */ class ResolveTargetDocumentListener implements EventSubscriber { - /** @psalm-var array */ + /** @var array */ private array $resolveTargetDocuments = []; public function getSubscribedEvents() @@ -38,7 +38,7 @@ public function getSubscribedEvents() /** * Add a target-document class name to resolve to a new class name. * - * @psalm-param array{targetDocument?: class-string} $mapping + * @param array{targetDocument?: class-string} $mapping */ public function addResolveTargetDocument(string $originalDocument, string $newDocument, array $mapping): void { @@ -46,7 +46,7 @@ public function addResolveTargetDocument(string $originalDocument, string $newDo $this->resolveTargetDocuments[$this->getRealClassName($originalDocument)] = $mapping; } - /** @psalm-return class-string */ + /** @return class-string */ private function getRealClassName(string $className): string { return ltrim($className, '\\'); @@ -88,7 +88,7 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $args): void /** * @param ClassMetadata $classMetadata - * @psalm-param AssociationFieldMapping $mapping + * @phpstan-param AssociationFieldMapping $mapping */ private function remapAssociation(ClassMetadata $classMetadata, array $mapping): void { diff --git a/lib/Doctrine/ODM/MongoDB/Types/Type.php b/lib/Doctrine/ODM/MongoDB/Types/Type.php index 90a795bbe4..b8b308c11e 100644 --- a/lib/Doctrine/ODM/MongoDB/Types/Type.php +++ b/lib/Doctrine/ODM/MongoDB/Types/Type.php @@ -57,10 +57,7 @@ abstract class Type /** @var Type[] Map of already instantiated type objects. One instance per type (flyweight). */ private static array $typeObjects = []; - /** - * @var string[] The map of supported doctrine mapping types. - * @psalm-var array - */ + /** @var array The map of supported doctrine mapping types. */ private static array $typesMap = [ self::ID => Types\IdType::class, self::INTID => Types\IntIdType::class, @@ -203,7 +200,7 @@ public static function convertPHPToDatabaseValue($value) /** * Adds a custom type to the type map. * - * @psalm-param class-string $className + * @param class-string $className * * @throws MappingException * @@ -231,7 +228,7 @@ public static function hasType(string $name): bool /** * Overrides an already defined type to use a different implementation. * - * @psalm-param class-string $className + * @param class-string $className * * @throws MappingException * @@ -250,7 +247,7 @@ public static function overrideType(string $name, string $className): void * Get the types array map which holds all registered types and the corresponding * type class * - * @psalm-return array + * @phpstan-return array */ public static function getTypesMap(): array { diff --git a/lib/Doctrine/ODM/MongoDB/UnitOfWork.php b/lib/Doctrine/ODM/MongoDB/UnitOfWork.php index eb9591f996..f7a728693e 100644 --- a/lib/Doctrine/ODM/MongoDB/UnitOfWork.php +++ b/lib/Doctrine/ODM/MongoDB/UnitOfWork.php @@ -56,14 +56,14 @@ * "object-level" transaction and for writing out changes to the database * in the correct order. * - * @psalm-import-type FieldMapping from ClassMetadata - * @psalm-import-type AssociationFieldMapping from ClassMetadata - * @psalm-type ChangeSet = array{ + * @phpstan-import-type FieldMapping from ClassMetadata + * @phpstan-import-type AssociationFieldMapping from ClassMetadata + * @phpstan-type ChangeSet array{ * 0: mixed, * 1: mixed * } - * @psalm-type Hints = array - * @psalm-type CommitOptions array{ + * @phpstan-type Hints array + * @phpstan-type CommitOptions array{ * fsync?: bool, * safe?: int, * w?: int, @@ -118,7 +118,7 @@ final class UnitOfWork implements PropertyChangedListener * Since all classes in a hierarchy must share the same identifier set, * we always take the root class name of the hierarchy. * - * @psalm-var array> + * @var array> */ private array $identityMap = []; @@ -147,7 +147,7 @@ final class UnitOfWork implements PropertyChangedListener * Map of document changes. Keys are object ids (spl_object_hash). * Filled at the beginning of a commit of the UnitOfWork and cleaned at the end. * - * @psalm-var array> + * @var array> */ private array $documentChangeSets = []; @@ -155,7 +155,7 @@ final class UnitOfWork implements PropertyChangedListener * The (cached) states of any known documents. * Keys are object ids (spl_object_hash). * - * @psalm-var array + * @var array */ private array $documentStates = []; @@ -166,7 +166,7 @@ final class UnitOfWork implements PropertyChangedListener * object hash. This is only used for documents with a change tracking * policy of DEFERRED_EXPLICIT. * - * @psalm-var array> + * @var array> */ private array $scheduledForSynchronization = []; @@ -201,21 +201,21 @@ final class UnitOfWork implements PropertyChangedListener /** * All pending collection deletions. * - * @psalm-var array> + * @var array> */ private array $scheduledCollectionDeletions = []; /** * All pending collection updates. * - * @psalm-var array> + * @var array> */ private array $scheduledCollectionUpdates = []; /** * A list of documents related to collections scheduled for update or deletion * - * @psalm-var array>> + * @var array>> */ private array $hasScheduledCollections = []; @@ -224,7 +224,7 @@ final class UnitOfWork implements PropertyChangedListener * At the end of the UnitOfWork all these collections will make new snapshots * of their data. * - * @psalm-var array>> + * @var array>> */ private array $visitedCollections = []; @@ -253,7 +253,7 @@ final class UnitOfWork implements PropertyChangedListener /** * The document persister instances used to persist document instances. * - * @psalm-var array + * @var array */ private array $persisters = []; @@ -270,7 +270,7 @@ final class UnitOfWork implements PropertyChangedListener /** * Array of parent associations between embedded documents. * - * @psalm-var array + * @var array */ private array $parentAssociations = []; @@ -321,7 +321,7 @@ public function getPersistenceBuilder(): PersistenceBuilder * * @internal * - * @psalm-param FieldMapping $mapping + * @phpstan-param FieldMapping $mapping */ public function setParentAssociation(object $document, array $mapping, ?object $parent, string $propertyPath): void { @@ -337,7 +337,7 @@ public function setParentAssociation(object $document, array $mapping, ?object $ * list($mapping, $parent, $propertyPath) = $this->getParentAssociation($embeddedDocument); * * - * @psalm-return array{0: AssociationFieldMapping, 1: object|null, 2: string}|null + * @phpstan-return array{0: AssociationFieldMapping, 1: object|null, 2: string}|null */ public function getParentAssociation(object $document): ?array { @@ -349,9 +349,9 @@ public function getParentAssociation(object $document): ?array /** * Get the document persister instance for the given document name * - * @psalm-param class-string $documentName + * @param class-string $documentName * - * @psalm-return Persisters\DocumentPersister + * @return Persisters\DocumentPersister * * @template T of object */ @@ -363,7 +363,6 @@ public function getDocumentPersister(string $documentName): Persisters\DocumentP $this->persisters[$documentName] = new Persisters\DocumentPersister($pb, $this->dm, $this, $this->hydratorFactory, $class); } - /** @psalm-var Persisters\DocumentPersister */ return $this->persisters[$documentName]; } @@ -385,8 +384,8 @@ public function getCollectionPersister(): CollectionPersister * * @internal * - * @psalm-param class-string $documentName - * @psalm-param Persisters\DocumentPersister $persister + * @param class-string $documentName + * @phpstan-param Persisters\DocumentPersister $persister * * @template T of object */ @@ -407,7 +406,7 @@ public function setDocumentPersister(string $documentName, Persisters\DocumentPe * 3) All document deletions * * @param array $options Array of options to be used with batchInsert(), update() and remove() - * @psalm-param CommitOptions $options + * @phpstan-param CommitOptions $options */ public function commit(array $options = []): void { @@ -504,7 +503,7 @@ function (Session $session) use ($options): void { * * @param array $documents * - * @psalm-return array, 1: array}> + * @phpstan-return array, 1: array}> */ private function getClassesForCommitAction(array $documents, bool $includeEmbedded = false): array { @@ -583,7 +582,7 @@ private function computeScheduleUpsertsChangeSets(): void * Gets the changeset for a document. * * @return array array('property' => array(0 => mixed, 1 => mixed)) - * @psalm-return array + * @phpstan-return array */ public function getDocumentChangeSet(object $document): array { @@ -597,7 +596,7 @@ public function getDocumentChangeSet(object $document): array * * @internal * - * @psalm-param array $changeset + * @param array $changeset */ public function setDocumentChangeSet(object $document, array $changeset): void { @@ -667,8 +666,8 @@ public function getDocumentActualData(object $document): array * and any changes to its properties are detected, then a reference to the document is stored * there to mark it for an update. * - * @psalm-param ClassMetadata $class - * @psalm-param T $document + * @phpstan-param ClassMetadata $class + * @phpstan-param T $document * * @template T of object */ @@ -689,8 +688,8 @@ public function computeChangeSet(ClassMetadata $class, object $document): void /** * Used to do the common work of computeChangeSet and recomputeSingleDocumentChangeSet * - * @psalm-param ClassMetadata $class - * @psalm-param T $document + * @phpstan-param ClassMetadata $class + * @phpstan-param T $document * * @template T of object */ @@ -961,7 +960,7 @@ public function computeChangeSets(): void * Computes the changes of an association. * * @param mixed $value The value of the association. - * @psalm-param AssociationFieldMapping $assoc + * @phpstan-param AssociationFieldMapping $assoc * * @throws InvalidArgumentException */ @@ -1083,8 +1082,8 @@ private function computeAssociationChanges(object $parentDocument, array $assoc, * because this method is invoked during a commit cycle then the change sets are added. * whereby changes detected in this method prevail. * - * @psalm-param ClassMetadata $class - * @psalm-param T $document + * @phpstan-param ClassMetadata $class + * @phpstan-param T $document * * @throws InvalidArgumentException If the passed document is not MANAGED. * @@ -1111,8 +1110,8 @@ public function recomputeSingleDocumentChangeSet(ClassMetadata $class, object $d } /** - * @psalm-param ClassMetadata $class - * @psalm-param T $document + * @phpstan-param ClassMetadata $class + * @phpstan-param T $document * * @throws InvalidArgumentException If there is something wrong with document's identifier. * @@ -1165,9 +1164,9 @@ private function persistNew(ClassMetadata $class, object $document): void /** * Executes all document insertions for documents of the specified type. * - * @psalm-param ClassMetadata $class - * @psalm-param T[] $documents - * @psalm-param CommitOptions $options + * @phpstan-param ClassMetadata $class + * @phpstan-param T[] $documents + * @phpstan-param CommitOptions $options * * @template T of object */ @@ -1189,9 +1188,9 @@ private function executeInserts(ClassMetadata $class, array $documents, array $o /** * Executes all document upserts for documents of the specified type. * - * @psalm-param ClassMetadata $class - * @psalm-param T[] $documents - * @psalm-param CommitOptions $options + * @phpstan-param ClassMetadata $class + * @phpstan-param T[] $documents + * @phpstan-param CommitOptions $options * * @template T of object */ @@ -1213,9 +1212,9 @@ private function executeUpserts(ClassMetadata $class, array $documents, array $o /** * Executes all document updates for documents of the specified type. * - * @psalm-param ClassMetadata $class - * @psalm-param T[] $documents - * @psalm-param CommitOptions $options + * @phpstan-param ClassMetadata $class + * @phpstan-param T[] $documents + * @phpstan-param CommitOptions $options * * @template T of object */ @@ -1242,9 +1241,9 @@ private function executeUpdates(ClassMetadata $class, array $documents, array $o /** * Executes all document deletions for documents of the specified type. * - * @psalm-param ClassMetadata $class - * @psalm-param T[] $documents - * @psalm-param CommitOptions $options + * @phpstan-param ClassMetadata $class + * @phpstan-param T[] $documents + * @phpstan-param CommitOptions $options * * @template T of object */ @@ -1288,8 +1287,8 @@ private function executeDeletions(ClassMetadata $class, array $documents, array * * @internal * - * @psalm-param ClassMetadata $class - * @psalm-param T $document + * @phpstan-param ClassMetadata $class + * @phpstan-param T $document * * @throws InvalidArgumentException * @@ -1326,8 +1325,8 @@ public function scheduleForInsert(ClassMetadata $class, object $document): void * * @internal * - * @psalm-param ClassMetadata $class - * @psalm-param T $document + * @phpstan-param ClassMetadata $class + * @phpstan-param T $document * * @throws InvalidArgumentException * @@ -1614,15 +1613,13 @@ public function removeFromIdentityMap(object $document): bool * @internal * * @param mixed $id Document identifier - * @psalm-param ClassMetadata $class + * @phpstan-param ClassMetadata $class * - * @psalm-return T + * @phpstan-return T * * @throws InvalidArgumentException If the class does not have an identifier. * * @template T of object - * - * @psalm-suppress InvalidReturnStatement, InvalidReturnType because of the inability of defining a generic property map */ public function getById($id, ClassMetadata $class): object { @@ -1642,16 +1639,16 @@ public function getById($id, ClassMetadata $class): object * @internal * * @param mixed $id Document identifier - * @psalm-param ClassMetadata $class + * @phpstan-param ClassMetadata $class * * @return mixed The found document or FALSE. - * @psalm-return T|false + * @phpstan-return T|false * * @throws InvalidArgumentException If the class does not have an identifier. * * @template T of object * - * @psalm-suppress InvalidReturnStatement, InvalidReturnType because of the inability of defining a generic property map + * @ phpstan-suppress InvalidReturnStatement, InvalidReturnType because of the inability of defining a generic property map */ public function tryGetById($id, ClassMetadata $class) { @@ -1874,7 +1871,7 @@ public function merge(object $document): object * Executes a merge operation on a document. * * @param array $visited - * @psalm-param AssociationFieldMapping|null $assoc + * @phpstan-param AssociationFieldMapping|null $assoc * * @throws InvalidArgumentException If the entity instance is NEW. * @throws LockException If the document uses optimistic locking through a @@ -2463,11 +2460,11 @@ public function unscheduleOrphanRemoval(object $document): void * 3) NOP if state is OK * Returned collection should be used from now on (only important with 2nd point) * - * @psalm-param PersistentCollectionInterface $coll - * @psalm-param T $document - * @psalm-param ClassMetadata $class + * @phpstan-param PersistentCollectionInterface $coll + * @phpstan-param T $document + * @phpstan-param ClassMetadata $class * - * @psalm-return PersistentCollectionInterface + * @phpstan-return PersistentCollectionInterface * * @template T of object */ @@ -2497,7 +2494,7 @@ private function fixPersistentCollectionOwnership(PersistentCollectionInterface * * @internal * - * @psalm-param PersistentCollectionInterface $coll + * @phpstan-param PersistentCollectionInterface $coll */ public function scheduleCollectionDeletion(PersistentCollectionInterface $coll): void { @@ -2516,7 +2513,7 @@ public function scheduleCollectionDeletion(PersistentCollectionInterface $coll): * * @internal * - * @psalm-param PersistentCollectionInterface $coll + * @phpstan-param PersistentCollectionInterface $coll */ public function isCollectionScheduledForDeletion(PersistentCollectionInterface $coll): bool { @@ -2528,7 +2525,7 @@ public function isCollectionScheduledForDeletion(PersistentCollectionInterface $ * * @internal * - * @psalm-param PersistentCollectionInterface $coll + * @phpstan-param PersistentCollectionInterface $coll */ public function unscheduleCollectionDeletion(PersistentCollectionInterface $coll): void { @@ -2551,7 +2548,7 @@ public function unscheduleCollectionDeletion(PersistentCollectionInterface $coll * * @internal * - * @psalm-param PersistentCollectionInterface $coll + * @phpstan-param PersistentCollectionInterface $coll */ public function scheduleCollectionUpdate(PersistentCollectionInterface $coll): void { @@ -2577,7 +2574,7 @@ public function scheduleCollectionUpdate(PersistentCollectionInterface $coll): v * * @internal * - * @psalm-param PersistentCollectionInterface $coll + * @phpstan-param PersistentCollectionInterface $coll */ public function unscheduleCollectionUpdate(PersistentCollectionInterface $coll): void { @@ -2600,7 +2597,7 @@ public function unscheduleCollectionUpdate(PersistentCollectionInterface $coll): * * @internal * - * @psalm-param PersistentCollectionInterface $coll + * @phpstan-param PersistentCollectionInterface $coll */ public function isCollectionScheduledForUpdate(PersistentCollectionInterface $coll): bool { @@ -2614,7 +2611,7 @@ public function isCollectionScheduledForUpdate(PersistentCollectionInterface $co * @internal * * @return PersistentCollectionInterface[] - * @psalm-return array> + * @phpstan-return array> */ public function getVisitedCollections(object $document): array { @@ -2628,8 +2625,7 @@ public function getVisitedCollections(object $document): array * * @internal * - * @return PersistentCollectionInterface[] - * @psalm-return array> + * @return array> */ public function getScheduledCollections(object $document): array { @@ -2660,7 +2656,7 @@ public function hasScheduledCollections(object $document): bool * unscheduled and atomic one is scheduled for update instead. This makes * calculating update data way easier. * - * @psalm-param PersistentCollectionInterface $coll + * @phpstan-param PersistentCollectionInterface $coll */ private function scheduleCollectionOwner(PersistentCollectionInterface $coll): void { @@ -2728,12 +2724,12 @@ public function getOwningDocument(object $document): object /** * Creates a document. Used for reconstitution of documents during hydration. * - * @psalm-param class-string $className - * @psalm-param array $data - * @psalm-param T|null $document - * @psalm-param Hints $hints + * @param class-string $className + * @param array $data + * @param T|null $document + * @phpstan-param Hints $hints * - * @psalm-return T + * @return T * * @template T of object */ @@ -2750,7 +2746,7 @@ public function getOrCreateDocument(string $className, array $data, array &$hint } if ($discriminatorValue !== null) { - /** @psalm-var class-string $className */ + /** @var class-string $className */ $className = $class->discriminatorMap[$discriminatorValue] ?? $discriminatorValue; $class = $this->dm->getClassMetadata($className); @@ -2759,7 +2755,7 @@ public function getOrCreateDocument(string $className, array $data, array &$hint } if (! empty($hints[Query::HINT_READ_ONLY])) { - /** @psalm-var T $document */ + /** @phpstan-var T $document */ $document = $class->newInstance(); $this->hydratorFactory->hydrate($document, $data, $hints); @@ -2777,7 +2773,7 @@ public function getOrCreateDocument(string $className, array $data, array &$hint $oid = null; if ($isManagedObject) { - /** @psalm-var T $document */ + /** @phpstan-var T $document */ $document = $this->identityMap[$class->name][$serializedId]; $oid = spl_object_hash($document); if ($this->isUninitializedObject($document)) { @@ -2796,7 +2792,7 @@ public function getOrCreateDocument(string $className, array $data, array &$hint } } else { if ($document === null) { - /** @psalm-var T $document */ + /** @phpstan-var T $document */ $document = $class->newInstance(); } @@ -2822,7 +2818,7 @@ public function getOrCreateDocument(string $className, array $data, array &$hint * * @internal * - * @psalm-param PersistentCollectionInterface $collection + * @phpstan-param PersistentCollectionInterface $collection */ public function loadCollection(PersistentCollectionInterface $collection): void { @@ -2839,7 +2835,7 @@ public function loadCollection(PersistentCollectionInterface $collection): void * * @internal * - * @psalm-return array> + * @return array> */ public function getIdentityMap(): array { @@ -2993,7 +2989,7 @@ public function propertyChanged($sender, $propertyName, $oldValue, $newValue) /** * Gets the currently scheduled document insertions in this UnitOfWork. * - * @psalm-return array + * @return array */ public function getScheduledDocumentInsertions(): array { @@ -3003,7 +2999,7 @@ public function getScheduledDocumentInsertions(): array /** * Gets the currently scheduled document upserts in this UnitOfWork. * - * @psalm-return array + * @return array */ public function getScheduledDocumentUpserts(): array { @@ -3013,7 +3009,7 @@ public function getScheduledDocumentUpserts(): array /** * Gets the currently scheduled document updates in this UnitOfWork. * - * @psalm-return array + * @return array */ public function getScheduledDocumentUpdates(): array { @@ -3023,7 +3019,7 @@ public function getScheduledDocumentUpdates(): array /** * Gets the currently scheduled document deletions in this UnitOfWork. * - * @psalm-return array + * @return array */ public function getScheduledDocumentDeletions(): array { @@ -3035,7 +3031,7 @@ public function getScheduledDocumentDeletions(): array * * @internal * - * @psalm-return array> + * @return array> */ public function getScheduledCollectionDeletions(): array { @@ -3047,7 +3043,7 @@ public function getScheduledCollectionDeletions(): array * * @internal * - * @psalm-return array> + * @return array> */ public function getScheduledCollectionUpdates(): array { @@ -3096,7 +3092,7 @@ private function objToStr(object $obj): string return method_exists($obj, '__toString') ? (string) $obj : $obj::class . '@' . spl_object_hash($obj); } - /** @psalm-param CommitOptions $options */ + /** @phpstan-param CommitOptions $options */ private function doCommit(array $options): void { foreach ($this->getClassesForCommitAction($this->scheduledDocumentUpserts) as $classAndDocuments) { @@ -3120,7 +3116,7 @@ private function doCommit(array $options): void } } - /** @psalm-param CommitOptions $options */ + /** @phpstan-param CommitOptions $options */ private function useTransaction(array $options): bool { if (isset($options['withTransaction'])) { @@ -3130,7 +3126,7 @@ private function useTransaction(array $options): bool return $this->dm->getConfiguration()->isTransactionalFlushEnabled(); } - /** @psalm-param CommitOptions $options */ + /** @phpstan-param CommitOptions $options */ private function getTransactionOptions(array $options): array { return array_intersect_key( diff --git a/lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php b/lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php index a44ff8f254..a6d26cc2fc 100644 --- a/lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php +++ b/lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php @@ -63,7 +63,7 @@ public function documentNotFound(object $proxy, $id): bool /** * Dispatches postCollectionLoad event. * - * @psalm-param PersistentCollectionInterface $coll + * @phpstan-param PersistentCollectionInterface $coll */ public function postCollectionLoad(PersistentCollectionInterface $coll): void { @@ -74,8 +74,8 @@ public function postCollectionLoad(PersistentCollectionInterface $coll): void /** * Invokes postPersist callbacks and events for given document cascading them to embedded documents as well. * - * @psalm-param ClassMetadata $class - * @psalm-param T $document + * @phpstan-param ClassMetadata $class + * @phpstan-param T $document * * @template T of object */ @@ -95,8 +95,8 @@ public function postPersist(ClassMetadata $class, object $document, ?Session $se /** * Invokes postRemove callbacks and events for given document. * - * @psalm-param ClassMetadata $class - * @psalm-param T $document + * @phpstan-param ClassMetadata $class + * @phpstan-param T $document * * @template T of object */ @@ -116,8 +116,8 @@ public function postRemove(ClassMetadata $class, object $document, ?Session $ses * Invokes postUpdate callbacks and events for given document. The same will be done for embedded documents owned * by given document unless they were new in which case postPersist callbacks and events will be dispatched. * - * @psalm-param ClassMetadata $class - * @psalm-param T $document + * @phpstan-param ClassMetadata $class + * @phpstan-param T $document * * @template T of object */ diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index ae58dfd951..e2c6ab2d83 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -956,12 +956,12 @@ parameters: path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataLoadEventTest.php - - message: "#^Parameter \\#1 \\$mapping of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadata\\\\:\\:mapField\\(\\) expects array\\{type\\?\\: string, fieldName\\?\\: string, name\\?\\: string, strategy\\?\\: string, association\\?\\: int, id\\?\\: bool, isOwningSide\\?\\: bool, collectionClass\\?\\: class\\-string, \\.\\.\\.\\}, array\\{fieldName\\: 'enum', enumType\\: 'Documents\\\\\\\\Card'\\} given\\.$#" + message: "#^Parameter \\#1 \\$mapping of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadata\\\\:\\:mapField\\(\\) expects array\\{type\\?\\: string, fieldName\\?\\: string, name\\?\\: string, strategy\\?\\: string, association\\?\\: int, id\\?\\: bool, isOwningSide\\?\\: bool, collectionClass\\?\\: class\\-string, \\.\\.\\.\\}, array\\{fieldName\\: 'enum', enumType\\: 'Documents\\\\\\\\Card'\\} given\\.$#" count: 1 path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php - - message: "#^Parameter \\#1 \\$mapping of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadata\\\\:\\:mapField\\(\\) expects array\\{type\\?\\: string, fieldName\\?\\: string, name\\?\\: string, strategy\\?\\: string, association\\?\\: int, id\\?\\: bool, isOwningSide\\?\\: bool, collectionClass\\?\\: class\\-string, \\.\\.\\.\\}, array\\{fieldName\\: 'enum', enumType\\: 'Documents\\\\\\\\SuitNonBacked'\\} given\\.$#" + message: "#^Parameter \\#1 \\$mapping of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadata\\\\:\\:mapField\\(\\) expects array\\{type\\?\\: string, fieldName\\?\\: string, name\\?\\: string, strategy\\?\\: string, association\\?\\: int, id\\?\\: bool, isOwningSide\\?\\: bool, collectionClass\\?\\: class\\-string, \\.\\.\\.\\}, array\\{fieldName\\: 'enum', enumType\\: 'Documents\\\\\\\\SuitNonBacked'\\} given\\.$#" count: 1 path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml deleted file mode 100644 index 221a7a95f3..0000000000 --- a/psalm-baseline.xml +++ /dev/null @@ -1,479 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - implementsInterface(ClassMetadataFactoryInterface::class)]]> - implementsInterface(GridFSRepository::class)]]> - implementsInterface(ObjectRepository::class)]]> - - - - - - - - - - - - - - - - - - - - - - - ]]> - - - - - - - - - - identifier => $this->getIdentifierValue($object)]]]> - - - associationMappings]]> - associationMappings]]> - fieldMappings]]> - - - - - - - - - - ]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - {'default-discriminator-value'})]]> - {'discriminator-field'})]]> - {'discriminator-map'})]]> - {'generator-option'})]]> - and)]]> - field)]]> - criteria)]]> - prime)]]> - sort)]]> - {'default-discriminator-value'})]]> - {'discriminator-field'})]]> - {'discriminator-map'})]]> - option)]]> - {'partial-filter-expression'})]]> - {'tag-set'})]]> - field)]]> - id)]]> - indexes)]]> - {'default-discriminator-value'})]]> - {'discriminator-field'})]]> - {'discriminator-map'})]]> - {'embed-many'})]]> - {'embed-one'})]]> - {'lifecycle-callbacks'})]]> - {'read-preference'})]]> - {'reference-many'})]]> - {'reference-one'})]]> - {'schema-validation'})]]> - {'search-indexes'})]]> - {'shard-key'})]]> - option)]]> - - - metadata)]]> - {'also-load-methods'})]]> - - - - - getUnitOfWork())]]> - getUnitOfWork())]]> - - - - - coll)]]> - uow) && $this->isOrphanRemovalEnabled() && $value !== null]]> - uow) && $this->isOrphanRemovalEnabled() && $value !== null]]> - - - owner && isset($this->dm)]]> - dm)]]> - uow)]]> - uow)]]> - - - - - - - - - - - - - - - query]]> - - - - - newObj]]> - query]]> - query[$this->currentField]]]> - - - - - - - - - - - - - - - ['root' => 'array']]]]> - ['root' => 'array']]]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - parentAssociations]]> - - - - - - , 1: array}>]]> - - - - - - - - - - - - - - - - - - associationMappings['ref']]]> - associationMappings['ref1']]]> - associationMappings['ref2']]]> - associationMappings['ref3']]]> - associationMappings['ref4']]]> - ClassMetadata::REFERENCE_STORE_AS_DB_REF]]]> - - - - - - - - - - getData()]]> - - - - - categories[0]->children, $user->categories[1]->children]]]> - categories[0]->children[0]->children, $user->categories[0]->children[1]->children]]]> - categories[0]->children[0]->children, $user->categories[0]->children[1]->children]]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true]]]> - - - - - 'Doctrine\ODM\MongoDB\Tests\Functional\SomeInvalidClass']]]> - - - - - embeds]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - getData()]]> - - - - - - - - dm]]> - - - - - - - 'assoc', - 'reference' => true, - 'type' => 'many', - 'targetDocument' => 'stdClass', - 'repositoryMethod' => 'fetch', - $prop => $value, - ]]]> - 'enum', - 'enumType' => Card::class, - ]]]> - 'enum', - 'enumType' => SuitNonBacked::class, - ]]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/psalm.xml.dist b/psalm.xml.dist deleted file mode 100644 index 63e41ba00e..0000000000 --- a/psalm.xml.dist +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FacetTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FacetTest.php index d2b0ee0506..a25183060c 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FacetTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FacetTest.php @@ -66,7 +66,6 @@ public function testThrowsExceptionWithoutFieldName(): void $facetStage->pipeline($this->getTestAggregationBuilder()); } - /** @psalm-suppress InvalidArgument on purpose to throw exception */ public function testThrowsExceptionOnInvalidPipeline(): void { $facetStage = new Facet($this->getTestAggregationBuilder()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SortTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SortTest.php index 76331a095c..e56dddceae 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SortTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SortTest.php @@ -9,14 +9,14 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use PHPUnit\Framework\Attributes\DataProvider; -/** @psalm-import-type SortShape from Sort */ +/** @phpstan-import-type SortShape from Sort */ class SortTest extends BaseTestCase { use AggregationTestTrait; /** * @param string|array $field - * @psalm-param SortShape $expectedSort + * @phpstan-param SortShape $expectedSort */ #[DataProvider('provideSortOptions')] public function testStage(array $expectedSort, $field, ?string $order = null): void @@ -28,7 +28,7 @@ public function testStage(array $expectedSort, $field, ?string $order = null): v /** * @param string|array $field - * @psalm-param SortShape $expectedSort + * @phpstan-param SortShape $expectedSort */ #[DataProvider('provideSortOptions')] public function testFromBuilder(array $expectedSort, $field, ?string $order = null): void diff --git a/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php b/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php index 7806d9cf40..b24c54c3fe 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php @@ -160,7 +160,7 @@ protected function skipTestIfTransactionalFlushEnabled(): void } } - /** @psalm-param class-string $className */ + /** @param class-string $className */ protected function skipTestIfNotSharded(string $className): void { $result = $this->dm->getDocumentDatabase($className)->command(['listCommands' => true], ['typeMap' => DocumentManager::CLIENT_TYPEMAP])->toArray()[0]; @@ -172,7 +172,7 @@ protected function skipTestIfNotSharded(string $className): void $this->markTestSkipped('Test skipped because server does not support sharding'); } - /** @psalm-param class-string $className */ + /** @param class-string $className */ protected function skipTestIfSharded(string $className): void { $result = $this->dm->getDocumentDatabase($className)->command(['listCommands' => true], ['typeMap' => DocumentManager::CLIENT_TYPEMAP])->toArray()[0]; diff --git a/tests/Doctrine/ODM/MongoDB/Tests/ClassMetadataTestUtil.php b/tests/Doctrine/ODM/MongoDB/Tests/ClassMetadataTestUtil.php index 5783c7e9ed..40b3ffd05d 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/ClassMetadataTestUtil.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/ClassMetadataTestUtil.php @@ -8,15 +8,15 @@ use Doctrine\ODM\MongoDB\Types\Type; /** - * @psalm-import-type FieldMapping from ClassMetadata - * @psalm-import-type FieldMappingConfig from ClassMetadata + * @phpstan-import-type FieldMapping from ClassMetadata + * @phpstan-import-type FieldMappingConfig from ClassMetadata */ class ClassMetadataTestUtil { /** - * @psalm-param FieldMappingConfig $mapping + * @phpstan-param FieldMappingConfig $mapping * - * @psalm-return FieldMapping + * @phpstan-return FieldMapping */ public static function getFieldMapping(array $mapping): array { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php index 7a5ea7e836..088040c918 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php @@ -220,7 +220,7 @@ public function testPostCollectionLoad(): void class MyEventListener { - /** @psalm-var array> */ + /** @var array> */ public array $called = []; /** @param array{LifecycleEventArgs} $args */ diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php index 03f2ef3d73..232c35343c 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php @@ -624,7 +624,7 @@ public static function dataProviderTestWriteConcern(): array ]; } - /** @psalm-param class-string $class */ + /** @param class-string $class */ #[DataProvider('dataProviderTestWriteConcern')] public function testExecuteInsertsRespectsWriteConcern(string $class, string|int $writeConcern): void { @@ -646,7 +646,7 @@ public function testExecuteInsertsRespectsWriteConcern(string $class, string|int $this->dm->flush(); } - /** @psalm-param class-string $class */ + /** @param class-string $class */ #[DataProvider('dataProviderTestWriteConcern')] public function testExecuteInsertsOmitsWriteConcernInTransaction(string $class, string|int $writeConcern): void { @@ -668,7 +668,7 @@ public function testExecuteInsertsOmitsWriteConcernInTransaction(string $class, $this->dm->flush(); } - /** @psalm-param class-string $class */ + /** @param class-string $class */ #[DataProvider('dataProviderTestWriteConcern')] public function testExecuteUpsertsRespectsWriteConcern(string $class, string|int $writeConcern): void { @@ -691,7 +691,7 @@ public function testExecuteUpsertsRespectsWriteConcern(string $class, string|int $this->dm->flush(); } - /** @psalm-param class-string $class */ + /** @param class-string $class */ #[DataProvider('dataProviderTestWriteConcern')] public function testExecuteUpsertsDoesNotUseWriteConcernInTransaction(string $class, string|int $writeConcern): void { @@ -714,7 +714,7 @@ public function testExecuteUpsertsDoesNotUseWriteConcernInTransaction(string $cl $this->dm->flush(); } - /** @psalm-param class-string $class */ + /** @param class-string $class */ #[DataProvider('dataProviderTestWriteConcern')] public function testRemoveRespectsWriteConcern(string $class, string|int $writeConcern): void { @@ -739,7 +739,7 @@ public function testRemoveRespectsWriteConcern(string $class, string|int $writeC $this->dm->flush(); } - /** @psalm-param class-string $class */ + /** @param class-string $class */ #[DataProvider('dataProviderTestWriteConcern')] public function testRemoveDoesNotUseWriteConcernInTransaction(string $class, string|int $writeConcern): void { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReadPreferenceTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReadPreferenceTest.php index 1ad64db3aa..c1f2b70e9f 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReadPreferenceTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReadPreferenceTest.php @@ -14,7 +14,7 @@ use MongoDB\Driver\WriteConcern; use PHPUnit\Framework\Attributes\DataProvider; -/** @psalm-type ReadPreferenceTagShape = array{dc?: string, usage?: string} */ +/** @phpstan-type ReadPreferenceTagShape array{dc?: string, usage?: string} */ class ReadPreferenceTest extends BaseTestCase { public function setUp(): void @@ -44,7 +44,7 @@ public function testHintIsNotSetByDefault(): void self::assertArrayNotHasKey(Query::HINT_READ_PREFERENCE, $groups->getHints()); } - /** @psalm-param ReadPreferenceTagShape[] $tags */ + /** @phpstan-param ReadPreferenceTagShape[] $tags */ #[DataProvider('provideReadPreferenceHints')] public function testHintIsSetOnQuery(string $readPreference, array $tags = []): void { @@ -101,7 +101,7 @@ public function testDocumentLevelReadPreferenceCanBeOverriddenInQueryBuilder(): $this->assertReadPreferenceHint(ReadPreference::SECONDARY, $query->getQuery()['readPreference']); } - /** @psalm-param ReadPreferenceTagShape[] $tags */ + /** @phpstan-param ReadPreferenceTagShape[] $tags */ private function assertReadPreferenceHint(string $mode, ReadPreference $readPreference, array $tags = []): void { self::assertInstanceOf(ReadPreference::class, $readPreference); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1152Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1152Test.php index cfee336c63..050a36bcdb 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1152Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1152Test.php @@ -49,11 +49,11 @@ class GH1152Parent public $child; } -/** @psalm-import-type AssociationFieldMapping from ClassMetadata */ +/** @phpstan-import-type AssociationFieldMapping from ClassMetadata */ #[ODM\EmbeddedDocument] class GH1152Child { - /** @psalm-var array{0: AssociationFieldMapping, 1: object|null, 2: string}|null */ + /** @var array{0: AssociationFieldMapping, 1: object|null, 2: string}|null */ public $parentAssociation; } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ValidationTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ValidationTest.php index 042afd4763..60f71fd1c4 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ValidationTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ValidationTest.php @@ -8,11 +8,7 @@ use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\SchemaValidated; - -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\fromPHP; -use function MongoDB\BSON\toCanonicalExtendedJSON; -use function MongoDB\BSON\toPHP; +use MongoDB\BSON\Document; class ValidationTest extends BaseTestCase { @@ -41,15 +37,13 @@ public function testCreateUpdateValidatedDocument(): void ] } EOT; - $expectedValidatorBson = fromJSON($expectedValidatorJson); - $expectedValidator = toPHP($expectedValidatorBson, []); + $expectedValidator = Document::fromJSON($expectedValidatorJson)->toPHP(); $expectedOptions = [ 'validator' => $expectedValidator, 'validationLevel' => ClassMetadata::SCHEMA_VALIDATION_LEVEL_MODERATE, 'validationAction' => ClassMetadata::SCHEMA_VALIDATION_ACTION_WARN, ]; - $expectedOptionsBson = fromPHP($expectedOptions); - $expectedOptionsJson = toCanonicalExtendedJSON($expectedOptionsBson); + $expectedOptionsJson = Document::fromPHP($expectedOptions)->toCanonicalExtendedJSON(); $collections = $this->dm->getDocumentDatabase($cm->name)->listCollections(); $assertNb = 0; foreach ($collections as $collection) { @@ -58,8 +52,7 @@ public function testCreateUpdateValidatedDocument(): void } $assertNb++; - $collectionOptionsBson = fromPHP($collection->getOptions()); - $collectionOptionsJson = toCanonicalExtendedJSON($collectionOptionsBson); + $collectionOptionsJson = Document::fromPHP($collection->getOptions())->toCanonicalExtendedJSON(); self::assertJsonStringEqualsJsonString($expectedOptionsJson, $collectionOptionsJson); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractMappingDriverTestCase.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractMappingDriverTestCase.php index 1a20c17458..db287a771b 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractMappingDriverTestCase.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractMappingDriverTestCase.php @@ -11,6 +11,7 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\MappingException; +use Doctrine\ODM\MongoDB\Mapping\TimeSeries\Granularity; use Doctrine\ODM\MongoDB\Repository\DefaultGridFSRepository; use Doctrine\ODM\MongoDB\Repository\DocumentRepository; use Doctrine\ODM\MongoDB\Repository\ViewRepository; @@ -677,6 +678,26 @@ public function testEnumType(): void self::assertTrue($metadata->fieldMappings['nullableSuit']['nullable']); self::assertInstanceOf(EnumReflectionProperty::class, $metadata->reflFields['nullableSuit']); } + + public function testTimeSeriesDocumentWithGranularity(): void + { + $metadata = $this->dm->getClassMetadata(AbstractMappingDriverTimeSeriesDocumentWithGranularity::class); + + self::assertEquals( + new ODM\TimeSeries('time', 'metadata', Granularity::Seconds, 86400), + $metadata->timeSeriesOptions, + ); + } + + public function testTimeSeriesDocumentWithBucket(): void + { + $metadata = $this->dm->getClassMetadata(AbstractMappingDriverTimeSeriesDocumentWithBucket::class); + + self::assertEquals( + new ODM\TimeSeries('time', 'metadata', expireAfterSeconds: 86400, bucketMaxSpanSeconds: 10, bucketRoundingSeconds: 15), + $metadata->timeSeriesOptions, + ); + } } /** @@ -1296,3 +1317,53 @@ public function createViewAggregation(Builder $builder): void ->includeFields(['name']); } } + +/** + * @ODM\Document(collection="cms_users", writeConcern=1, readOnly=true) + * @ODM\TimeSeries(timeField="time", metaField="metadata", granularity=Granularity::Seconds, expireAfterSeconds=86400) + */ +#[ODM\Document] +#[ODM\TimeSeries(timeField: 'time', metaField: 'metadata', granularity: Granularity::Seconds, expireAfterSeconds: 86400)] +class AbstractMappingDriverTimeSeriesDocumentWithGranularity +{ + /** @ODM\Id */ + #[ODM\Id] + public ?string $id = null; + + /** @ODM\Field(type="date") */ + #[ODM\Field(type: 'date')] + public DateTime $time; + + /** @ODM\Field */ + #[ODM\Field] + public string $metadata; + + /** @ODM\Field(type="int") */ + #[ODM\Field(type: 'int')] + public int $value; +} + +/** + * @ODM\Document(collection="cms_users", writeConcern=1, readOnly=true) + * @ODM\TimeSeries(timeField="time", metaField="metadata", expireAfterSeconds=86400, bucketMaxSpanSeconds=10, bucketRoundingSeconds=15) + */ +#[ODM\Document] +#[ODM\TimeSeries(timeField: 'time', metaField: 'metadata', expireAfterSeconds: 86400, bucketMaxSpanSeconds: 10, bucketRoundingSeconds: 15)] +class AbstractMappingDriverTimeSeriesDocumentWithBucket +{ + /** @ODM\Id */ + #[ODM\Id] + public ?string $id = null; + + /** @ODM\Field(type="date") */ + #[ODM\Field(type: 'date')] + public DateTime $time; + + /** @ODM\Field */ + #[ODM\Field] + public string $metadata; + + /** @ODM\Field(type="int") */ + #[ODM\Field(type: 'int')] + public int $value; +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php index adf80fb6a6..c8f74769d4 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php @@ -4,10 +4,12 @@ namespace Doctrine\ODM\MongoDB\Tests\Mapping; +use DateTime; use Doctrine\ODM\MongoDB\Events; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\MappingException; +use Doctrine\ODM\MongoDB\Mapping\TimeSeries\Granularity; use Doctrine\ODM\MongoDB\Repository\DocumentRepository; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Doctrine\ODM\MongoDB\Tests\ClassMetadataTestUtil; @@ -35,6 +37,7 @@ use Documents\UserTyped; use Generator; use InvalidArgumentException; +use MongoDB\BSON\Document; use PHPUnit\Framework\Attributes\DataProvider; use ProxyManager\Proxy\GhostObjectInterface; use ReflectionClass; @@ -42,8 +45,6 @@ use stdClass; use function array_merge; -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\toPHP; use function serialize; use function unserialize; @@ -78,7 +79,7 @@ public function testClassMetadataInstanceSerialization(): void $cm->setVersioned(true); $cm->setVersionField('version'); $validatorJson = '{ "$and": [ { "email": { "$regularExpression" : { "pattern": "@mongodb\\\\.com$", "options": "" } } } ] }'; - $cm->setValidator(toPHP(fromJSON($validatorJson))); + $cm->setValidator(Document::fromJSON($validatorJson)->toPHP()); $cm->setValidationAction(ClassMetadata::SCHEMA_VALIDATION_ACTION_WARN); $cm->setValidationLevel(ClassMetadata::SCHEMA_VALIDATION_LEVEL_OFF); self::assertIsArray($cm->getFieldMapping('phonenumbers')); @@ -110,7 +111,7 @@ public function testClassMetadataInstanceSerialization(): void self::assertEquals('lock', $cm->lockField); self::assertEquals(true, $cm->isVersioned); self::assertEquals('version', $cm->versionField); - self::assertEquals(toPHP(fromJSON($validatorJson)), $cm->getValidator()); + self::assertEquals(Document::fromJSON($validatorJson)->toPHP(), $cm->getValidator()); self::assertEquals(ClassMetadata::SCHEMA_VALIDATION_ACTION_WARN, $cm->getValidationAction()); self::assertEquals(ClassMetadata::SCHEMA_VALIDATION_LEVEL_OFF, $cm->getValidationLevel()); } @@ -239,6 +240,7 @@ public function testEnumTypeMustPointToAnEnum(): void $this->expectExceptionMessage( 'Attempting to map a non-enum type Documents\Card as an enum: ', ); + // @phpstan-ignore-next-line $cm->mapField([ 'fieldName' => 'enum', 'enumType' => Card::class, @@ -258,6 +260,7 @@ public function testEnumTypeMustPointToABackedEnum(): void $this->expectExceptionMessage( 'Attempting to map a non-backed enum Documents\SuitNonBacked: ', ); + // @phpstan-ignore-next-line $cm->mapField([ 'fieldName' => 'enum', 'enumType' => SuitNonBacked::class, @@ -540,6 +543,7 @@ public function testSetFieldValueWithProxy(): void $proxy = $this->dm->getReference(Album::class, $document->getId()); self::assertInstanceOf(GhostObjectInterface::class, $proxy); + self::assertInstanceOf(Album::class, $proxy); self::assertEquals('nevermind', $proxy->getName()); } @@ -969,6 +973,72 @@ public function testDefaultValueForValidationLevel(): void $cm = new ClassMetadata('stdClass'); self::assertEquals(ClassMetadata::SCHEMA_VALIDATION_LEVEL_STRICT, $cm->getValidationLevel()); } + + public function testEmptySearchIndexDefinition(): void + { + $cm = new ClassMetadata('stdClass'); + + $this->expectException(MappingException::class); + $this->expectExceptionMessage('stdClass search index "default" must be dynamic or specify a field mapping'); + $cm->addSearchIndex(['mappings' => []]); + } + + public function testTimeSeriesMappingOnlyWithTimeField(): void + { + $metadata = $this->dm->getClassMetadata(TimeSeriesTestDocument::class); + $metadata->markAsTimeSeries(new ODM\TimeSeries('time')); + + self::assertNotNull($metadata->timeSeriesOptions); + self::assertSame('time', $metadata->timeSeriesOptions->timeField); + } + + public function testTimeSeriesMappingWithMissingTimeField(): void + { + $metadata = $this->dm->getClassMetadata(TimeSeriesTestDocument::class); + + self::expectExceptionObject(MappingException::timeSeriesFieldNotFound(TimeSeriesTestDocument::class, 'foo', 'time')); + $metadata->markAsTimeSeries(new ODM\TimeSeries('foo')); + } + + public function testTimeSeriesMappingWithMetadataField(): void + { + $metadata = $this->dm->getClassMetadata(TimeSeriesTestDocument::class); + $metadata->markAsTimeSeries(new ODM\TimeSeries('time', 'metadata')); + + self::assertNotNull($metadata->timeSeriesOptions); + self::assertSame('metadata', $metadata->timeSeriesOptions->metaField); + } + + public function testTimeSeriesMappingWithMissingMetadataField(): void + { + $metadata = $this->dm->getClassMetadata(TimeSeriesTestDocument::class); + + self::expectExceptionObject(MappingException::timeSeriesFieldNotFound(TimeSeriesTestDocument::class, 'foo', 'metadata')); + $metadata->markAsTimeSeries(new ODM\TimeSeries('time', 'foo')); + } + + public function testTimeSeriesMappingWithExpireAfterSeconds(): void + { + $metadata = $this->dm->getClassMetadata(TimeSeriesTestDocument::class); + $metadata->markAsTimeSeries(new ODM\TimeSeries('time', expireAfterSeconds: 10)); + + self::assertSame(10, $metadata->timeSeriesOptions->expireAfterSeconds); + } + + public function testTimeSeriesMappingWithGranularityAndBucketMaxSpanSeconds(): void + { + $metadata = $this->dm->getClassMetadata(TimeSeriesTestDocument::class); + $metadata->markAsTimeSeries(new ODM\TimeSeries('time', granularity: Granularity::Hours, bucketMaxSpanSeconds: 15, bucketRoundingSeconds: 20)); + + /* + * We don't throw for invalid settings here, including: + * - bucketMaxSpanSeconds not being equal to bucketRoundingSeconds + * - granularity and bucket settings applied together + */ + self::assertSame(Granularity::Hours, $metadata->timeSeriesOptions->granularity); + self::assertSame(15, $metadata->timeSeriesOptions->bucketMaxSpanSeconds); + self::assertSame(20, $metadata->timeSeriesOptions->bucketRoundingSeconds); + } } /** @template-extends DocumentRepository */ @@ -997,3 +1067,16 @@ class EmbeddedAssociationsCascadeTest #[ODM\EmbedOne(targetDocument: Address::class)] public $addresses; } + +#[ODM\Document] +class TimeSeriesTestDocument +{ + #[ODM\Id] + public ?string $id = null; + + #[ODM\Field] + public DateTime $time; + + #[ODM\Field] + public string $metadata; +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/XmlDriverTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/XmlDriverTest.php index 9884143849..8280a1df13 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/XmlDriverTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/Driver/XmlDriverTest.php @@ -7,6 +7,7 @@ use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Mapping\Driver\XmlDriver; use Doctrine\ODM\MongoDB\Mapping\MappingException; +use MongoDB\BSON\Document; use TestDocuments\AlsoLoadDocument; use TestDocuments\CustomIdGenerator; use TestDocuments\InvalidPartialFilterDocument; @@ -16,9 +17,6 @@ use TestDocuments\UserNonStringOptions; use TestDocuments\WildcardIndexDocument; -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\toPHP; - class XmlDriverTest extends AbstractDriverTestCase { public function setUp(): void @@ -137,8 +135,7 @@ public function testValidationMapping(): void ] } EOT; - $expectedValidatorBson = fromJSON($expectedValidatorJson); - $expectedValidator = toPHP($expectedValidatorBson, []); + $expectedValidator = Document::fromJSON($expectedValidatorJson)->toPHP(); self::assertEquals($expectedValidator, $classMetadata->getValidator()); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/xml/Doctrine.ODM.MongoDB.Tests.Mapping.AbstractMappingDriverTimeSeriesDocumentWithBucket.dcm.xml b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/xml/Doctrine.ODM.MongoDB.Tests.Mapping.AbstractMappingDriverTimeSeriesDocumentWithBucket.dcm.xml new file mode 100644 index 0000000000..981a3b8dc2 --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/xml/Doctrine.ODM.MongoDB.Tests.Mapping.AbstractMappingDriverTimeSeriesDocumentWithBucket.dcm.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/xml/Doctrine.ODM.MongoDB.Tests.Mapping.AbstractMappingDriverTimeSeriesDocumentWithGranularity.dcm.xml b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/xml/Doctrine.ODM.MongoDB.Tests.Mapping.AbstractMappingDriverTimeSeriesDocumentWithGranularity.dcm.xml new file mode 100644 index 0000000000..6cb5fddb7a --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/xml/Doctrine.ODM.MongoDB.Tests.Mapping.AbstractMappingDriverTimeSeriesDocumentWithGranularity.dcm.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php b/tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php index 0d7e2c1e0a..b8d3a6df42 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php @@ -420,11 +420,10 @@ public function testConstructorShouldThrowExceptionForInvalidType(): void { $this->expectException(InvalidArgumentException::class); - /** @psalm-suppress InvalidArgument */ new Query($this->dm, new ClassMetadata(User::class), $this->getMockCollection(), ['type' => -1], []); } - /** @psalm-param Query::TYPE_* $type */ + /** @param Query::TYPE_* $type */ #[DataProvider('provideQueryTypesThatDoNotReturnAnIterator')] public function testGetIteratorShouldThrowExceptionWithoutExecutingForTypesThatDoNotReturnAnIterator(int $type, string $method): void { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Repository/DefaultGridFSRepositoryTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Repository/DefaultGridFSRepositoryTest.php index 51ba3d519a..6a65706c41 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Repository/DefaultGridFSRepositoryTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Repository/DefaultGridFSRepositoryTest.php @@ -307,7 +307,6 @@ private function getRepository(string $className = File::class): GridFSRepositor assert($repository instanceof GridFSRepository); - /** @psalm-var GridFSRepository */ return $repository; } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php b/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php index 83f4dd5590..e3f1174445 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/SchemaManagerTest.php @@ -8,6 +8,7 @@ use Doctrine\Common\EventManager; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Mapping\TimeSeries\Granularity; use Doctrine\ODM\MongoDB\SchemaManager; use Documents\BaseDocument; use Documents\CmsAddress; @@ -20,9 +21,11 @@ use Documents\Sharded\ShardedOne; use Documents\Sharded\ShardedOneWithDifferentKey; use Documents\SimpleReferenceUser; +use Documents\TimeSeries\TimeSeriesDocument; use Documents\Tournament\Tournament; use Documents\UserName; use InvalidArgumentException; +use MongoDB\BSON\Document; use MongoDB\Client; use MongoDB\Collection; use MongoDB\Database; @@ -43,16 +46,14 @@ use function array_map; use function assert; use function in_array; -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\toPHP; /** - * @psalm-import-type IndexMapping from ClassMetadata - * @psalm-import-type IndexOptions from ClassMetadata + * @phpstan-import-type IndexMapping from ClassMetadata + * @phpstan-import-type IndexOptions from ClassMetadata */ class SchemaManagerTest extends BaseTestCase { - /** @psalm-var list */ + /** @var list */ private array $indexedClasses = [ CmsAddress::class, CmsArticle::class, @@ -64,13 +65,13 @@ class SchemaManagerTest extends BaseTestCase ShardedOneWithDifferentKey::class, ]; - /** @psalm-var list */ + /** @var list */ private array $searchIndexedClasses = [ CmsAddress::class, CmsArticle::class, ]; - /** @psalm-var list */ + /** @var list */ private array $views = [ UserName::class, ]; @@ -168,7 +169,7 @@ public static function getIndexCreationWriteOptions(): array ]; } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getIndexCreationWriteOptions')] public function testEnsureIndexes(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern, bool $background = false): void { @@ -214,7 +215,7 @@ public function testEnsureIndexes(array $expectedWriteOptions, ?int $maxTimeMs, $this->schemaManager->ensureIndexes($maxTimeMs, $writeConcern, $background); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getIndexCreationWriteOptions')] public function testEnsureDocumentIndexes(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern, bool $background = false): void { @@ -233,7 +234,7 @@ public function testEnsureDocumentIndexes(array $expectedWriteOptions, ?int $max $this->schemaManager->ensureDocumentIndexes(CmsArticle::class, $maxTimeMs, $writeConcern, $background); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getIndexCreationWriteOptions')] public function testEnsureDocumentIndexesForGridFSFile(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern, bool $background = false): void { @@ -274,7 +275,7 @@ public function testEnsureDocumentIndexesForGridFSFile(array $expectedWriteOptio $this->schemaManager->ensureDocumentIndexes(File::class, $maxTimeMs, $writeConcern, $background); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getIndexCreationWriteOptions')] public function testEnsureDocumentIndexesWithTwoLevelInheritance(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern, bool $background = false): void { @@ -288,7 +289,7 @@ public function testEnsureDocumentIndexesWithTwoLevelInheritance(array $expected $this->schemaManager->ensureDocumentIndexes(CmsProduct::class, $maxTimeMs, $writeConcern, $background); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testUpdateDocumentIndexesShouldCreateMappedIndexes(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -310,7 +311,7 @@ public function testUpdateDocumentIndexesShouldCreateMappedIndexes(array $expect $this->schemaManager->updateDocumentIndexes(CmsArticle::class, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testUpdateDocumentIndexesShouldDeleteUnmappedIndexesBeforeCreatingMappedIndexes(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -340,7 +341,7 @@ public function testUpdateDocumentIndexesShouldDeleteUnmappedIndexesBeforeCreati $this->schemaManager->updateDocumentIndexes(CmsArticle::class, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testDeleteIndexes(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -363,7 +364,7 @@ public function testDeleteIndexes(array $expectedWriteOptions, ?int $maxTimeMs, $this->schemaManager->deleteIndexes($maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testDeleteDocumentIndexes(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -577,7 +578,7 @@ public function testUpdateValidators(): void $this->schemaManager->updateValidators(); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testUpdateDocumentValidator(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -601,8 +602,7 @@ public function testUpdateDocumentValidator(array $expectedWriteOptions, ?int $m ] } EOT; - $expectedValidatorBson = fromJSON($expectedValidatorJson); - $expectedValidator = toPHP($expectedValidatorBson, []); + $expectedValidator = Document::fromJSON($expectedValidatorJson)->toPHP(); $database ->expects($this->once()) ->method('command') @@ -625,7 +625,7 @@ public function testUpdateDocumentValidatorShouldThrowExceptionForMappedSupercla $this->schemaManager->updateDocumentValidator($class->name); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testUpdateDocumentValidatorReset(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -646,7 +646,7 @@ public function testUpdateDocumentValidatorReset(array $expectedWriteOptions, ?i $this->schemaManager->updateDocumentValidator($class->name, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testCreateDocumentCollection(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -672,7 +672,7 @@ public function testCreateDocumentCollection(array $expectedWriteOptions, ?int $ $this->schemaManager->createDocumentCollection(CmsArticle::class, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testCreateDocumentCollectionForFile(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -685,7 +685,7 @@ public function testCreateDocumentCollectionForFile(array $expectedWriteOptions, $this->schemaManager->createDocumentCollection(File::class, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testCreateDocumentCollectionWithValidator(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -707,8 +707,7 @@ public function testCreateDocumentCollectionWithValidator(array $expectedWriteOp ] } EOT; - $expectedValidatorBson = fromJSON($expectedValidatorJson); - $expectedValidator = toPHP($expectedValidatorBson, []); + $expectedValidator = Document::fromJSON($expectedValidatorJson)->toPHP(); $options = [ 'capped' => false, 'size' => null, @@ -727,7 +726,7 @@ public function testCreateDocumentCollectionWithValidator(array $expectedWriteOp $this->schemaManager->createDocumentCollection($cm->name, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testCreateView(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -763,7 +762,34 @@ public function testCreateView(array $expectedWriteOptions, ?int $maxTimeMs, ?Wr $this->schemaManager->createDocumentCollection(UserName::class, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ + #[DataProvider('getWriteOptions')] + public function testCreateTimeSeriesCollection(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void + { + $metadata = $this->dm->getClassMetadata(TimeSeriesDocument::class); + + $options = [ + 'timeseries' => [ + 'timeField' => 'time', + 'metaField' => 'metadata', + 'granularity' => Granularity::Seconds, + ], + 'expireAfterSeconds' => 86400, + ]; + + $database = $this->documentDatabases[$this->getDatabaseName($metadata)]; + $database + ->expects($this->once()) + ->method('createCollection') + ->with( + 'TimeSeriesDocument', + $this->writeOptions($options + $expectedWriteOptions), + ); + + $this->schemaManager->createDocumentCollection(TimeSeriesDocument::class, $maxTimeMs, $writeConcern); + } + + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testCreateCollections(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -790,7 +816,7 @@ public function testCreateCollections(array $expectedWriteOptions, ?int $maxTime self::assertSame(1, array_count_values($createdCollections)['Tournament']); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testDropCollections(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -803,7 +829,7 @@ public function testDropCollections(array $expectedWriteOptions, ?int $maxTimeMs $this->schemaManager->dropCollections($maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testDropDocumentCollection(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -821,7 +847,7 @@ public function testDropDocumentCollection(array $expectedWriteOptions, ?int $ma $this->schemaManager->dropDocumentCollection(CmsArticle::class, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testDropDocumentCollectionForGridFSFile(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -855,7 +881,7 @@ public function testDropDocumentCollectionForGridFSFile(array $expectedWriteOpti $this->schemaManager->dropDocumentCollection(File::class, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testDropView(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -873,7 +899,7 @@ public function testDropView(array $expectedWriteOptions, ?int $maxTimeMs, ?Writ $this->schemaManager->dropDocumentCollection(UserName::class, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testDropDocumentDatabase(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -892,7 +918,7 @@ public function testDropDocumentDatabase(array $expectedWriteOptions, ?int $maxT $this->schemaManager->dropDocumentDatabase(CmsArticle::class, $maxTimeMs, $writeConcern); } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ #[DataProvider('getWriteOptions')] public function testDropDatabases(array $expectedWriteOptions, ?int $maxTimeMs, ?WriteConcern $writeConcern): void { @@ -908,7 +934,7 @@ public function testDropDatabases(array $expectedWriteOptions, ?int $maxTimeMs, /** * @param array $mongoIndex - * @psalm-param IndexMapping $documentIndex + * @phpstan-param IndexMapping $documentIndex */ #[DataProvider('dataIsMongoIndexEquivalentToDocumentIndex')] public function testIsMongoIndexEquivalentToDocumentIndex(bool $expected, array $mongoIndex, array $documentIndex): void @@ -1126,7 +1152,7 @@ public static function dataIsMongoIndexEquivalentToDocumentIndex(): array /** * @param array $mongoIndex - * @psalm-param IndexMapping $documentIndex + * @phpstan-param IndexMapping $documentIndex */ #[DataProvider('dataIsMongoTextIndexEquivalentToDocumentIndex')] public function testIsMongoIndexEquivalentToDocumentIndexWithTextIndexes(bool $expected, array $mongoIndex, array $documentIndex): void @@ -1319,7 +1345,7 @@ private function getMockDatabase() return $db; } - /** @psalm-param IndexOptions $expectedWriteOptions */ + /** @phpstan-param IndexOptions $expectedWriteOptions */ private function writeOptions(array $expectedWriteOptions): Constraint { return new Callback(static function (array $value) use ($expectedWriteOptions) { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php index 81fac9bea7..e79c0ef931 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php @@ -511,8 +511,7 @@ public function testCommitsInProgressIsUpdatedOnException(): void try { $this->dm->flush(); } catch (Throwable) { - $getCommitsInProgress = Closure::bind(fn (UnitOfWork $unitOfWork) => /** @psalm-suppress InaccessibleProperty */ -$unitOfWork->commitsInProgress, $this->dm->getUnitOfWork(), UnitOfWork::class); + $getCommitsInProgress = Closure::bind(fn (UnitOfWork $unitOfWork) => $unitOfWork->commitsInProgress, $this->dm->getUnitOfWork(), UnitOfWork::class); self::assertSame(0, $getCommitsInProgress($this->dm->getUnitOfWork())); diff --git a/tests/Documents/TimeSeries/TimeSeriesDocument.php b/tests/Documents/TimeSeries/TimeSeriesDocument.php new file mode 100644 index 0000000000..74426a4144 --- /dev/null +++ b/tests/Documents/TimeSeries/TimeSeriesDocument.php @@ -0,0 +1,26 @@ +