Compare commits

...

37 Commits

Author SHA1 Message Date
Grégoire Paris
40a0964f06 Merge pull request #11289 from themasch/reproduce-issue-11154-composite-key-eager-fetch-one
Do not use batch loading for collections with composite identifier
2024-03-18 20:12:56 +01:00
Grégoire Paris
08a9e60ed0 Remove outdated git metadata files (#11362)
Some of it seems related to the previous documentation build system,
some of it seems related to IntelliJ.
2024-03-17 23:06:30 +01:00
Benjamin Eberlei
3e3c023c95 Switch join columns around, otherwise index doesnt match 2024-03-17 19:50:56 +01:00
Benjamin Eberlei
5e6d5c06a9 Key on fk 2024-03-17 19:43:26 +01:00
Benjamin Eberlei
1622b7877d Fix entities and mapping. 2024-03-17 18:02:11 +01:00
Benjamin Eberlei
80aae2796d Merge pull request #11373 from kaznovac/patch-3
Minor code style fix in AbstractRemoteControl
2024-03-17 17:20:01 +01:00
Marko Kaznovac
528ef40fc4 Minor code style fix in AbstractRemoteControl 2024-03-17 15:55:54 +01:00
Benjamin Eberlei
820a0da4c1 Do not schedule batch loading for target classes with composite identifier. 2024-03-16 23:05:28 +01:00
Benjamin Eberlei
fcd02b1ee2 Cleanup tests not to use model sets. 2024-03-16 23:04:57 +01:00
Grégoire Paris
abcad6fa45 Merge pull request #11090 from dbannik/2.17.x-failed-getting-entity-with-fetch-eager
[2.17.x] Failed getting entity with fetch eager property
2024-03-16 21:23:13 +01:00
Benjamin Eberlei
1b6cf58a1a Rename tables to avoid pg related illegal table name 2024-03-16 21:08:30 +01:00
Benjamin Eberlei
6501890ab5 Static analysis enforces the extra isset() even though that just masks no sense. 2024-03-16 20:48:15 +01:00
Benjamin Eberlei
e399d21fb3 Simplify condition, improve comment on this edge case. 2024-03-16 20:41:24 +01:00
Benjamin Eberlei
16f355f0cc Remove tests for already working case as they add no value other than exploration, and we only need the regression test. 2024-03-16 20:31:09 +01:00
Grégoire Paris
94d45a036f Merge pull request #11347 from greg0ire/remove-orphan
Remove guides-specific markup
2024-03-11 21:08:16 +01:00
Grégoire Paris
9acca2252f Remove guides-specific markup
doctrine/rst-parser does not appear to support orphan metadata yet, and
renders it verbatim on the website.

Let's move this to the CI job.
2024-03-11 20:31:22 +01:00
Alexander M. Turek
a809a71aa6 Prepare releases 2.19 and 3.1 (#11335) 2024-03-03 18:43:41 +01:00
Alexander M. Turek
bd4449c462 Merge branch '2.18.x' into 2.19.x
* 2.18.x:
  Fix annotation
2024-03-03 16:49:22 +01:00
Alexander M. Turek
e3e96745cc Fix annotation 2024-03-03 16:49:00 +01:00
Alexander M. Turek
12e0cefba1 Merge branch '2.18.x' into 2.19.x
* 2.18.x:
  Bump CI workflows (#11336)
  Fix SchemaTool::getSchemaFromMetadata() uniqueConstraint without a predefined name (#11314)
2024-03-03 16:46:50 +01:00
Alexander M. Turek
21221f73cc Bump CI workflows (#11336) 2024-03-03 16:46:12 +01:00
Rok Motaln
ab5e9e393b Fix SchemaTool::getSchemaFromMetadata() uniqueConstraint without a predefined name (#11314)
* Fix loading SchemaTool::getSchemaFromMetadata() uniqueConstraint without a name

Fixes a type miss-match exception when reading a UniqueConstraint defined on an Entity which doesn't have a predefined name.

* Fix deprecation on DBAL 3

---------

Co-authored-by: Alexander M. Turek <me@derrabus.de>
2024-03-03 16:02:48 +01:00
Benjamin Eberlei
e62571c8f4 Refator array_map into simple loop for performance. (#11332) 2024-03-02 23:11:11 +01:00
Alexander M. Turek
98f9de2af6 Merge branch '2.18.x' into 2.19.x
* 2.18.x:
  Psalm 5.22.2 (#11326)
2024-03-01 10:48:12 +01:00
Alexander M. Turek
83c81f6c41 Merge branch '2.18.x' into 2.19.x
* 2.18.x:
  PHPStan 1.10.59 (#11320)
2024-02-29 16:48:49 +01:00
Grégoire Paris
791667a9e4 Merge pull request #11317 from doctrine/2.18.x
Merge 2.18.x up into 2.19.x
2024-02-28 23:05:01 +01:00
Grégoire Paris
abd9186d00 Merge pull request #11309 from greg0ire/deprecate-invalid-call
Deprecate invalid method call
2024-02-26 08:45:50 +01:00
Grégoire Paris
08d3f72755 Deprecate invalid method call
`getAssociationMappedByTargetField()` returns `null` when called with
the owning side of an association.
This is undocumented and wrong because the phpdoc advertises a string as
a return type.

Instead, callers should ensure they are calling that method with an
inverse side.

Closes #11250
2024-02-25 22:09:47 +01:00
Grégoire Paris
779781173a Merge pull request #11301 from doctrine/2.18.x
Merge 2.18.x up into 2.19.x
2024-02-24 20:51:45 +01:00
Alexander M. Turek
c9c493b2fe Merge branch '2.18.x' into 2.19.x
* 2.18.x:
  Backport QueryParameterTest (#11288)
2024-02-22 13:23:21 +01:00
Mark Schmale
8d4718f875 provides a test case for github issue 11154
After 2.17 (some?) EAGER fetched OneToMany associations stopped working, if they have multiple join columns. Loads for these associations will trigger a `MessingPositionalParameter` exception "Positional parameter at index 1 does not have a bound value".

This test case should reproduce this issue, so it can be fixed.
2024-02-22 10:58:50 +01:00
Alexander M. Turek
c0dfba2ef3 Merge branch '2.18.x' into 2.19.x
* 2.18.x:
  Fix Static Analysis folder reference (#11281)
  docs: recommend safer way to disable logging (#11269)
  Remove unused baseline entries
  Treat '0' as a legitimate trim char
2024-02-21 18:52:54 +01:00
Grégoire Paris
b59189ab48 Merge pull request #11267 from doctrine/2.18.x
Merge 2.18.x up into 2.19.x
2024-02-17 20:00:05 +01:00
Dmitry Bannik
e5e3166747 #11090 - Fix obtaining an identifier in cases where the hydration has not yet fully completed on eagerLoadCollections 2024-02-16 12:57:23 +03:00
Grégoire Paris
afbf293c94 Merge pull request #11255 from doctrine/2.18.x
Merge 2.18.x up into 2.19.x
2024-02-13 12:07:19 +01:00
Alexander M. Turek
7baef1e120 Remove references to deprecated constants from Lexer (#11234) 2024-02-07 15:39:20 +01:00
Karoly Gossler
5049b615c5 Add TokenType class (#11228)
* Add TokenType class
Co-authored-by: Alexander M. Turek <me@derrabus.de>

* Deprecated Lexer constants in favour of TokenType

* Replace all Lexer::T_ occurrences with TokenType::T_

* Add upgrade note

* Fixed import Lexer => TokenType

* Fixed deprecation phpdoc

* Replaced int value with matching constant of TokenType

* Update src/Query/Lexer.php

---------

Co-authored-by: Alexander M. Turek <me@derrabus.de>
2024-02-07 13:31:08 +01:00
54 changed files with 1320 additions and 678 deletions

View File

@@ -5,23 +5,47 @@
"slug": "orm",
"docsSlug": "doctrine-orm",
"versions": [
{
"name": "4.0",
"branchName": "4.0.x",
"slug": "latest",
"upcoming": true
},
{
"name": "3.2",
"branchName": "3.2.x",
"slug": "3.2",
"upcoming": true
},
{
"name": "3.1",
"branchName": "3.1.x",
"slug": "3.1",
"current": true
},
{
"name": "3.0",
"branchName": "3.0.x",
"slug": "latest",
"slug": "3.0",
"maintained": false
},
{
"name": "2.20",
"branchName": "2.20.x",
"slug": "2.20",
"upcoming": true
},
{
"name": "2.19",
"branchName": "2.19.x",
"slug": "2.19",
"upcoming": true
"maintained": true
},
{
"name": "2.18",
"branchName": "2.18.x",
"slug": "2.18",
"current": true
"maintained": false
},
{
"name": "2.17",

View File

@@ -97,9 +97,9 @@ jobs:
ORM_PROXY_IMPLEMENTATION: "${{ matrix.proxy }}"
- name: "Upload coverage file"
uses: "actions/upload-artifact@v3"
uses: "actions/upload-artifact@v4"
with:
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
name: "phpunit-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.proxy }}-coverage"
path: "coverage*.xml"
@@ -170,9 +170,9 @@ jobs:
run: "vendor/bin/phpunit -c ci/github/phpunit/pdo_pgsql.xml --coverage-clover=coverage.xml"
- name: "Upload coverage file"
uses: "actions/upload-artifact@v3"
uses: "actions/upload-artifact@v4"
with:
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
name: "${{ github.job }}-${{ matrix.postgres-version }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-${{ matrix.extension }}-coverage"
path: "coverage.xml"
@@ -240,7 +240,7 @@ jobs:
run: "vendor/bin/phpunit -c ci/github/phpunit/${{ matrix.extension }}.xml --coverage-clover=coverage.xml"
- name: "Upload coverage file"
uses: "actions/upload-artifact@v3"
uses: "actions/upload-artifact@v4"
with:
name: "${{ github.job }}-${{ matrix.mariadb-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
path: "coverage.xml"
@@ -317,7 +317,7 @@ jobs:
ENABLE_SECOND_LEVEL_CACHE: 1
- name: "Upload coverage files"
uses: "actions/upload-artifact@v3"
uses: "actions/upload-artifact@v4"
with:
name: "${{ github.job }}-${{ matrix.mysql-version }}-${{ matrix.extension }}-${{ matrix.php-version }}-${{ matrix.dbal-version }}-coverage"
path: "coverage*.xml"
@@ -372,7 +372,7 @@ jobs:
fetch-depth: 2
- name: "Download coverage files"
uses: "actions/download-artifact@v3"
uses: "actions/download-artifact@v4"
with:
path: "reports"

View File

@@ -27,7 +27,7 @@ jobs:
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "8.2"
php-version: "8.3"
- name: "Remove existing composer file"
run: "rm composer.json"
@@ -40,5 +40,10 @@ jobs:
with:
dependency-versions: "highest"
- name: "Add orphan metadata where needed"
run: |
printf '%s\n\n%s\n' ":orphan:" "$(cat docs/en/sidebar.rst)" > docs/en/sidebar.rst
printf '%s\n\n%s\n' ":orphan:" "$(cat docs/en/reference/installation.rst)" > docs/en/reference/installation.rst
- name: "Run guides-cli"
run: "vendor/bin/guides -vvv --no-progress docs/en 2>&1 | grep -v 'No template found for rendering directive' | ( ! grep WARNING )"

View File

@@ -7,7 +7,7 @@ on:
jobs:
release:
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@3.0.0"
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@4.0.0"
secrets:
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}

View File

@@ -48,7 +48,7 @@ jobs:
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "8.2"
php-version: "8.3"
- name: "Require specific DBAL version"
run: "composer require doctrine/dbal ^${{ matrix.dbal-version }} --no-update"
@@ -89,7 +89,7 @@ jobs:
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "8.2"
php-version: "8.3"
- name: "Require specific persistence version"
run: "composer require doctrine/persistence ^3.1 --no-update"

View File

@@ -1,7 +1,7 @@
| [4.0.x][4.0] | [3.1.x][3.1] | [3.0.x][3.0] | [2.19.x][2.19] | [2.18.x][2.18] |
|:------------------------------------------------------:|:------------------------------------------------------:|:-------------------------------------------------------:|:--------------------------------------------------------:|:---------------------------------------------------------:|
| [![Build status][4.0 image]][4.0] | [![Build status][3.1 image]][3.1] | [![Build status][3.0 image]][3.0] | [![Build status][2.19 image]][2.19] | [![Build status][2.18 image]][2.18] |
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.1 coverage image]][3.1 coverage] | [![Coverage Status][3.0 coverage image]][3.0 coverage] | [![Coverage Status][2.19 coverage image]][2.19 coverage] | [![Coverage Status][2.18 coverage image]][2.18 coverage] |
| [4.0.x][4.0] | [3.2.x][3.2] | [3.1.x][3.1] | [2.20.x][2.20] | [2.19.x][2.19] |
|:------------------------------------------------------:|:------------------------------------------------------:|:------------------------------------------------------:|:--------------------------------------------------------:|:--------------------------------------------------------:|
| [![Build status][4.0 image]][4.0] | [![Build status][3.2 image]][3.2] | [![Build status][3.1 image]][3.1] | [![Build status][2.20 image]][2.20] | [![Build status][2.19 image]][2.19] |
| [![Coverage Status][4.0 coverage image]][4.0 coverage] | [![Coverage Status][3.2 coverage image]][3.2 coverage] | [![Coverage Status][3.1 coverage image]][3.1 coverage] | [![Coverage Status][2.20 coverage image]][2.20 coverage] | [![Coverage Status][2.19 coverage image]][2.19 coverage] |
[<h1 align="center">🇺🇦 UKRAINE NEEDS YOUR HELP NOW!</h1>](https://www.doctrine-project.org/stop-war.html)
@@ -22,19 +22,19 @@ without requiring unnecessary code duplication.
[4.0]: https://github.com/doctrine/orm/tree/4.0.x
[4.0 coverage image]: https://codecov.io/gh/doctrine/orm/branch/4.0.x/graph/badge.svg
[4.0 coverage]: https://codecov.io/gh/doctrine/orm/branch/4.0.x
[3.2 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.2.x
[3.2]: https://github.com/doctrine/orm/tree/3.2.x
[3.2 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.2.x/graph/badge.svg
[3.2 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.2.x
[3.1 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.1.x
[3.1]: https://github.com/doctrine/orm/tree/3.1.x
[3.1 coverage image]: https://codecov.io/gh/doctrine/orm/branch/3.1.x/graph/badge.svg
[3.1 coverage]: https://codecov.io/gh/doctrine/orm/branch/3.1.x
[3.0 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=3.0.x
[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.20 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.20.x
[2.20]: https://github.com/doctrine/orm/tree/2.20.x
[2.20 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.20.x/graph/badge.svg
[2.20 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.20.x
[2.19 image]: https://github.com/doctrine/orm/actions/workflows/continuous-integration.yml/badge.svg?branch=2.19.x
[2.19]: https://github.com/doctrine/orm/tree/2.19.x
[2.19 coverage image]: https://codecov.io/gh/doctrine/orm/branch/2.19.x/graph/badge.svg
[2.19 coverage]: https://codecov.io/gh/doctrine/orm/branch/2.19.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

View File

@@ -1,3 +1,20 @@
# Upgrade to 2.19
## Deprecate calling `ClassMetadata::getAssociationMappedByTargetField()` with the owning side of an association
Calling
`Doctrine\ORM\Mapping\ClassMetadata::getAssociationMappedByTargetField()` with
the owning side of an association returns `null`, which is undocumented, and
wrong according to the phpdoc of the parent method.
If you do not know whether you are on the owning or inverse side of an association,
you can use `Doctrine\ORM\Mapping\ClassMetadata::isAssociationInverseSide()`
to find out.
## Deprecate `Doctrine\ORM\Query\Lexer::T_*` constants
Use `Doctrine\ORM\Query\TokenType::T_*` instead.
# Upgrade to 2.17
## Deprecate annotations classes for named queries

4
docs/.gitignore vendored
View File

@@ -1,4 +0,0 @@
en/_exts/configurationblock.pyc
build
en/_build
.idea

3
docs/.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "en/_theme"]
path = en/_theme
url = https://github.com/doctrine/doctrine-sphinx-theme.git

View File

@@ -99,12 +99,12 @@ discuss it step by step:
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER); // (2)
$parser->match(Lexer::T_OPEN_PARENTHESIS); // (3)
$parser->match(TokenType::T_IDENTIFIER); // (2)
$parser->match(TokenType::T_OPEN_PARENTHESIS); // (3)
$this->firstDateExpression = $parser->ArithmeticPrimary(); // (4)
$parser->match(Lexer::T_COMMA); // (5)
$parser->match(TokenType::T_COMMA); // (5)
$this->secondDateExpression = $parser->ArithmeticPrimary(); // (6)
$parser->match(Lexer::T_CLOSE_PARENTHESIS); // (3)
$parser->match(TokenType::T_CLOSE_PARENTHESIS); // (3)
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
@@ -183,23 +183,23 @@ I'll skip the blah and show the code for this function:
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->firstDateExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(TokenType::T_COMMA);
$parser->match(TokenType::T_IDENTIFIER);
$this->intervalExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(TokenType::T_IDENTIFIER);
/** @var Lexer $lexer */
$lexer = $parser->getLexer();
$this->unit = $lexer->token['value'];
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)

View File

@@ -812,7 +812,7 @@ classes have to implement the base class :
namespace MyProject\Query\AST;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\TokenType;
class MysqlFloor extends FunctionNode
{
@@ -827,12 +827,12 @@ classes have to implement the base class :
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -1,5 +1,3 @@
:orphan:
Installation
============

View File

@@ -1,5 +1,3 @@
:orphan:
.. toc::
.. tocheader:: Tutorials

View File

@@ -2108,7 +2108,7 @@
</PossiblyUndefinedVariable>
<RedundantConditionGivenDocblockType>
<code><![CDATA[$AST instanceof AST\SelectStatement]]></code>
<code><![CDATA[$token === Lexer::T_IDENTIFIER]]></code>
<code><![CDATA[$token === TokenType::T_IDENTIFIER]]></code>
</RedundantConditionGivenDocblockType>
</file>
<file src="src/Query/ParserResult.php">

View File

@@ -3681,6 +3681,17 @@ class ClassMetadataInfo implements ClassMetadata
*/
public function getAssociationMappedByTargetField($fieldName)
{
if (! $this->isAssociationInverseSide($fieldName)) {
Deprecation::trigger(
'doctrine/orm',
'https://github.com/doctrine/orm/pull/11309',
'Calling %s with owning side field %s is deprecated and will no longer be supported in Doctrine ORM 3.0. Call %s::isAssociationInverseSide() to check first.',
__METHOD__,
$fieldName,
self::class
);
}
return $this->associationMappings[$fieldName]['mappedBy'];
}

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "ABS" "(" SimpleArithmeticExpression ")"
@@ -30,11 +30,11 @@ class AbsFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "BIT_AND" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
@@ -36,13 +36,13 @@ class BitAndFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->firstArithmetic = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(TokenType::T_COMMA);
$this->secondArithmetic = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "BIT_OR" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
@@ -36,13 +36,13 @@ class BitOrFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->firstArithmetic = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(TokenType::T_COMMA);
$this->secondArithmetic = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "CONCAT" "(" StringPrimary "," StringPrimary {"," StringPrimary }* ")"
@@ -42,22 +42,22 @@ class ConcatFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->firstStringPrimary = $parser->StringPrimary();
$this->concatExpressions[] = $this->firstStringPrimary;
$parser->match(Lexer::T_COMMA);
$parser->match(TokenType::T_COMMA);
$this->secondStringPrimary = $parser->StringPrimary();
$this->concatExpressions[] = $this->secondStringPrimary;
while ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) {
$parser->match(Lexer::T_COMMA);
while ($parser->getLexer()->isNextToken(TokenType::T_COMMA)) {
$parser->match(TokenType::T_COMMA);
$this->concatExpressions[] = $parser->StringPrimary();
}
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "CURRENT_DATE"
@@ -24,8 +24,8 @@ class CurrentDateFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "CURRENT_TIME"
@@ -24,8 +24,8 @@ class CurrentTimeFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -4,9 +4,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "CURRENT_TIMESTAMP"
@@ -24,8 +24,8 @@ class CurrentTimestampFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -5,10 +5,10 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use function strtolower;
@@ -84,15 +84,15 @@ class DateAddFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->firstDateExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(TokenType::T_COMMA);
$this->intervalExpression = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(TokenType::T_COMMA);
$this->unit = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "DATE_DIFF" "(" ArithmeticPrimary "," ArithmeticPrimary ")"
@@ -34,13 +34,13 @@ class DateDiffFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->date1 = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(TokenType::T_COMMA);
$this->date2 = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -5,10 +5,10 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use function assert;
use function reset;
@@ -77,20 +77,20 @@ class IdentityFunction extends FunctionNode
*/
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->pathExpression = $parser->SingleValuedAssociationPathExpression();
if ($parser->getLexer()->isNextToken(Lexer::T_COMMA)) {
$parser->match(Lexer::T_COMMA);
$parser->match(Lexer::T_STRING);
if ($parser->getLexer()->isNextToken(TokenType::T_COMMA)) {
$parser->match(TokenType::T_COMMA);
$parser->match(TokenType::T_STRING);
$token = $parser->getLexer()->token;
assert($token !== null);
$this->fieldMapping = $token->value;
}
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -8,9 +8,9 @@ use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\TypedExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "LENGTH" "(" StringPrimary ")"
@@ -33,12 +33,12 @@ class LengthFunction extends FunctionNode implements TypedExpression
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
public function getReturnType(): Type

View File

@@ -6,9 +6,9 @@ namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "LOCATE" "(" StringPrimary "," StringPrimary ["," SimpleArithmeticExpression]")"
@@ -48,22 +48,22 @@ class LocateFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->firstStringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(TokenType::T_COMMA);
$this->secondStringPrimary = $parser->StringPrimary();
$lexer = $parser->getLexer();
if ($lexer->isNextToken(Lexer::T_COMMA)) {
$parser->match(Lexer::T_COMMA);
if ($lexer->isNextToken(TokenType::T_COMMA)) {
$parser->match(TokenType::T_COMMA);
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
}
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use function sprintf;
@@ -33,11 +33,11 @@ class LowerFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "MOD" "(" SimpleArithmeticExpression "," SimpleArithmeticExpression ")"
@@ -34,15 +34,15 @@ class ModFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_COMMA);
$parser->match(TokenType::T_COMMA);
$this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -6,9 +6,9 @@ namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use function assert;
@@ -105,11 +105,11 @@ class SizeFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->collectionPathExpression = $parser->CollectionValuedPathExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use function sprintf;
@@ -33,11 +33,11 @@ class SqrtFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -6,9 +6,9 @@ namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
/**
* "SUBSTRING" "(" StringPrimary "," SimpleArithmeticExpression "," SimpleArithmeticExpression ")"
@@ -44,22 +44,22 @@ class SubstringFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(TokenType::T_COMMA);
$this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$lexer = $parser->getLexer();
if ($lexer->isNextToken(Lexer::T_COMMA)) {
$parser->match(Lexer::T_COMMA);
if ($lexer->isNextToken(TokenType::T_COMMA)) {
$parser->match(TokenType::T_COMMA);
$this->secondSimpleArithmeticExpression = $parser->SimpleArithmeticExpression();
}
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -6,9 +6,9 @@ namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\DBAL\Platforms\TrimMode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use function assert;
use function strcasecmp;
@@ -62,25 +62,25 @@ class TrimFunction extends FunctionNode
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->parseTrimMode($parser);
if ($lexer->isNextToken(Lexer::T_STRING)) {
$parser->match(Lexer::T_STRING);
if ($lexer->isNextToken(TokenType::T_STRING)) {
$parser->match(TokenType::T_STRING);
assert($lexer->token !== null);
$this->trimChar = $lexer->token->value;
}
if ($this->leading || $this->trailing || $this->both || ($this->trimChar !== false)) {
$parser->match(Lexer::T_FROM);
$parser->match(TokenType::T_FROM);
}
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
/** @psalm-return TrimMode::* */
@@ -108,7 +108,7 @@ class TrimFunction extends FunctionNode
$value = $lexer->lookahead->value;
if (strcasecmp('leading', $value) === 0) {
$parser->match(Lexer::T_LEADING);
$parser->match(TokenType::T_LEADING);
$this->leading = true;
@@ -116,7 +116,7 @@ class TrimFunction extends FunctionNode
}
if (strcasecmp('trailing', $value) === 0) {
$parser->match(Lexer::T_TRAILING);
$parser->match(TokenType::T_TRAILING);
$this->trailing = true;
@@ -124,7 +124,7 @@ class TrimFunction extends FunctionNode
}
if (strcasecmp('both', $value) === 0) {
$parser->match(Lexer::T_BOTH);
$parser->match(TokenType::T_BOTH);
$this->both = true;

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Doctrine\ORM\Query\AST\Functions;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use function sprintf;
@@ -33,11 +33,11 @@ class UpperFunction extends FunctionNode
/** @inheritDoc */
public function parse(Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->stringPrimary = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}

View File

@@ -21,95 +21,249 @@ use function substr;
/**
* Scans a DQL query for tokens.
*
* @extends AbstractLexer<Lexer::T_*, string>
* @extends AbstractLexer<TokenType::T_*, string>
*/
class Lexer extends AbstractLexer
{
// All tokens that are not valid identifiers must be < 100
public const T_NONE = 1;
public const T_INTEGER = 2;
public const T_STRING = 3;
public const T_INPUT_PARAMETER = 4;
public const T_FLOAT = 5;
public const T_CLOSE_PARENTHESIS = 6;
public const T_OPEN_PARENTHESIS = 7;
public const T_COMMA = 8;
public const T_DIVIDE = 9;
public const T_DOT = 10;
public const T_EQUALS = 11;
public const T_GREATER_THAN = 12;
public const T_LOWER_THAN = 13;
public const T_MINUS = 14;
public const T_MULTIPLY = 15;
public const T_NEGATE = 16;
public const T_PLUS = 17;
public const T_OPEN_CURLY_BRACE = 18;
public const T_CLOSE_CURLY_BRACE = 19;
/** @deprecated use {@see TokenType::T_NONE} */
public const T_NONE = TokenType::T_NONE;
/** @deprecated use {@see TokenType::T_INTEGER} */
public const T_INTEGER = TokenType::T_INTEGER;
/** @deprecated use {@see TokenType::T_STRING} */
public const T_STRING = TokenType::T_STRING;
/** @deprecated use {@see TokenType::T_INPUT_PARAMETER} */
public const T_INPUT_PARAMETER = TokenType::T_INPUT_PARAMETER;
/** @deprecated use {@see TokenType::T_FLOAT} */
public const T_FLOAT = TokenType::T_FLOAT;
/** @deprecated use {@see TokenType::T_CLOSE_PARENTHESIS} */
public const T_CLOSE_PARENTHESIS = TokenType::T_CLOSE_PARENTHESIS;
/** @deprecated use {@see TokenType::T_OPEN_PARENTHESIS} */
public const T_OPEN_PARENTHESIS = TokenType::T_OPEN_PARENTHESIS;
/** @deprecated use {@see TokenType::T_COMMA} */
public const T_COMMA = TokenType::T_COMMA;
/** @deprecated use {@see TokenType::T_DIVIDE} */
public const T_DIVIDE = TokenType::T_DIVIDE;
/** @deprecated use {@see TokenType::T_DOT} */
public const T_DOT = TokenType::T_DOT;
/** @deprecated use {@see TokenType::T_EQUALS} */
public const T_EQUALS = TokenType::T_EQUALS;
/** @deprecated use {@see TokenType::T_GREATER_THAN} */
public const T_GREATER_THAN = TokenType::T_GREATER_THAN;
/** @deprecated use {@see TokenType::T_LOWER_THAN} */
public const T_LOWER_THAN = TokenType::T_LOWER_THAN;
/** @deprecated use {@see TokenType::T_MINUS} */
public const T_MINUS = TokenType::T_MINUS;
/** @deprecated use {@see TokenType::T_MULTIPLY} */
public const T_MULTIPLY = TokenType::T_MULTIPLY;
/** @deprecated use {@see TokenType::T_NEGATE} */
public const T_NEGATE = TokenType::T_NEGATE;
/** @deprecated use {@see TokenType::T_PLUS} */
public const T_PLUS = TokenType::T_PLUS;
/** @deprecated use {@see TokenType::T_OPEN_CURLY_BRACE} */
public const T_OPEN_CURLY_BRACE = TokenType::T_OPEN_CURLY_BRACE;
/** @deprecated use {@see TokenType::T_CLOSE_CURLY_BRACE} */
public const T_CLOSE_CURLY_BRACE = TokenType::T_CLOSE_CURLY_BRACE;
// All tokens that are identifiers or keywords that could be considered as identifiers should be >= 100
/** @deprecated No Replacement planned. */
public const T_ALIASED_NAME = 100;
public const T_FULLY_QUALIFIED_NAME = 101;
public const T_IDENTIFIER = 102;
public const T_ALIASED_NAME = TokenType::T_ALIASED_NAME;
/** @deprecated use {@see TokenType::T_FULLY_QUALIFIED_NAME} */
public const T_FULLY_QUALIFIED_NAME = TokenType::T_FULLY_QUALIFIED_NAME;
/** @deprecated use {@see TokenType::T_IDENTIFIER} */
public const T_IDENTIFIER = TokenType::T_IDENTIFIER;
// All keyword tokens should be >= 200
public const T_ALL = 200;
public const T_AND = 201;
public const T_ANY = 202;
public const T_AS = 203;
public const T_ASC = 204;
public const T_AVG = 205;
public const T_BETWEEN = 206;
public const T_BOTH = 207;
public const T_BY = 208;
public const T_CASE = 209;
public const T_COALESCE = 210;
public const T_COUNT = 211;
public const T_DELETE = 212;
public const T_DESC = 213;
public const T_DISTINCT = 214;
public const T_ELSE = 215;
public const T_EMPTY = 216;
public const T_END = 217;
public const T_ESCAPE = 218;
public const T_EXISTS = 219;
public const T_FALSE = 220;
public const T_FROM = 221;
public const T_GROUP = 222;
public const T_HAVING = 223;
public const T_HIDDEN = 224;
public const T_IN = 225;
public const T_INDEX = 226;
public const T_INNER = 227;
public const T_INSTANCE = 228;
public const T_IS = 229;
public const T_JOIN = 230;
public const T_LEADING = 231;
public const T_LEFT = 232;
public const T_LIKE = 233;
public const T_MAX = 234;
public const T_MEMBER = 235;
public const T_MIN = 236;
public const T_NEW = 237;
public const T_NOT = 238;
public const T_NULL = 239;
public const T_NULLIF = 240;
public const T_OF = 241;
public const T_OR = 242;
public const T_ORDER = 243;
public const T_OUTER = 244;
public const T_PARTIAL = 245;
public const T_SELECT = 246;
public const T_SET = 247;
public const T_SOME = 248;
public const T_SUM = 249;
public const T_THEN = 250;
public const T_TRAILING = 251;
public const T_TRUE = 252;
public const T_UPDATE = 253;
public const T_WHEN = 254;
public const T_WHERE = 255;
public const T_WITH = 256;
/** @deprecated use {@see TokenType::T_ALL} */
public const T_ALL = TokenType::T_ALL;
/** @deprecated use {@see TokenType::T_AND} */
public const T_AND = TokenType::T_AND;
/** @deprecated use {@see TokenType::T_ANY} */
public const T_ANY = TokenType::T_ANY;
/** @deprecated use {@see TokenType::T_AS} */
public const T_AS = TokenType::T_AS;
/** @deprecated use {@see TokenType::T_ASC} */
public const T_ASC = TokenType::T_ASC;
/** @deprecated use {@see TokenType::T_AVG} */
public const T_AVG = TokenType::T_AVG;
/** @deprecated use {@see TokenType::T_BETWEEN} */
public const T_BETWEEN = TokenType::T_BETWEEN;
/** @deprecated use {@see TokenType::T_BOTH} */
public const T_BOTH = TokenType::T_BOTH;
/** @deprecated use {@see TokenType::T_BY} */
public const T_BY = TokenType::T_BY;
/** @deprecated use {@see TokenType::T_CASE} */
public const T_CASE = TokenType::T_CASE;
/** @deprecated use {@see TokenType::T_COALESCE} */
public const T_COALESCE = TokenType::T_COALESCE;
/** @deprecated use {@see TokenType::T_COUNT} */
public const T_COUNT = TokenType::T_COUNT;
/** @deprecated use {@see TokenType::T_DELETE} */
public const T_DELETE = TokenType::T_DELETE;
/** @deprecated use {@see TokenType::T_DESC} */
public const T_DESC = TokenType::T_DESC;
/** @deprecated use {@see TokenType::T_DISTINCT} */
public const T_DISTINCT = TokenType::T_DISTINCT;
/** @deprecated use {@see TokenType::T_ELSE} */
public const T_ELSE = TokenType::T_ELSE;
/** @deprecated use {@see TokenType::T_EMPTY} */
public const T_EMPTY = TokenType::T_EMPTY;
/** @deprecated use {@see TokenType::T_END} */
public const T_END = TokenType::T_END;
/** @deprecated use {@see TokenType::T_ESCAPE} */
public const T_ESCAPE = TokenType::T_ESCAPE;
/** @deprecated use {@see TokenType::T_EXISTS} */
public const T_EXISTS = TokenType::T_EXISTS;
/** @deprecated use {@see TokenType::T_FALSE} */
public const T_FALSE = TokenType::T_FALSE;
/** @deprecated use {@see TokenType::T_FROM} */
public const T_FROM = TokenType::T_FROM;
/** @deprecated use {@see TokenType::T_GROUP} */
public const T_GROUP = TokenType::T_GROUP;
/** @deprecated use {@see TokenType::T_HAVING} */
public const T_HAVING = TokenType::T_HAVING;
/** @deprecated use {@see TokenType::T_HIDDEN} */
public const T_HIDDEN = TokenType::T_HIDDEN;
/** @deprecated use {@see TokenType::T_IN} */
public const T_IN = TokenType::T_IN;
/** @deprecated use {@see TokenType::T_INDEX} */
public const T_INDEX = TokenType::T_INDEX;
/** @deprecated use {@see TokenType::T_INNER} */
public const T_INNER = TokenType::T_INNER;
/** @deprecated use {@see TokenType::T_INSTANCE} */
public const T_INSTANCE = TokenType::T_INSTANCE;
/** @deprecated use {@see TokenType::T_IS} */
public const T_IS = TokenType::T_IS;
/** @deprecated use {@see TokenType::T_JOIN} */
public const T_JOIN = TokenType::T_JOIN;
/** @deprecated use {@see TokenType::T_LEADING} */
public const T_LEADING = TokenType::T_LEADING;
/** @deprecated use {@see TokenType::T_LEFT} */
public const T_LEFT = TokenType::T_LEFT;
/** @deprecated use {@see TokenType::T_LIKE} */
public const T_LIKE = TokenType::T_LIKE;
/** @deprecated use {@see TokenType::T_MAX} */
public const T_MAX = TokenType::T_MAX;
/** @deprecated use {@see TokenType::T_MEMBER} */
public const T_MEMBER = TokenType::T_MEMBER;
/** @deprecated use {@see TokenType::T_MIN} */
public const T_MIN = TokenType::T_MIN;
/** @deprecated use {@see TokenType::T_NEW} */
public const T_NEW = TokenType::T_NEW;
/** @deprecated use {@see TokenType::T_NOT} */
public const T_NOT = TokenType::T_NOT;
/** @deprecated use {@see TokenType::T_NULL} */
public const T_NULL = TokenType::T_NULL;
/** @deprecated use {@see TokenType::T_NULLIF} */
public const T_NULLIF = TokenType::T_NULLIF;
/** @deprecated use {@see TokenType::T_OF} */
public const T_OF = TokenType::T_OF;
/** @deprecated use {@see TokenType::T_OR} */
public const T_OR = TokenType::T_OR;
/** @deprecated use {@see TokenType::T_ORDER} */
public const T_ORDER = TokenType::T_ORDER;
/** @deprecated use {@see TokenType::T_OUTER} */
public const T_OUTER = TokenType::T_OUTER;
/** @deprecated use {@see TokenType::T_PARTIAL} */
public const T_PARTIAL = TokenType::T_PARTIAL;
/** @deprecated use {@see TokenType::T_SELECT} */
public const T_SELECT = TokenType::T_SELECT;
/** @deprecated use {@see TokenType::T_SET} */
public const T_SET = TokenType::T_SET;
/** @deprecated use {@see TokenType::T_SOME} */
public const T_SOME = TokenType::T_SOME;
/** @deprecated use {@see TokenType::T_SUM} */
public const T_SUM = TokenType::T_SUM;
/** @deprecated use {@see TokenType::T_THEN} */
public const T_THEN = TokenType::T_THEN;
/** @deprecated use {@see TokenType::T_TRAILING} */
public const T_TRAILING = TokenType::T_TRAILING;
/** @deprecated use {@see TokenType::T_TRUE} */
public const T_TRUE = TokenType::T_TRUE;
/** @deprecated use {@see TokenType::T_UPDATE} */
public const T_UPDATE = TokenType::T_UPDATE;
/** @deprecated use {@see TokenType::T_WHEN} */
public const T_WHEN = TokenType::T_WHEN;
/** @deprecated use {@see TokenType::T_WHERE} */
public const T_WHERE = TokenType::T_WHERE;
/** @deprecated use {@see TokenType::T_WITH} */
public const T_WITH = TokenType::T_WITH;
/**
* Creates a new query scanner object.
@@ -150,26 +304,26 @@ class Lexer extends AbstractLexer
*/
protected function getType(&$value)
{
$type = self::T_NONE;
$type = TokenType::T_NONE;
switch (true) {
// Recognize numeric values
case is_numeric($value):
if (str_contains($value, '.') || stripos($value, 'e') !== false) {
return self::T_FLOAT;
return TokenType::T_FLOAT;
}
return self::T_INTEGER;
return TokenType::T_INTEGER;
// Recognize quoted strings
case $value[0] === "'":
$value = str_replace("''", "'", substr($value, 1, strlen($value) - 2));
return self::T_STRING;
return TokenType::T_STRING;
// Recognize identifiers, aliased or qualified names
case ctype_alpha($value[0]) || $value[0] === '_' || $value[0] === '\\':
$name = 'Doctrine\ORM\Query\Lexer::T_' . strtoupper($value);
$name = 'Doctrine\ORM\Query\TokenType::T_' . strtoupper($value);
if (defined($name)) {
$type = constant($name);
@@ -187,61 +341,61 @@ class Lexer extends AbstractLexer
$value
);
return self::T_ALIASED_NAME;
return TokenType::T_ALIASED_NAME;
}
if (str_contains($value, '\\')) {
return self::T_FULLY_QUALIFIED_NAME;
return TokenType::T_FULLY_QUALIFIED_NAME;
}
return self::T_IDENTIFIER;
return TokenType::T_IDENTIFIER;
// Recognize input parameters
case $value[0] === '?' || $value[0] === ':':
return self::T_INPUT_PARAMETER;
return TokenType::T_INPUT_PARAMETER;
// Recognize symbols
case $value === '.':
return self::T_DOT;
return TokenType::T_DOT;
case $value === ',':
return self::T_COMMA;
return TokenType::T_COMMA;
case $value === '(':
return self::T_OPEN_PARENTHESIS;
return TokenType::T_OPEN_PARENTHESIS;
case $value === ')':
return self::T_CLOSE_PARENTHESIS;
return TokenType::T_CLOSE_PARENTHESIS;
case $value === '=':
return self::T_EQUALS;
return TokenType::T_EQUALS;
case $value === '>':
return self::T_GREATER_THAN;
return TokenType::T_GREATER_THAN;
case $value === '<':
return self::T_LOWER_THAN;
return TokenType::T_LOWER_THAN;
case $value === '+':
return self::T_PLUS;
return TokenType::T_PLUS;
case $value === '-':
return self::T_MINUS;
return TokenType::T_MINUS;
case $value === '*':
return self::T_MULTIPLY;
return TokenType::T_MULTIPLY;
case $value === '/':
return self::T_DIVIDE;
return TokenType::T_DIVIDE;
case $value === '!':
return self::T_NEGATE;
return TokenType::T_NEGATE;
case $value === '{':
return self::T_OPEN_CURLY_BRACE;
return TokenType::T_OPEN_CURLY_BRACE;
case $value === '}':
return self::T_CLOSE_CURLY_BRACE;
return TokenType::T_CLOSE_CURLY_BRACE;
// Default
default:

File diff suppressed because it is too large Load Diff

99
src/Query/TokenType.php Normal file
View File

@@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
namespace Doctrine\ORM\Query;
final class TokenType
{
// All tokens that are not valid identifiers must be < 100
public const T_NONE = 1;
public const T_INTEGER = 2;
public const T_STRING = 3;
public const T_INPUT_PARAMETER = 4;
public const T_FLOAT = 5;
public const T_CLOSE_PARENTHESIS = 6;
public const T_OPEN_PARENTHESIS = 7;
public const T_COMMA = 8;
public const T_DIVIDE = 9;
public const T_DOT = 10;
public const T_EQUALS = 11;
public const T_GREATER_THAN = 12;
public const T_LOWER_THAN = 13;
public const T_MINUS = 14;
public const T_MULTIPLY = 15;
public const T_NEGATE = 16;
public const T_PLUS = 17;
public const T_OPEN_CURLY_BRACE = 18;
public const T_CLOSE_CURLY_BRACE = 19;
// All tokens that are identifiers or keywords that could be considered as identifiers should be >= 100
/** @deprecated No Replacement planned. */
public const T_ALIASED_NAME = 100;
public const T_FULLY_QUALIFIED_NAME = 101;
public const T_IDENTIFIER = 102;
// All keyword tokens should be >= 200
public const T_ALL = 200;
public const T_AND = 201;
public const T_ANY = 202;
public const T_AS = 203;
public const T_ASC = 204;
public const T_AVG = 205;
public const T_BETWEEN = 206;
public const T_BOTH = 207;
public const T_BY = 208;
public const T_CASE = 209;
public const T_COALESCE = 210;
public const T_COUNT = 211;
public const T_DELETE = 212;
public const T_DESC = 213;
public const T_DISTINCT = 214;
public const T_ELSE = 215;
public const T_EMPTY = 216;
public const T_END = 217;
public const T_ESCAPE = 218;
public const T_EXISTS = 219;
public const T_FALSE = 220;
public const T_FROM = 221;
public const T_GROUP = 222;
public const T_HAVING = 223;
public const T_HIDDEN = 224;
public const T_IN = 225;
public const T_INDEX = 226;
public const T_INNER = 227;
public const T_INSTANCE = 228;
public const T_IS = 229;
public const T_JOIN = 230;
public const T_LEADING = 231;
public const T_LEFT = 232;
public const T_LIKE = 233;
public const T_MAX = 234;
public const T_MEMBER = 235;
public const T_MIN = 236;
public const T_NEW = 237;
public const T_NOT = 238;
public const T_NULL = 239;
public const T_NULLIF = 240;
public const T_OF = 241;
public const T_OR = 242;
public const T_ORDER = 243;
public const T_OUTER = 244;
public const T_PARTIAL = 245;
public const T_SELECT = 246;
public const T_SET = 247;
public const T_SOME = 248;
public const T_SUM = 249;
public const T_THEN = 250;
public const T_TRAILING = 251;
public const T_TRUE = 252;
public const T_UPDATE = 253;
public const T_WHEN = 254;
public const T_WHERE = 255;
public const T_WITH = 256;
/** @internal */
private function __construct()
{
}
}

View File

@@ -365,7 +365,7 @@ class SchemaTool
if (isset($class->table['uniqueConstraints'])) {
foreach ($class->table['uniqueConstraints'] as $indexName => $indexData) {
$uniqIndex = new Index($indexName, $this->getIndexColumns($class, $indexData), true, false, [], $indexData['options'] ?? []);
$uniqIndex = new Index('tmp__' . $indexName, $this->getIndexColumns($class, $indexData), true, false, [], $indexData['options'] ?? []);
foreach ($table->getIndexes() as $tableIndexName => $tableIndex) {
$method = method_exists($tableIndex, 'isFulfilledBy') ? 'isFulfilledBy' : 'isFullfilledBy';

View File

@@ -1781,18 +1781,15 @@ EXCEPTION
*/
final public static function getIdHashByIdentifier(array $identifier): string
{
foreach ($identifier as $k => $value) {
if ($value instanceof BackedEnum) {
$identifier[$k] = $value->value;
}
}
return implode(
' ',
array_map(
static function ($value) {
if ($value instanceof BackedEnum) {
return $value->value;
}
return $value;
},
$identifier
)
$identifier
);
}
@@ -3169,9 +3166,9 @@ EXCEPTION
if ($hints['fetchMode'][$class->name][$field] === ClassMetadata::FETCH_EAGER) {
$isIteration = isset($hints[Query::HINT_INTERNAL_ITERATION]) && $hints[Query::HINT_INTERNAL_ITERATION];
if (! $isIteration && $assoc['type'] === ClassMetadata::ONE_TO_MANY) {
if ($assoc['type'] === ClassMetadata::ONE_TO_MANY && ! $isIteration && ! $targetClass->isIdentifierComposite) {
$this->scheduleCollectionForBatchLoading($pColl, $class);
} elseif (($isIteration && $assoc['type'] === ClassMetadata::ONE_TO_MANY) || $assoc['type'] === ClassMetadata::MANY_TO_MANY) {
} else {
$this->loadCollection($pColl);
$pColl->takeSnapshot();
}
@@ -3252,7 +3249,19 @@ EXCEPTION
foreach ($found as $targetValue) {
$sourceEntity = $targetProperty->getValue($targetValue);
$id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity));
if ($sourceEntity === null && isset($targetClass->associationMappings[$mappedBy]['joinColumns'])) {
// case where the hydration $targetValue itself has not yet fully completed, for example
// in case a bi-directional association is being hydrated and deferring eager loading is
// not possible due to subclassing.
$data = $this->getOriginalEntityData($targetValue);
$id = [];
foreach ($targetClass->associationMappings[$mappedBy]['joinColumns'] as $joinColumn) {
$id[] = $data[$joinColumn['name']];
}
} else {
$id = $this->identifierFlattener->flattenIdentifier($class, $class->getIdentifierValues($sourceEntity));
}
$idHash = implode(' ', $id);
if (isset($mapping['indexBy'])) {

View File

@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\AbstractFetchEager;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
* @ORM\Table(name="abstract_fetch_eager_remote_control")
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({"mobile"="MobileRemoteControl"})
*/
abstract class AbstractRemoteControl
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*
* @var int
*/
public $id;
/**
* @ORM\Column(type="string")
*
* @var string
*/
public $name;
/**
* @ORM\OneToMany(targetEntity="User", mappedBy="remoteControl", fetch="EAGER")
*
* @var Collection<User>
*/
public $users;
public function __construct(string $name)
{
$this->name = $name;
$this->users = new ArrayCollection();
}
}

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\AbstractFetchEager;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
*/
class MobileRemoteControl extends AbstractRemoteControl
{
}

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\AbstractFetchEager;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
* @ORM\Table(name="abstract_fetch_eager_user")
*/
class User
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*
* @var int
*/
public $id;
/**
* @ORM\ManyToOne(targetEntity="AbstractRemoteControl", inversedBy="users")
* @ORM\JoinColumn(nullable=false)
*
* @var AbstractRemoteControl
*/
public $remoteControl;
public function __construct(AbstractRemoteControl $control)
{
$this->remoteControl = $control;
}
}

View File

@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\EagerFetchedCompositeOneToMany;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="eager_composite_join_root")
*/
class RootEntity
{
/**
* @ORM\Id
* @ORM\Column(type="integer", nullable=false)
*
* @var int|null
*/
private $id = null;
/**
* @ORM\Id
* @ORM\Column(type="string", nullable=false, name="other_key")
*
* @var string
*/
private $otherKey;
/**
* @ORM\OneToMany(mappedBy="root", targetEntity=SecondLevel::class, fetch="EAGER")
*
* @var Collection<int, SecondLevel>
*/
private $secondLevel;
public function __construct(int $id, string $other)
{
$this->otherKey = $other;
$this->secondLevel = new ArrayCollection();
$this->id = $id;
}
public function getId(): ?int
{
return $this->id;
}
public function getOtherKey(): string
{
return $this->otherKey;
}
}

View File

@@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\Models\EagerFetchedCompositeOneToMany;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="eager_composite_join_second_level", indexes={
* @ORM\Index(name="root_other_key_idx", columns={"root_other_key", "root_id"})
* })
*/
class SecondLevel
{
/**
* @ORM\Id
* @ORM\Column(type="integer", nullable=false)
*
* @var int|null
*/
private $upperId;
/**
* @ORM\Id
* @ORM\Column(type="string", nullable=false, name="other_key")
*
* @var string
*/
private $otherKey;
/**
* @ORM\ManyToOne(targetEntity=RootEntity::class, inversedBy="secondLevel")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="root_id", referencedColumnName="id"),
* @ORM\JoinColumn(name="root_other_key", referencedColumnName="other_key")
* })
*
* @var RootEntity
*/
private $root;
public function __construct(RootEntity $upper)
{
$this->upperId = $upper->getId();
$this->otherKey = $upper->getOtherKey();
$this->root = $upper;
}
public function getId(): ?int
{
return $this->id;
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\AbstractFetchEager\AbstractRemoteControl;
use Doctrine\Tests\Models\AbstractFetchEager\MobileRemoteControl;
use Doctrine\Tests\Models\AbstractFetchEager\User;
use Doctrine\Tests\OrmFunctionalTestCase;
final class AbstractFetchEagerTest extends OrmFunctionalTestCase
{
public function testWithAbstractFetchEager(): void
{
$this->createSchemaForModels(
AbstractRemoteControl::class,
User::class
);
$control = new MobileRemoteControl('smart');
$user = new User($control);
$entityManage = $this->getEntityManager();
$entityManage->persist($control);
$entityManage->persist($user);
$entityManage->flush();
$entityManage->clear();
$user = $entityManage->find(User::class, $user->id);
self::assertNotNull($user);
self::assertEquals('smart', $user->remoteControl->name);
self::assertTrue($user->remoteControl->users->contains($user));
}
}

View File

@@ -7,9 +7,9 @@ namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\PathExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\OrmFunctionalTestCase;
@@ -75,10 +75,10 @@ class NoOp extends FunctionNode
public function parse(Parser $parser): void
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->field = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
public function getSql(SqlWalker $sqlWalker): string

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Tests\Models\EagerFetchedCompositeOneToMany\RootEntity;
use Doctrine\Tests\Models\EagerFetchedCompositeOneToMany\SecondLevel;
use Doctrine\Tests\OrmFunctionalTestCase;
final class EagerFetchOneToManyWithCompositeKeyTest extends OrmFunctionalTestCase
{
/** @ticket 11154 */
public function testItDoesNotThrowAnExceptionWhenTriggeringALoad(): void
{
$this->setUpEntitySchema([RootEntity::class, SecondLevel::class]);
$a1 = new RootEntity(1, 'A');
$this->_em->persist($a1);
$this->_em->flush();
$this->_em->clear();
self::assertCount(1, $this->_em->getRepository(RootEntity::class)->findAll());
}
}

View File

@@ -10,9 +10,9 @@ use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\Node;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use Doctrine\Tests\OrmFunctionalTestCase;
final class GH7286Test extends OrmFunctionalTestCase
@@ -114,14 +114,14 @@ class GH7286CustomConcat extends FunctionNode
public function parse(Parser $parser): void
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->first = $parser->StringPrimary();
$parser->match(Lexer::T_COMMA);
$parser->match(TokenType::T_COMMA);
$this->second = $parser->StringPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
public function getSql(SqlWalker $walker): string

View File

@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM\Mapping;
use ArrayObject;
use Doctrine\DBAL\Types\Types;
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
use Doctrine\ORM\Events;
use Doctrine\ORM\Mapping\ChainTypedFieldMapper;
use Doctrine\ORM\Mapping\ClassMetadata;
@@ -54,6 +55,8 @@ require_once __DIR__ . '/../../Models/Global/GlobalNamespaceModel.php';
class ClassMetadataTest extends OrmTestCase
{
use VerifyDeprecations;
public function testClassMetadataInstanceSerialization(): void
{
$cm = new ClassMetadata(CMS\CmsUser::class);
@@ -1379,6 +1382,16 @@ class ClassMetadataTest extends OrmTestCase
'columnPrefix' => false,
]);
}
public function testInvalidCallToGetAssociationMappedByTargetFieldIsDeprecated(): void
{
$metadata = new ClassMetadata(self::class);
$metadata->mapOneToOne(['fieldName' => 'foo', 'targetEntity' => 'bar']);
$this->expectDeprecationWithIdentifier('https://github.com/doctrine/orm/pull/11309');
$metadata->getAssociationMappedByTargetField('foo');
}
}
/** @MappedSuperclass */

View File

@@ -6,6 +6,7 @@ namespace Doctrine\Tests\ORM\Query;
use Doctrine\Common\Lexer\Token;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\TokenType;
use Doctrine\Tests\OrmTestCase;
class LexerTest extends OrmTestCase
@@ -35,7 +36,7 @@ class LexerTest extends OrmTestCase
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_ALL, $token->type);
self::assertEquals(TokenType::T_ALL, $token->type);
}
public function testScannerRecognizesDecimalInteger(): void
@@ -43,7 +44,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer('1234');
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_INTEGER, $token->type);
self::assertEquals(TokenType::T_INTEGER, $token->type);
self::assertEquals(1234, $token->value);
}
@@ -52,7 +53,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer('1.234');
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_FLOAT, $token->type);
self::assertEquals(TokenType::T_FLOAT, $token->type);
self::assertEquals(1.234, $token->value);
}
@@ -61,7 +62,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer('1.2e3');
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_FLOAT, $token->type);
self::assertEquals(TokenType::T_FLOAT, $token->type);
self::assertEquals(1.2e3, $token->value);
}
@@ -70,7 +71,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer('0.2e3');
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_FLOAT, $token->type);
self::assertEquals(TokenType::T_FLOAT, $token->type);
self::assertEquals(.2e3, $token->value);
}
@@ -79,7 +80,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer('7E-10');
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_FLOAT, $token->type);
self::assertEquals(TokenType::T_FLOAT, $token->type);
self::assertEquals(7E-10, $token->value);
}
@@ -88,7 +89,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer('123456789.01');
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_FLOAT, $token->type);
self::assertEquals(TokenType::T_FLOAT, $token->type);
self::assertEquals(1.2345678901e8, $token->value);
}
@@ -97,12 +98,12 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer('- 1.234e2');
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_MINUS, $token->type);
self::assertEquals(TokenType::T_MINUS, $token->type);
self::assertEquals('-', $token->value);
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_FLOAT, $token->type);
self::assertEquals(TokenType::T_FLOAT, $token->type);
self::assertNotEquals(-1.234e2, $token->value);
self::assertEquals(1.234e2, $token->value);
}
@@ -112,7 +113,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer("'This is a string.'");
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_STRING, $token->type);
self::assertEquals(TokenType::T_STRING, $token->type);
self::assertEquals('This is a string.', $token->value);
}
@@ -121,7 +122,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer("'abc''defg'''");
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_STRING, $token->type);
self::assertEquals(TokenType::T_STRING, $token->type);
self::assertEquals("abc'defg'", $token->value);
}
@@ -130,7 +131,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer('?1');
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_INPUT_PARAMETER, $token->type);
self::assertEquals(TokenType::T_INPUT_PARAMETER, $token->type);
self::assertEquals('?1', $token->value);
}
@@ -139,7 +140,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer(':name');
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_INPUT_PARAMETER, $token->type);
self::assertEquals(TokenType::T_INPUT_PARAMETER, $token->type);
self::assertEquals(':name', $token->value);
}
@@ -148,7 +149,7 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer(':_name');
$lexer->moveNext();
$token = $lexer->lookahead;
self::assertEquals(Lexer::T_INPUT_PARAMETER, $token->type);
self::assertEquals(TokenType::T_INPUT_PARAMETER, $token->type);
self::assertEquals(':_name', $token->value);
}
@@ -158,17 +159,17 @@ class LexerTest extends OrmTestCase
$lexer = new Lexer($dql);
$tokens = [
new Token('SELECT', Lexer::T_SELECT, 0),
new Token('u', Lexer::T_IDENTIFIER, 7),
new Token('FROM', Lexer::T_FROM, 9),
new Token('My\Namespace\User', Lexer::T_FULLY_QUALIFIED_NAME, 14),
new Token('u', Lexer::T_IDENTIFIER, 32),
new Token('WHERE', Lexer::T_WHERE, 34),
new Token('u', Lexer::T_IDENTIFIER, 40),
new Token('.', Lexer::T_DOT, 41),
new Token('name', Lexer::T_IDENTIFIER, 42),
new Token('=', Lexer::T_EQUALS, 47),
new Token("Jack O'Neil", Lexer::T_STRING, 49),
new Token('SELECT', TokenType::T_SELECT, 0),
new Token('u', TokenType::T_IDENTIFIER, 7),
new Token('FROM', TokenType::T_FROM, 9),
new Token('My\Namespace\User', TokenType::T_FULLY_QUALIFIED_NAME, 14),
new Token('u', TokenType::T_IDENTIFIER, 32),
new Token('WHERE', TokenType::T_WHERE, 34),
new Token('u', TokenType::T_IDENTIFIER, 40),
new Token('.', TokenType::T_DOT, 41),
new Token('name', TokenType::T_IDENTIFIER, 42),
new Token('=', TokenType::T_EQUALS, 47),
new Token("Jack O'Neil", TokenType::T_STRING, 49),
];
foreach ($tokens as $expected) {
@@ -186,15 +187,15 @@ class LexerTest extends OrmTestCase
public static function provideTokens(): array
{
return [
[Lexer::T_IDENTIFIER, 'u'], // one char
[Lexer::T_IDENTIFIER, 'someIdentifier'],
[Lexer::T_IDENTIFIER, 's0m31d3nt1f13r'], // including digits
[Lexer::T_IDENTIFIER, 'some_identifier'], // including underscore
[Lexer::T_IDENTIFIER, '_some_identifier'], // starts with underscore
[Lexer::T_IDENTIFIER, 'comma'], // name of a token class with value < 100 (whitebox test)
[Lexer::T_FULLY_QUALIFIED_NAME, 'Some\Class'], // DQL class reference
[Lexer::T_ALIASED_NAME, 'Some:Name'],
[Lexer::T_ALIASED_NAME, 'Some:Subclassed\Name'],
[TokenType::T_IDENTIFIER, 'u'], // one char
[TokenType::T_IDENTIFIER, 'someIdentifier'],
[TokenType::T_IDENTIFIER, 's0m31d3nt1f13r'], // including digits
[TokenType::T_IDENTIFIER, 'some_identifier'], // including underscore
[TokenType::T_IDENTIFIER, '_some_identifier'], // starts with underscore
[TokenType::T_IDENTIFIER, 'comma'], // name of a token class with value < 100 (whitebox test)
[TokenType::T_FULLY_QUALIFIED_NAME, 'Some\Class'], // DQL class reference
[TokenType::T_ALIASED_NAME, 'Some:Name'],
[TokenType::T_ALIASED_NAME, 'Some:Subclassed\Name'],
];
}
}

