1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Query;
use Doctrine\ORM\Query;
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 PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\DoesNotPerformAssertions;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\RequiresPhpunit;
use stdClass;
#[RequiresPhpunit('< 12')]
class ParserTest extends OrmTestCase
{
#[Group('DDC-3715')]
public function testAbstractSchemaNameSupportsFQCN(): void
{
$parser = $this->createParser(CmsUser::class);
self::assertEquals(CmsUser::class, $parser->AbstractSchemaName());
}
#[Group('DDC-3715')]
public function testAbstractSchemaNameSupportsClassnamesWithLeadingBackslash(): void
{
$parser = $this->createParser('\\' . CmsUser::class);
self::assertEquals('\\' . CmsUser::class, $parser->AbstractSchemaName());
}
#[Group('DDC-3715')]
public function testAbstractSchemaNameSupportsIdentifier(): void
{
$parser = $this->createParser(stdClass::class);
self::assertEquals(stdClass::class, $parser->AbstractSchemaName());
}
#[DataProvider('validMatches')]
#[Group('DDC-3701')]
#[DoesNotPerformAssertions]
public function testMatch(TokenType $expectedToken, string $inputString): void
{
$parser = $this->createParser($inputString);
$parser->match($expectedToken); // throws exception if not matched
}
#[DataProvider('invalidMatches')]
#[Group('DDC-3701')]
public function testMatchFailure(TokenType $expectedToken, string $inputString): void
{
$this->expectException(QueryException::class);
$parser = $this->createParser($inputString);
$parser->match($expectedToken);
}
/** @phpstan-return list<array{int, string}> */
public static function validMatches(): array
{
/*
* This only covers the special case handling in the Parser that some
* tokens that are *not* T_IDENTIFIER are accepted as well when matching
* identifiers.
*
* The basic checks that tokens are classified correctly do not belong here
* but in LexerTest.
*/
return [
[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)
];
}
/** @phpstan-return list<array{int, string}> */
public static function invalidMatches(): array
{
return [
[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
[TokenType::T_IDENTIFIER, '\\Some\\Class'],
[TokenType::T_IDENTIFIER, 'Some\\Class'],
];
}
/**
* PHP 7.4 would fail with Notice: Trying to access array offset on value of type null.
*
* @see https://github.com/doctrine/orm/pull/7934
*/
#[Group('GH7934')]
public function testNullLookahead(): void
{
$query = new Query($this->getTestEntityManager());
$query->setDQL('SELECT CURRENT_TIMESTAMP()');
$parser = new Parser($query);
$this->expectException(QueryException::class);
$parser->match(TokenType::T_SELECT);
}
private function createParser(string $dql): Parser
{
$query = new Query($this->getTestEntityManager());
$query->setDQL($dql);
$parser = new Parser($query);
$parser->getLexer()->moveNext();
return $parser;
}
}
|