Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge release 2.9.1 into 2.10.x #2710

Merged
merged 6 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,6 @@ jobs:
composer require --no-update symfony/var-dumper:^7@dev
composer require --no-update --dev symfony/cache:^7@dev

- name: "Configure PHP 8.4"
if: "${{ matrix.php-version == '8.4' }}"
run: |
# psalm is not compatible with PHP 8.4
# https://github.com/vimeo/psalm/pull/10928
composer remove --no-update --dev vimeo/psalm

- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v3"
with:
Expand Down
2 changes: 1 addition & 1 deletion docs/en/reference/basic-mapping.rst
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ Here is a quick overview of the built-in mapping types:
- ``string``
- ``timestamp``

You can read more about the available MongoDB types on `php.net <https://www.php.net/manual/en/book.bson.php>`_.
You can read more about the available MongoDB types on `php.net <https://www.php.net/manual/en/mongodb.bson.php>`_.

.. note::

Expand Down
49 changes: 16 additions & 33 deletions lib/Doctrine/ODM/MongoDB/Iterator/CachingIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
namespace Doctrine\ODM\MongoDB\Iterator;

use Countable;
use Generator;
use Iterator as SPLIterator;
use IteratorIterator;
use ReturnTypeWillChange;
use RuntimeException;
use Traversable;
Expand Down Expand Up @@ -33,13 +34,11 @@ final class CachingIterator implements Countable, Iterator
/** @var array<mixed, TValue> */
private array $items = [];

/** @var Generator<mixed, TValue>|null */
private ?Generator $iterator;
/** @var SPLIterator<mixed, TValue>|null */
private ?SPLIterator $iterator;

private bool $iteratorAdvanced = false;

private bool $iteratorExhausted = false;

/**
* Initialize the iterator and stores the first item in the cache. This
* effectively rewinds the Traversable and the wrapping Generator, which
Expand All @@ -51,7 +50,8 @@ final class CachingIterator implements Countable, Iterator
*/
public function __construct(Traversable $iterator)
{
$this->iterator = $this->wrapTraversable($iterator);
$this->iterator = new IteratorIterator($iterator);
$this->iterator->rewind();
$this->storeCurrentItem();
}

