diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml
index 1ce1a90c1b0..ac2788b39a4 100644
--- a/.github/workflows/coding-standards.yml
+++ b/.github/workflows/coding-standards.yml
@@ -8,7 +8,7 @@ on:
- .github/workflows/coding-standards.yml
- bin/**
- composer.*
- - lib/**
+ - src/**
- phpcs.xml.dist
- tests/**
push:
@@ -18,7 +18,7 @@ on:
- .github/workflows/coding-standards.yml
- bin/**
- composer.*
- - lib/**
+ - src/**
- phpcs.xml.dist
- tests/**
diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index 6af2ac37939..07d12226501 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -8,7 +8,7 @@ on:
- .github/workflows/continuous-integration.yml
- ci/**
- composer.*
- - lib/**
+ - src/**
- phpunit.xml.dist
- tests/**
push:
@@ -18,7 +18,7 @@ on:
- .github/workflows/continuous-integration.yml
- ci/**
- composer.*
- - lib/**
+ - src/**
- phpunit.xml.dist
- tests/**
diff --git a/.github/workflows/phpbench.yml b/.github/workflows/phpbench.yml
index a0a1efe6c0c..d98e7fa2158 100644
--- a/.github/workflows/phpbench.yml
+++ b/.github/workflows/phpbench.yml
@@ -8,7 +8,7 @@ on:
paths:
- .github/workflows/phpbench.yml
- composer.*
- - lib/**
+ - src/**
- phpbench.json
- tests/**
push:
@@ -17,7 +17,7 @@ on:
paths:
- .github/workflows/phpbench.yml
- composer.*
- - lib/**
+ - src/**
- phpbench.json
- tests/**
diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml
index 921ccf8115c..025f29ea02d 100644
--- a/.github/workflows/static-analysis.yml
+++ b/.github/workflows/static-analysis.yml
@@ -7,7 +7,7 @@ on:
paths:
- .github/workflows/static-analysis.yml
- composer.*
- - lib/**
+ - src/**
- phpstan*
- psalm*
- tests/Doctrine/StaticAnalysis/**
@@ -17,7 +17,7 @@ on:
paths:
- .github/workflows/static-analysis.yml
- composer.*
- - lib/**
+ - src/**
- phpstan*
- psalm*
- tests/Doctrine/StaticAnalysis/**
diff --git a/.gitignore b/.gitignore
index 4b84f4214b1..0b0720faa94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,9 +3,6 @@ logs/
reports/
dist/
download/
-lib/api/
-lib/Doctrine/Common
-lib/Doctrine/DBAL
/.settings/
.buildpath
.project
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 8727b0dadbc..00000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,6 +0,0 @@
-[submodule "docs/en/_theme"]
- path = docs/en/_theme
- url = git://github.com/doctrine/doctrine-sphinx-theme.git
-[submodule "lib/vendor/doctrine-build-common"]
- path = lib/vendor/doctrine-build-common
- url = git://github.com/doctrine/doctrine-build-common.git
diff --git a/README.md b/README.md
index b72a261a80f..22aab0538c4 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
-| [3.0.x][3.0] | [2.16.x][2.16] | [2.15.x][2.15] |
+| [3.0.x][3.0] | [2.18.x][2.18] | [2.17.x][2.17] |
|:----------------:|:----------------:|:----------:|
-| [![Build status][3.0 image]][3.0] | [![Build status][2.16 image]][2.16] | [![Build status][2.15 image]][2.15] |
-| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.16 coverage image]][2.16 coverage] | [![Coverage Status][2.15 coverage image]][2.15 coverage] |
+| [![Build status][3.0 image]][3.0] | [![Build status][2.18 image]][2.18] | [![Build status][2.17 image]][2.17] |
+| [![Coverage Status][3.0 coverage image]][3.0 coverage]| [![Coverage Status][2.18 coverage image]][2.18 coverage] | [![Coverage Status][2.17 coverage image]][2.17 coverage] |
[
πΊπ¦ UKRAINE NEEDS YOUR HELP NOW!
](https://www.doctrine-project.org/stop-war.html)
@@ -22,11 +22,11 @@ without requiring unnecessary code duplication.
[3.0]: https://github.com/doctrine/orm/tree/3.0.x
[3.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.0.x/graph/badge.svg
[3.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.0.x
- [2.16 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.16.x
- [2.16]: https://github.com/doctrine/orm/tree/2.16.x
- [2.16 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.16.x/graph/badge.svg
- [2.16 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.16.x
- [2.15 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.15.x
- [2.15]: https://github.com/doctrine/orm/tree/2.15.x
- [2.15 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.15.x/graph/badge.svg
- [2.15 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.15.x
+ [2.18 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.18.x
+ [2.18]: https://github.com/doctrine/orm/tree/2.18.x
+ [2.18 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.18.x/graph/badge.svg
+ [2.18 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.18.x
+ [2.17 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.17.x
+ [2.17]: https://github.com/doctrine/orm/tree/2.17.x
+ [2.17 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.17.x/graph/badge.svg
+ [2.17 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.17.x
diff --git a/SECURITY.md b/SECURITY.md
index d7013cb4069..b0e72932b29 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -13,6 +13,5 @@ understand the assumptions we make.
- [DBAL Security Page](https://www.doctrine-project.org/projects/doctrine-dbal/en/stable/reference/security.html)
- [ORM Security Page](https://www.doctrine-project.org/projects/doctrine-orm/en/stable/reference/security.html)
-If you find a Security bug in Doctrine, please report it on Jira and change the
-Security Level to "Security Issues". It will be visible to Doctrine Core
-developers and you only.
+If you find a Security bug in Doctrine, please follow our
+[Security reporting guidelines](https://www.doctrine-project.org/policies/security.html#reporting).
diff --git a/docs/en/cookbook/aggregate-fields.rst b/docs/en/cookbook/aggregate-fields.rst
index c9635fa52d6..001d70d34b4 100644
--- a/docs/en/cookbook/aggregate-fields.rst
+++ b/docs/en/cookbook/aggregate-fields.rst
@@ -36,71 +36,50 @@ Our entities look like:
namespace Bank\Entities;
use Doctrine\ORM\Mapping as ORM;
-
- /**
- * @ORM\Entity
- */
+ use Doctrine\Common\Collections\ArrayCollection;
+ use Doctrine\Common\Collections\Collection;
+
+ #[ORM\Entity]
class Account
{
- /**
- * @ORM\Id
- * @ORM\GeneratedValue
- * @ORM\Column(type="integer")
- */
+ #[ORM\Id]
+ #[ORM\GeneratedValue]
+ #[ORM\Column(type: 'integer')]
private ?int $id;
-
- /**
- * @ORM\Column(type="string", unique=true)
- */
- private string $no;
-
- /**
- * @ORM\OneToMany(targetEntity="Entry", mappedBy="account", cascade={"persist"})
- */
- private array $entries;
-
- /**
- * @ORM\Column(type="integer")
- */
- private int $maxCredit = 0;
-
- public function __construct(string $no, int $maxCredit = 0)
- {
- $this->no = $no;
- $this->maxCredit = $maxCredit;
- $this->entries = new \Doctrine\Common\Collections\ArrayCollection();
+
+ #[ORM\OneToMany(targetEntity: Entry::class, mappedBy: 'account', cascade: ['persist'])]
+ private Collection $entries;
+
+
+ public function __construct(
+ #[ORM\Column(type: 'string', unique: true)]
+ private string $no,
+
+ #[ORM\Column(type: 'integer')]
+ private int $maxCredit = 0,
+ ) {
+ $this->entries = new ArrayCollection();
}
}
-
- /**
- * @ORM\Entity
- */
+
+ #[ORM\Entity]
class Entry
{
- /**
- * @ORM\Id
- * @ORM\GeneratedValue
- * @ORM\Column(type="integer")
- */
+ #[ORM\Id]
+ #[ORM\GeneratedValue]
+ #[ORM\Column(type: 'integer')]
private ?int $id;
-
- /**
- * @ORM\ManyToOne(targetEntity="Account", inversedBy="entries")
- */
- private Account $account;
-
- /**
- * @ORM\Column(type="integer")
- */
- private int $amount;
-
- public function __construct(Account $account, int $amount)
- {
- $this->account = $account;
- $this->amount = $amount;
+
+ public function __construct(
+ #[ORM\ManyToOne(targetEntity: Account::class, inversedBy: 'entries')]
+ private Account $account,
+
+ #[ORM\Column(type: 'integer')]
+ private int $amount,
+ ) {
// more stuff here, from/to whom, stated reason, execution date and such
}
-
+
public function getAmount(): Amount
{
return $this->amount;
@@ -193,9 +172,8 @@ relation with this method:
public function addEntry(int $amount): void
{
$this->assertAcceptEntryAllowed($amount);
-
- $e = new Entry($this, $amount);
- $this->entries[] = $e;
+
+ $this->entries[] = new Entry($this, $amount);
}
}
@@ -213,18 +191,18 @@ Now look at the following test-code for our entities:
{
$account = new Account("123456", maxCredit: 200);
$this->assertEquals(0, $account->getBalance());
-
+
$account->addEntry(500);
$this->assertEquals(500, $account->getBalance());
-
+
$account->addEntry(-700);
$this->assertEquals(-200, $account->getBalance());
}
-
+
public function testExceedMaxLimit()
{
$account = new Account("123456", maxCredit: 200);
-
+
$this->expectException(Exception::class);
$account->addEntry(-1000);
}
@@ -285,22 +263,19 @@ entries collection) we want to add an aggregate field called
balance;
}
-
+
public function addEntry(int $amount): void
{
$this->assertAcceptEntryAllowed($amount);
-
- $e = new Entry($this, $amount);
- $this->entries[] = $e;
+
+ $this->entries[] = new Entry($this, $amount);
$this->balance += $amount;
}
}
@@ -331,13 +306,13 @@ potentially lead to inconsistent state. See this example:
// The Account $accId has a balance of 0 and a max credit limit of 200:
// request 1 account
$account1 = $em->find(Account::class, $accId);
-
+
// request 2 account
$account2 = $em->find(Account::class, $accId);
-
+
$account1->addEntry(-200);
$account2->addEntry(-200);
-
+
// now request 1 and 2 both flush the changes.
The aggregate field ``Account::$balance`` is now -200, however the
@@ -357,10 +332,8 @@ Optimistic locking is as easy as adding a version column:
class Account
{
- /**
- * @ORM\Column(type="integer")
- * @ORM\Version
- */
+ #[ORM\Column(type: 'integer')]
+ #[ORM\Version]
private int $version;
}
diff --git a/docs/en/cookbook/resolve-target-entity-listener.rst b/docs/en/cookbook/resolve-target-entity-listener.rst
index 04a50e56043..294c5779af4 100644
--- a/docs/en/cookbook/resolve-target-entity-listener.rst
+++ b/docs/en/cookbook/resolve-target-entity-listener.rst
@@ -47,10 +47,8 @@ A Customer entity
use Acme\CustomerModule\Entity\Customer as BaseCustomer;
use Acme\InvoiceModule\Model\InvoiceSubjectInterface;
- /**
- * @ORM\Entity
- * @ORM\Table(name="customer")
- */
+ #[ORM\Entity]
+ #[ORM\Table(name: 'customer')]
class Customer extends BaseCustomer implements InvoiceSubjectInterface
{
// In our example, any methods defined in the InvoiceSubjectInterface
@@ -69,19 +67,12 @@ An Invoice entity
use Doctrine\ORM\Mapping AS ORM;
use Acme\InvoiceModule\Model\InvoiceSubjectInterface;
- /**
- * Represents an Invoice.
- *
- * @ORM\Entity
- * @ORM\Table(name="invoice")
- */
+ #[ORM\Entity]
+ #[ORM\Table(name: 'invoice')]
class Invoice
{
- /**
- * @ORM\ManyToOne(targetEntity="Acme\InvoiceModule\Model\InvoiceSubjectInterface")
- * @var InvoiceSubjectInterface
- */
- protected $subject;
+ #[ORM\ManyToOne(targetEntity: InvoiceSubjectInterface::class)]
+ protected InvoiceSubjectInterface $subject;
}
An InvoiceSubjectInterface
diff --git a/docs/en/reference/security.rst b/docs/en/reference/security.rst
index 51e6a3903a6..53d2a87ab60 100644
--- a/docs/en/reference/security.rst
+++ b/docs/en/reference/security.rst
@@ -12,9 +12,8 @@ page only handles Security issues in the ORM.
- `DBAL Security Page `
-If you find a Security bug in Doctrine, please report it on Jira and change the
-Security Level to "Security Issues". It will be visible to Doctrine Core
-developers and you only.
+If you find a Security bug in Doctrine, please follow our
+`Security reporting guidelines `_.
User input and Doctrine ORM
---------------------------
diff --git a/phpstan-dbal3.neon b/phpstan-dbal3.neon
index cb6fb45261c..a223cd4b792 100644
--- a/phpstan-dbal3.neon
+++ b/phpstan-dbal3.neon
@@ -31,6 +31,3 @@ parameters:
message: '#Negated boolean expression is always false\.#'
paths:
- src/Mapping/Driver/AttributeDriver.php
- - src/Mapping/Driver/SimplifiedXmlDriver.php
- - src/Mapping/Driver/XmlDriver.php
- - src/ORMSetup.php
diff --git a/phpstan.neon b/phpstan.neon
index 90fda7be9f1..93e3875d269 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -49,6 +49,3 @@ parameters:
message: '#Negated boolean expression is always false\.#'
paths:
- src/Mapping/Driver/AttributeDriver.php
- - src/Mapping/Driver/SimplifiedXmlDriver.php
- - src/Mapping/Driver/XmlDriver.php
- - src/ORMSetup.php
diff --git a/src/Mapping/DefaultTypedFieldMapper.php b/src/Mapping/DefaultTypedFieldMapper.php
index 7a9aadc9254..e83cc9ea47c 100644
--- a/src/Mapping/DefaultTypedFieldMapper.php
+++ b/src/Mapping/DefaultTypedFieldMapper.php
@@ -55,7 +55,15 @@ public function validateAndComplete(array $mapping, ReflectionProperty $field):
$mapping['enumType'] = $type->getName();
$reflection = new ReflectionEnum($type->getName());
- $type = $reflection->getBackingType();
+ if (! $reflection->isBacked()) {
+ throw MappingException::backedEnumTypeRequired(
+ $field->class,
+ $mapping['fieldName'],
+ $mapping['enumType'],
+ );
+ }
+
+ $type = $reflection->getBackingType();
assert($type instanceof ReflectionNamedType);
}
diff --git a/src/Mapping/MappingException.php b/src/Mapping/MappingException.php
index bd50bb25111..9b732427642 100644
--- a/src/Mapping/MappingException.php
+++ b/src/Mapping/MappingException.php
@@ -622,6 +622,16 @@ public static function invalidOverrideType(string $expectdType, mixed $givenValu
));
}
+ public static function backedEnumTypeRequired(string $className, string $fieldName, string $enumType): self
+ {
+ return new self(sprintf(
+ 'Attempting to map a non-backed enum type %s in entity %s::$%s. Please use backed enums only',
+ $enumType,
+ $className,
+ $fieldName,
+ ));
+ }
+
public static function nonEnumTypeMapped(string $className, string $fieldName, string $enumType): self
{
return new self(sprintf(
diff --git a/src/Tools/Pagination/LimitSubqueryOutputWalker.php b/src/Tools/Pagination/LimitSubqueryOutputWalker.php
index 6ae0e2f6375..8bbc44c21a1 100644
--- a/src/Tools/Pagination/LimitSubqueryOutputWalker.php
+++ b/src/Tools/Pagination/LimitSubqueryOutputWalker.php
@@ -232,7 +232,7 @@ public function walkSelectStatementWithoutRowNumber(SelectStatement $AST, bool $
$innerSql,
);
- // http://www.doctrine-project.org/jira/browse/DDC-1958
+ // https://github.com/doctrine/orm/issues/2630
$sql = $this->preserveSqlOrdering($sqlIdentifier, $innerSql, $sql, $orderByClause);
// Apply the limit and offset.
diff --git a/tests/Tests/Models/Enums/FaultySwitch.php b/tests/Tests/Models/Enums/FaultySwitch.php
new file mode 100644
index 00000000000..7f01b239966
--- /dev/null
+++ b/tests/Tests/Models/Enums/FaultySwitch.php
@@ -0,0 +1,22 @@
+name = $name;
+ }
+}
diff --git a/tests/Tests/ORM/Internal/TopologicalSortTest.php b/tests/Tests/ORM/Internal/TopologicalSortTest.php
index 12855a426c0..05a1be05534 100644
--- a/tests/Tests/ORM/Internal/TopologicalSortTest.php
+++ b/tests/Tests/ORM/Internal/TopologicalSortTest.php
@@ -285,14 +285,3 @@ private function computeResult(): array
}, array_values($this->topologicalSort->sort()));
}
}
-
-class Node
-{
- /** @var string */
- public $name;
-
- public function __construct(string $name)
- {
- $this->name = $name;
- }
-}
diff --git a/tests/Tests/ORM/Mapping/TypedEnumFieldMapperTest.php b/tests/Tests/ORM/Mapping/TypedEnumFieldMapperTest.php
new file mode 100644
index 00000000000..3954cff86a3
--- /dev/null
+++ b/tests/Tests/ORM/Mapping/TypedEnumFieldMapperTest.php
@@ -0,0 +1,31 @@
+expectException(MappingException::class);
+ $this->expectExceptionMessage(
+ 'Attempting to map a non-backed enum type Doctrine\Tests\Models\Enums\SwitchStatus in entity Doctrine\Tests\Models\Enums\FaultySwitch::$status. Please use backed enums only',
+ );
+
+ self::defaultTypedFieldMapper()->validateAndComplete(['fieldName' => 'status'], $reflectionClass->getProperty('status'));
+ }
+}