View File

@@ -6,9 +6,9 @@ namespace Doctrine\Tests\ORM\Query;
use Doctrine\Common\Persistence\PersistentObject;
use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\TokenType;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\OrmTestCase;
use stdClass;
@@ -124,11 +124,11 @@ class ParserTest extends OrmTestCase
* but in LexerTest.
*/
return [
[Lexer::T_WHERE, 'where'], // keyword
[Lexer::T_DOT, '.'], // token that cannot be an identifier
[Lexer::T_IDENTIFIER, 'someIdentifier'],
[Lexer::T_IDENTIFIER, 'from'], // also a terminal string (the "FROM" keyword) as in DDC-505
[Lexer::T_IDENTIFIER, 'comma'],
[TokenType::T_WHERE, 'where'], // keyword
[TokenType::T_DOT, '.'], // token that cannot be an identifier
[TokenType::T_IDENTIFIER, 'someIdentifier'],
[TokenType::T_IDENTIFIER, 'from'], // also a terminal string (the "FROM" keyword) as in DDC-505
[TokenType::T_IDENTIFIER, 'comma'],
// not even a terminal string, but the name of a constant in the Lexer (whitebox test)
];
}
@@ -137,15 +137,15 @@ class ParserTest extends OrmTestCase
public static function invalidMatches(): array
{
return [
[Lexer::T_DOT, 'ALL'], // ALL is a terminal string (reserved keyword) and also possibly an identifier
[Lexer::T_DOT, ','], // "," is a token on its own, but cannot be used as identifier
[Lexer::T_WHERE, 'WITH'], // as in DDC-3697
[Lexer::T_WHERE, '.'],
[TokenType::T_DOT, 'ALL'], // ALL is a terminal string (reserved keyword) and also possibly an identifier
[TokenType::T_DOT, ','], // "," is a token on its own, but cannot be used as identifier
[TokenType::T_WHERE, 'WITH'], // as in DDC-3697
[TokenType::T_WHERE, '.'],
// The following are qualified or aliased names and must not be accepted where only an Identifier is expected
[Lexer::T_IDENTIFIER, '\\Some\\Class'],
[Lexer::T_IDENTIFIER, 'Some\\Class'],
[Lexer::T_IDENTIFIER, 'Some:Name'],
[TokenType::T_IDENTIFIER, '\\Some\\Class'],
[TokenType::T_IDENTIFIER, 'Some\\Class'],
[TokenType::T_IDENTIFIER, 'Some:Name'],
];
}
@@ -164,7 +164,7 @@ class ParserTest extends OrmTestCase
$parser = new Parser($query);
$this->expectException(QueryException::class);
$parser->match(Lexer::T_SELECT);
$parser->match(TokenType::T_SELECT);
}
private function createParser(string $dql): Parser