Expand Down Expand Up @@ -94,9 +94,10 @@ public function key()
/** @see http://php.net/iterator.next */
public function next(): void
{
if (! $this->iteratorExhausted) {
$this->getIterator()->next();
if ($this->iterator !== null) {
$this->iterator->next();
$this->storeCurrentItem();
$this->iteratorAdvanced = true;
}

next($this->items);
Expand Down Expand Up @@ -126,15 +127,13 @@ public function valid(): bool
*/
private function exhaustIterator(): void
{
while (! $this->iteratorExhausted) {
while ($this->iterator !== null) {
$this->next();
}

$this->iterator = null;
}

/** @return Generator<mixed, TValue> */
private function getIterator(): Generator
/** @return SPLIterator<mixed, TValue> */
private function getIterator(): SPLIterator
{
if ($this->iterator === null) {
throw new RuntimeException('Iterator has already been destroyed');
Expand All @@ -148,28 +147,12 @@ private function getIterator(): Generator
*/
private function storeCurrentItem(): void
{
$key = $this->getIterator()->key();
$key = $this->iterator->key();

if ($key === null) {
return;
$this->iterator = null;
} else {
$this->items[$key] = $this->getIterator()->current();
}

$this->items[$key] = $this->getIterator()->current();
}

/**
* @param Traversable<mixed, TValue> $traversable
*
* @return Generator<mixed, TValue>
*/
private function wrapTraversable(Traversable $traversable): Generator
{
foreach ($traversable as $key => $value) {
yield $key => $value;

$this->iteratorAdvanced = true;
}

$this->iteratorExhausted = true;
}
}
25 changes: 7 additions & 18 deletions lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
use Doctrine\ODM\MongoDB\UnitOfWork;
use Generator;
use Iterator;
use IteratorIterator;
use ReturnTypeWillChange;
use RuntimeException;
use Traversable;
Expand All @@ -24,8 +24,8 @@
*/
final class HydratingIterator implements Iterator
{
/** @var Generator<mixed, array<string, mixed>>|null */
private ?Generator $iterator;
/** @var Iterator<mixed, array<string, mixed>>|null */
private ?Iterator $iterator;

/**
* @param Traversable<mixed, array<string, mixed>> $traversable
Expand All @@ -34,7 +34,8 @@ final class HydratingIterator implements Iterator
*/
public function __construct(Traversable $traversable, private UnitOfWork $unitOfWork, private ClassMetadata $class, private array $unitOfWorkHints = [])
{
$this->iterator = $this->wrapTraversable($traversable);
$this->iterator = new IteratorIterator($traversable);
$this->iterator->rewind();
}

public function __destruct()
Expand Down Expand Up @@ -74,8 +75,8 @@ public function valid(): bool
return $this->key() !== null;
}

/** @return Generator<mixed, array<string, mixed>> */
private function getIterator(): Generator
/** @return Iterator<mixed, array<string, mixed>> */
private function getIterator(): Iterator
{
if ($this->iterator === null) {
throw new RuntimeException('Iterator has already been destroyed');
Expand All @@ -93,16 +94,4 @@ private function hydrate(?array $document): ?object
{
return $document !== null ? $this->unitOfWork->getOrCreateDocument($this->class->name, $document, $this->unitOfWorkHints) : null;
}

/**
* @param Traversable<mixed, array<string, mixed>> $traversable
*
* @return Generator<mixed, array<string, mixed>>
*/
private function wrapTraversable(Traversable $traversable): Generator
{
foreach ($traversable as $key => $value) {
yield $key => $value;
}
}
}
61 changes: 24 additions & 37 deletions lib/Doctrine/ODM/MongoDB/Iterator/UnrewindableIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

namespace Doctrine\ODM\MongoDB\Iterator;

use Generator;
use Iterator as SPLIterator;
use IteratorIterator;
use LogicException;
use ReturnTypeWillChange;
use RuntimeException;
Expand All @@ -23,39 +24,34 @@
*/
final class UnrewindableIterator implements Iterator
{
/** @var Generator<mixed, TValue>|null */
private ?Generator $iterator;
/** @var SPLIterator<mixed, TValue>|null */
private ?SPLIterator $iterator;

private bool $iteratorAdvanced = false;

/**
* Initialize the iterator. This effectively rewinds the Traversable and
* the wrapping Generator, which will execute up to its first yield statement.
* Additionally, this mimics behavior of the SPL iterators and allows users
* to omit an explicit call to rewind() before using the other methods.
* Initialize the iterator. This effectively rewinds the Traversable.
* This mimics behavior of the SPL iterators and allows users to omit an
* explicit call to rewind() before using the other methods.
*
* @param Traversable<mixed, TValue> $iterator
*/
public function __construct(Traversable $iterator)
{
$this->iterator = $this->wrapTraversable($iterator);
$this->iterator->key();
$this->iterator = new IteratorIterator($iterator);
$this->iterator->rewind();
}

public function toArray(): array
{
$this->preventRewinding(__METHOD__);

$toArray = function () {
if (! $this->valid()) {
return;
}

yield $this->key() => $this->current();
yield from $this->getIterator();
};

return iterator_to_array($toArray());
try {
return iterator_to_array($this->getIterator());
} finally {
$this->iteratorAdvanced = true;
$this->iterator = null;
}
}

/** @return TValue|null */
Expand Down Expand Up @@ -84,6 +80,13 @@ public function next(): void
}

$this->iterator->next();
$this->iteratorAdvanced = true;

if ($this->iterator->valid()) {
return;
}

$this->iterator = null;
}

/** @see http://php.net/iterator.rewind */
Expand All @@ -108,29 +111,13 @@ private function preventRewinding(string $method): void
}
}

/** @return Generator<mixed, TValue> */
private function getIterator(): Generator
/** @return SPLIterator<mixed, TValue> */
private function getIterator(): SPLIterator
{
if ($this->iterator === null) {
throw new RuntimeException('Iterator has already been destroyed');
}

return $this->iterator;
}

/**
* @param Traversable<mixed, TValue> $traversable
*
* @return Generator<mixed, TValue>
*/
private function wrapTraversable(Traversable $traversable): Generator
{
foreach ($traversable as $key => $value) {
yield $key => $value;

$this->iteratorAdvanced = true;
}

$this->iterator = null;
}
}
14 changes: 11 additions & 3 deletions lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,21 @@ private function createInitializer(
*/
private function skippedFieldsFqns(ClassMetadata $metadata): array
{
$idFieldFqcns = [];
$skippedFieldsFqns = [];

foreach ($metadata->getIdentifierFieldNames() as $idField) {
$idFieldFqcns[] = $this->propertyFqcn($metadata->getReflectionProperty($idField));
$skippedFieldsFqns[] = $this->propertyFqcn($metadata->getReflectionProperty($idField));
}

return $idFieldFqcns;
foreach ($metadata->getReflectionClass()->getProperties() as $property) {
if ($metadata->hasField($property->getName())) {
continue;
}

$skippedFieldsFqns[] = $this->propertyFqcn($property);
}

return $skippedFieldsFqns;
}

private function propertyFqcn(ReflectionProperty $property): string
Expand Down
8 changes: 6 additions & 2 deletions lib/Doctrine/ODM/MongoDB/SchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,9 @@ public function updateDocumentSearchIndexes(string $documentName): void

$definedNames = array_column($searchIndexes, 'name');
try {
$existingNames = array_column(iterator_to_array($collection->listSearchIndexes()), 'name');
/* The typeMap option can be removed when bug is fixed in the minimum required version.
* https://jira.mongodb.org/browse/PHPLIB-1548 */
$existingNames = array_column(iterator_to_array($collection->listSearchIndexes(['typeMap' => ['root' => 'array']])), 'name');
} catch (CommandException $e) {
/* If $listSearchIndexes doesn't exist, only throw if search indexes have been defined.
* If no search indexes are defined and the server doesn't support search indexes, there's
Expand Down Expand Up @@ -465,7 +467,9 @@ public function deleteDocumentSearchIndexes(string $documentName): void
$collection = $this->dm->getDocumentCollection($class->name);

try {
$searchIndexes = $collection->listSearchIndexes();
/* The typeMap option can be removed when bug is fixed in the minimum required version.
* https://jira.mongodb.org/browse/PHPLIB-1548 */
$searchIndexes = $collection->listSearchIndexes(['typeMap' => ['root' => 'array']]);
} catch (CommandException $e) {
// If the server does not support search indexes, there are no indexes to remove in any case
if ($this->isSearchIndexCommandException($e)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ public function testIterationWithEmptySet(): void
self::assertFalse($iterator->valid());
}

public function testIterationWithInvalidIterator(): void
{
$mock = $this->createMock(Iterator::class);
// The method next() should not be called on a dead cursor.
$mock->expects(self::never())->method('next');
// The method valid() return false on a dead cursor.
$mock->expects(self::once())->method('valid')->willReturn(false);

$iterator = new CachingIterator($mock);

$this->assertEquals([], $iterator->toArray());
}

public function testPartialIterationDoesNotExhaust(): void
{
$traversable = $this->getTraversableThatThrows([1, 2, new Exception()]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ public function testRewindAfterPartialIteration(): void
iterator_to_array($iterator);
}

public function testRewindAfterToArray(): void
{
$iterator = new UnrewindableIterator($this->getTraversable([1, 2, 3]));

$iterator->toArray();
$this->expectException(LogicException::class);
$iterator->rewind();
}

public function testToArray(): void
{
$iterator = new UnrewindableIterator($this->getTraversable([1, 2, 3]));
Expand Down
Loading