View File

@@ -18,10 +18,10 @@ use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Query as ORMQuery;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\SimpleArithmeticExpression;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\SqlWalker;
use Doctrine\ORM\Query\TokenType;
use Doctrine\Tests\DbalTypes\NegativeToPositiveType;
use Doctrine\Tests\Models\CMS\CmsGroup;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
@@ -2186,12 +2186,12 @@ class MyAbsFunction extends FunctionNode
{
$lexer = $parser->getLexer();
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$parser->match(TokenType::T_IDENTIFIER);
$parser->match(TokenType::T_OPEN_PARENTHESIS);
$this->simpleArithmeticExpression = $parser->SimpleArithmeticExpression();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
$parser->match(TokenType::T_CLOSE_PARENTHESIS);
}
}
/** @Entity */

View File

@@ -7,8 +7,8 @@ namespace Doctrine\Tests\ORM\Query;
use Doctrine\Deprecations\PHPUnit\VerifyDeprecations;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\ParserResult;
use Doctrine\ORM\Query\TokenType;
use Doctrine\ORM\Query\TreeWalkerAdapter;
use PHPUnit\Framework\TestCase;
use stdClass;
@@ -33,7 +33,7 @@ class TreeWalkerAdapterTest extends TestCase
'relation' => null,
'map' => null,
'nestingLevel' => 0,
'token' => ['value' => '', 'type' => Lexer::T_NONE, 'position' => 0],
'token' => ['value' => '', 'type' => TokenType::T_NONE, 'position' => 0],
]);
}
@@ -52,7 +52,7 @@ class TreeWalkerAdapterTest extends TestCase
'relation' => null,
'map' => null,
'nestingLevel' => 0,
'token' => ['value' => '', 'type' => Lexer::T_NONE, 'position' => 0],
'token' => ['value' => '', 'type' => TokenType::T_NONE, 'position' => 0],
]);
}
};

View File

@@ -374,6 +374,27 @@ class SchemaToolTest extends OrmTestCase
self::assertTrue($schema->hasTable('first_entity'), 'Table first_entity should exist.');
self::assertFalse($schema->hasTable('second_entity'), 'Table second_entity should not exist.');
}
/** @group GH-11314 */
public function testLoadUniqueConstraintWithoutName(): void
{
$em = $this->getTestEntityManager();
$entity = $em->getClassMetadata(GH11314Entity::class);
$schemaTool = new SchemaTool($em);
$schema = $schemaTool->getSchemaFromMetadata([$entity]);
self::assertTrue($schema->hasTable('GH11314Entity'));
$tableEntity = $schema->getTable('GH11314Entity');
self::assertTrue($tableEntity->hasIndex('uniq_2d81a3ed5bf54558875f7fd5'));
$tableIndex = $tableEntity->getIndex('uniq_2d81a3ed5bf54558875f7fd5');
self::assertTrue($tableIndex->isUnique());
self::assertSame(['field', 'anotherField'], $tableIndex->getColumns());
}
}
/**
@@ -559,6 +580,32 @@ class IndexByFieldEntity
public $fieldName;
}
/**
* @Entity
* @Table(uniqueConstraints={@UniqueConstraint(columns={"field", "anotherField"})})
*/
class GH11314Entity
{
/**
* @Column(type="integer")
* @Id
* @var int
*/
private $id;
/**
* @Column(name="field", type="string")
* @var string
*/
private $field;
/**
* @Column(name="anotherField", type="string")
* @var string
*/
private $anotherField;
}
class IncorrectIndexByFieldEntity
{
/** @var int */