Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
doctrine-dbal
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Tomáš Trávníček
doctrine-dbal
Commits
b3d110ba
Commit
b3d110ba
authored
Jul 21, 2009
by
guilhermeblanco
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[2.0] Started massive reorganization of grammar rules in DQL parser.
parent
a3018340
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
440 additions
and
435 deletions
+440
-435
Parser.php
lib/Doctrine/ORM/Query/Parser.php
+434
-423
LanguageRecognitionTest.php
tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php
+6
-12
No files found.
lib/Doctrine/ORM/Query/Parser.php
View file @
b3d110ba
...
...
@@ -66,14 +66,6 @@ class Parser
'current_timestamp'
=>
'Doctrine\ORM\Query\AST\Functions\CurrentTimestampFunction'
);
/**
* The minimum number of tokens read after last detected error before
* another error can be reported.
*
* @var int
*/
//const MIN_ERROR_DISTANCE = 2;
/**
* Path expressions that were encountered during parsing of SelectExpressions
* and still need to be validated.
...
...
@@ -134,7 +126,7 @@ class Parser
$this
->
_query
=
$query
;
$this
->
_em
=
$query
->
getEntityManager
();
$this
->
_lexer
=
new
Lexer
(
$query
->
getDql
());
$this
->
_parserResult
=
new
ParserResult
;
$this
->
_parserResult
=
new
ParserResult
()
;
}
/**
...
...
@@ -150,9 +142,8 @@ class Parser
{
$key
=
(
is_string
(
$token
))
?
'value'
:
'type'
;
if
(
!
(
$this
->
_lexer
->
lookahead
[
$key
]
===
$token
))
{
if
(
!
(
$this
->
_lexer
->
lookahead
[
$key
]
===
$token
))
$this
->
syntaxError
(
$this
->
_lexer
->
getLiteral
(
$token
));
}
$this
->
_lexer
->
moveNext
();
}
...
...
@@ -171,12 +162,10 @@ class Parser
// Deep = true cleans peek and also any previously defined errors
if
(
$deep
)
{
$this
->
_lexer
->
resetPeek
();
//$this->_errors = array();
}
$this
->
_lexer
->
token
=
null
;
$this
->
_lexer
->
lookahead
=
null
;
//$this->_errorDistance = self::MIN_ERROR_DISTANCE;
}
/**
...
...
@@ -191,7 +180,6 @@ class Parser
// Check for end of string
if
(
$this
->
_lexer
->
lookahead
!==
null
)
{
//var_dump($this->_lexer->lookahead);
$this
->
syntaxError
(
'end of string'
);
}
...
...
@@ -205,6 +193,16 @@ class Parser
return
$this
->
_parserResult
;
}
/**
* Sets the custom tree walker.
*
* @param SqlTreeWalker $sqlTreeWalker
*/
public
function
setSqlTreeWalker
(
$sqlTreeWalker
)
{
$this
->
_sqlTreeWalker
=
$sqlTreeWalker
;
}
/**
* Gets the lexer used by the parser.
...
...
@@ -225,6 +223,16 @@ class Parser
{
return
$this
->
_parserResult
;
}
/**
* Gets the EntityManager used by the parser.
*
* @return EntityManager
*/
public
function
getEntityManager
()
{
return
$this
->
_em
;
}
/**
* Generates a new syntax error.
...
...
@@ -238,10 +246,11 @@ class Parser
$token
=
$this
->
_lexer
->
lookahead
;
}
$message
=
'line 0, col '
.
(
isset
(
$token
[
'position'
])
?
$token
[
'position'
]
:
'-1'
)
.
': Error: '
;
$tokenPos
=
(
isset
(
$token
[
'position'
]))
?
$token
[
'position'
]
:
'-1'
;
$message
=
"line 0, col
{
$tokenPos
}
: Error: "
;
if
(
$expected
!==
''
)
{
$message
.=
"Expected '
$expected
', got "
;
$message
.=
"Expected '
{
$expected
}
', got "
;
}
else
{
$message
.=
'Unexpected '
;
}
...
...
@@ -252,7 +261,7 @@ class Parser
$message
.=
"'
{
$this
->
_lexer
->
lookahead
[
'value'
]
}
'"
;
}
throw
QueryException
::
syntaxError
(
$message
);
throw
\Doctrine\ORM\Query\
QueryException
::
syntaxError
(
$message
);
}
/**
...
...
@@ -276,44 +285,51 @@ class Parser
$message
=
'line 0, col '
.
(
isset
(
$token
[
'position'
])
?
$token
[
'position'
]
:
'-1'
)
.
" near '"
.
substr
(
$dql
,
$token
[
'position'
],
$length
)
.
"'): Error: "
.
$message
;
throw
QueryException
::
semanticalError
(
$message
);
throw
\Doctrine\ORM\Query\
QueryException
::
semanticalError
(
$message
);
}
/**
* Logs new error entry.
*
* @param string $message Message to log.
* @param array $token Token that it was processing.
* Peeks beyond the specified token and returns the first token after that one.
*/
/*protected function _logError($message = '', $token)
{
if ($this->_errorDistance >= self::MIN_ERROR_DISTANCE) {
$message = 'line 0, col ' . $token['position'] . ': ' . $message;
$this->_errors[] = $message;
}
private
function
_peekBeyond
(
$token
)
{
$peek
=
$this
->
_lexer
->
peek
();
while
(
$peek
[
'value'
]
!=
$token
)
{
$peek
=
$this
->
_lexer
->
peek
();
}
$peek
=
$this
->
_lexer
->
peek
();
$this
->
_lexer
->
resetPeek
();
$this->_errorDistance = 0
;
}*/
return
$peek
;
}
/**
*
Gets the EntityManager used by the parser
.
*
Checks if the next-next (after lookahead) token starts a function
.
*
* @return
EntityManager
* @return
boolean TRUE if the next-next tokens start a function, FALSE otherwise.
*/
p
ublic
function
getEntityManager
()
p
rivate
function
_isFunction
()
{
return
$this
->
_em
;
$peek
=
$this
->
_lexer
->
peek
();
$nextpeek
=
$this
->
_lexer
->
peek
();
$this
->
_lexer
->
resetPeek
();
// We deny the COUNT(SELECT * FROM User u) here. COUNT won't be considered a function
return
(
$peek
[
'value'
]
===
'('
&&
$nextpeek
[
'type'
]
!==
Lexer
::
T_SELECT
);
}
/**
* Checks
if the next-next (after lookahead) token starts a
function.
* Checks
whether the given token type indicates an aggregate
function.
*
* @return boolean
* @return boolean
TRUE if the token type is an aggregate function, FALSE otherwise.
*/
private
function
_is
Function
(
)
private
function
_is
AggregateFunction
(
$tokenType
)
{
$next
=
$this
->
_lexer
->
glimpse
();
return
$next
[
'value'
]
===
'('
;
return
$tokenType
==
Lexer
::
T_AVG
||
$tokenType
==
Lexer
::
T_MIN
||
$tokenType
==
Lexer
::
T_MAX
||
$tokenType
==
Lexer
::
T_SUM
||
$tokenType
==
Lexer
::
T_COUNT
;
}
/**
...
...
@@ -328,6 +344,168 @@ class Parser
return
(
$la
[
'value'
]
===
'('
&&
$next
[
'type'
]
===
Lexer
::
T_SELECT
);
}
/**
* Begins a new stack of deferred path expressions.
*/
private
function
_beginDeferredPathExpressionStack
()
{
$this
->
_deferredPathExpressionStacks
[]
=
array
();
}
/**
* Processes the topmost stack of deferred path expressions.
*/
private
function
_processDeferredPathExpressionStack
()
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
foreach
(
$exprStack
as
$expr
)
{
switch
(
$expr
->
getType
())
{
case
AST\PathExpression
::
TYPE_SINGLE_VALUED_PATH_EXPRESSION
:
$this
->
_validateSingleValuedPathExpression
(
$expr
);
break
;
case
AST\PathExpression
::
TYPE_STATE_FIELD
:
$this
->
_validateStateFieldPathExpression
(
$expr
);
break
;
case
AST\PathExpression
::
TYPE_SINGLE_VALUED_ASSOCIATION
:
$this
->
_validateSingleValuedAssociationPathExpression
(
$expr
);
break
;
case
AST\PathExpression
::
TYPE_COLLECTION_VALUED_ASSOCIATION
:
$this
->
_validateCollectionValuedAssociationPathExpression
(
$expr
);
break
;
default
:
$this
->
semanticalError
(
'Encountered invalid PathExpression.'
);
break
;
}
}
}
/**
* Validates that the given <tt>PathExpression</tt> is a semantically correct for grammar rules:
*
* AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
* SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
* StateFieldPathExpression ::= IdentificationVariable "." StateField | SingleValuedAssociationPathExpression "." StateField
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* SingleValuedAssociationField
* CollectionValuedPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* CollectionValuedAssociationField
*
* @param PathExpression $pathExpression
* @return boolean
*/
private
function
_validatePathExpression
(
AST\PathExpression
$pathExpression
)
{
$class
=
$this
->
_queryComponents
[
$pathExpression
->
getIdentificationVariable
()][
'metadata'
];
$stateField
=
$collectionField
=
null
;
foreach
(
$pathExpression
->
getParts
()
as
$field
)
{
// Check if it is not in a state field
if
(
$stateField
!==
null
)
{
$this
->
semanticalError
(
'Cannot navigate through state field named '
.
$stateField
);
}
// Check if it is not a collection field
if
(
$collectionField
!==
null
)
{
$this
->
semanticalError
(
'Can not navigate through collection-valued field named '
.
$collectionField
);
}
// Check if field exists
if
(
!
isset
(
$class
->
associationMappings
[
$field
])
&&
!
isset
(
$class
->
fieldMappings
[
$field
]))
{
$this
->
semanticalError
(
'Class '
.
$class
->
name
.
' has no field named '
.
$field
);
}
if
(
isset
(
$class
->
fieldMappings
[
$field
]))
{
$stateField
=
$field
;
}
else
if
(
$class
->
associationMappings
[
$field
]
->
isOneToOne
())
{
$class
=
$this
->
_em
->
getClassMetadata
(
$class
->
associationMappings
[
$field
]
->
targetEntityName
);
}
else
{
$collectionField
=
$field
;
}
}
$expressionType
=
null
;
if
(
$stateField
!==
null
)
{
$expressionType
=
AST\PathExpression
::
TYPE_STATE_FIELD
;
}
else
if
(
$collectionField
!==
null
)
{
$expressionType
=
AST\PathExpression
::
TYPE_COLLECTION_VALUED_ASSOCIATION
;
}
else
{
$expressionType
=
AST\PathExpression
::
TYPE_SINGLE_VALUED_ASSOCIATION
;
}
// We need to force the type in PathExpression
$pathExpression
->
setType
(
$expressionType
);
return
$expressionType
;
}
/**
* Validates that the given <tt>PathExpression</tt> is a semantically correct
* SingleValuedPathExpression as specified in the grammar.
*
* @param PathExpression $pathExpression
*/
private
function
_validateSingleValuedPathExpression
(
AST\PathExpression
$pathExpression
)
{
$pathExprType
=
$this
->
_validatePathExpression
(
$pathExpression
);
if
(
$pathExprType
!==
AST\PathExpression
::
TYPE_STATE_FIELD
&&
$pathExprType
!==
AST\PathExpression
::
TYPE_SINGLE_VALUED_ASSOCIATION
)
{
$this
->
semanticalError
(
'SingleValuedAssociationField or StateField expected.'
);
}
}
/**
* Validates that the given <tt>PathExpression</tt> is a semantically correct
* StateFieldPathExpression as specified in the grammar.
*
* @param PathExpression $pathExpression
*/
private
function
_validateStateFieldPathExpression
(
AST\PathExpression
$pathExpression
)
{
$pathExprType
=
$this
->
_validatePathExpression
(
$pathExpression
);
if
(
$pathExprType
!==
AST\PathExpression
::
TYPE_STATE_FIELD
)
{
$this
->
semanticalError
(
'Invalid StateFieldPathExpression. Must end in a state field.'
);
}
}
/**
* Validates that the given <tt>PathExpression</tt> is a semantically correct
* CollectionValuedPathExpression as specified in the grammar.
*
* @param PathExpression $pathExpression
*/
private
function
_validateCollectionValuedPathExpression
(
AST\PathExpression
$pathExpression
)
{
$pathExprType
=
$this
->
_validatePathExpression
(
$pathExpression
);
if
(
$pathExprType
!==
AST\PathExpression
::
TYPE_COLLECTION_VALUED_ASSOCIATION
)
{
$this
->
semanticalError
(
'Invalid CollectionValuedAssociationField. Must end in a collection-valued field.'
);
}
}
/**
* Validates that the given <tt>PathExpression</tt> is a semantically correct
* SingleValuedAssociationPathExpression as specified in the grammar.
*
* @param PathExpression $pathExpression
*/
private
function
_validateSingleValuedAssociationPathExpression
(
AST\PathExpression
$pathExpression
)
{
$pathExprType
=
$this
->
_validatePathExpression
(
$pathExpression
);
if
(
$pathExprType
!==
AST\PathExpression
::
TYPE_SINGLE_VALUED_ASSOCIATION
)
{
$this
->
semanticalError
(
'Invalid SingleValuedAssociationField. Must end in a single valued association field.'
);
}
}
/**
* QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement
...
...
@@ -351,15 +529,21 @@ class Parser
break
;
}
}
/**
* SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
*/
public
function
SelectStatement
()
{
// We need to prevent semantical checks on SelectClause,
// since we do not have any IdentificationVariable yet
$this
->
_beginDeferredPathExpressionStack
();
$selectClause
=
$this
->
SelectClause
();
$fromClause
=
$this
->
FromClause
();
// Activate semantical checks after this point. Process all deferred checks in pipeline
$this
->
_processDeferredPathExpressionStack
();
$whereClause
=
$this
->
_lexer
->
isNextToken
(
Lexer
::
T_WHERE
)
...
...
@@ -380,62 +564,221 @@ class Parser
}
/**
*
Begins a new stack of deferred path expressions.
*
UpdateStatement ::= UpdateClause [WhereClause]
*/
p
rivate
function
_beginDeferredPathExpressionStack
()
p
ublic
function
UpdateStatement
()
{
$this
->
_deferredPathExpressionStacks
[]
=
array
();
$updateStatement
=
new
AST\UpdateStatement
(
$this
->
UpdateClause
());
$updateStatement
->
setWhereClause
(
$this
->
_lexer
->
isNextToken
(
Lexer
::
T_WHERE
)
?
$this
->
WhereClause
()
:
null
);
return
$updateStatement
;
}
/**
* DeleteStatement ::= DeleteClause [WhereClause]
*/
public
function
DeleteStatement
()
{
$deleteStatement
=
new
AST\DeleteStatement
(
$this
->
DeleteClause
());
$deleteStatement
->
setWhereClause
(
$this
->
_lexer
->
isNextToken
(
Lexer
::
T_WHERE
)
?
$this
->
WhereClause
()
:
null
);
return
$deleteStatement
;
}
/**
* Processes the topmost stack of deferred path expressions.
* These will be validated to make sure they are indeed
* valid <tt>StateFieldPathExpression</tt>s and additional information
* is attached to their AST nodes.
* IdentificationVariable ::= identifier
*/
p
rivate
function
_processDeferredPathExpressionStack
()
p
ublic
function
IdentificationVariable
()
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$qComps
=
$this
->
_queryComponents
;
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
foreach
(
$exprStack
as
$expr
)
{
switch
(
$expr
->
getType
())
{
case
AST\PathExpression
::
TYPE_STATE_FIELD
:
$this
->
_validateStateFieldPathExpression
(
$expr
);
break
;
return
$this
->
_lexer
->
token
[
'value'
];
}
/**
* AliasIdentificationVariable = identifier
*/
public
function
AliasIdentificationVariable
()
{
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
case
AST\PathExpression
::
TYPE_SINGLE_VALUED_ASSOCIATION
:
$this
->
_validateSingleValuedAssociationPathExpression
(
$expr
);
break
;
return
$this
->
_lexer
->
token
[
'value'
];
}
/**
* AbstractSchemaName ::= identifier
*/
public
function
AbstractSchemaName
()
{
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
case
AST\PathExpression
::
TYPE_COLLECTION_VALUED_ASSOCIATION
:
$this
->
_validateCollectionValuedAssociationPathExpression
(
$expr
);
break
;
case
AST\PathExpression
::
TYPE_SINGLE_VALUED_PATH_EXPRESSION
:
$this
->
_validateSingleValuedPathExpression
(
$expr
);
break
;
return
$this
->
_lexer
->
token
[
'value'
];
}
/**
* ResultVariable ::= identifier
*/
public
function
ResultVariable
()
{
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
return
$this
->
_lexer
->
token
[
'value'
];
}
default
:
$this
->
semanticalError
(
'Encountered invalid PathExpression.'
);
break
;
}
/**
* JoinPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField)
*/
public
function
JoinPathExpression
()
{
$identificationVariable
=
$this
->
IdentificationVariable
();
$this
->
match
(
'.'
);
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
return
new
AST\JoinPathExpression
(
$identificationVariable
,
$this
->
_lexer
->
token
[
'value'
]
);
}
/**
* Parses an arbitrary path expression without any semantic validation.
*
* PathExpression ::= IdentificationVariable "." {identifier "."}* identifier
*
* @return PathExpression
*/
public
function
PathExpression
(
$type
)
{
$identificationVariable
=
$this
->
IdentificationVariable
();
$parts
=
array
();
do
{
$this
->
match
(
'.'
);
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
$parts
[]
=
$this
->
_lexer
->
token
[
'value'
];
}
while
(
$this
->
_lexer
->
isNextToken
(
'.'
));
return
new
AST\PathExpression
(
$type
,
$identificationVariable
,
$parts
);
}
/**
* AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression
*
* @todo Implement this grammar rule which is used in SubselectFromClause
*/
public
function
AssociationPathExpression
()
{
}
/**
* SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
*/
public
function
SingleValuedPathExpression
()
{
$pathExpr
=
$this
->
PathExpression
(
AST\PathExpression
::
TYPE_SINGLE_VALUED_PATH_EXPRESSION
);
if
(
!
empty
(
$this
->
_deferredPathExpressionStacks
))
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$exprStack
[]
=
$pathExpr
;
array_push
(
$this
->
_deferredPathExpressionStacks
,
$exprStack
);
return
$pathExpr
;
}
$this
->
_validateSingleValuedPathExpression
(
$pathExpr
);
return
$pathExpr
;
}
/**
* StateFieldPathExpression ::= SimpleStateFieldPathExpression | SimpleStateFieldAssociationPathExpression
*/
public
function
StateFieldPathExpression
()
{
$pathExpr
=
$this
->
PathExpression
(
AST\PathExpression
::
TYPE_STATE_FIELD
);
if
(
!
empty
(
$this
->
_deferredPathExpressionStacks
))
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$exprStack
[]
=
$pathExpr
;
array_push
(
$this
->
_deferredPathExpressionStacks
,
$exprStack
);
return
$pathExpr
;
}
$this
->
_validateStateFieldPathExpression
(
$pathExpr
);
return
$pathExpr
;
}
/**
*
UpdateStatement ::= UpdateClause [WhereClause]
*
SingleValuedAssociationPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* SingleValuedAssociationField
*/
public
function
UpdateStatement
()
public
function
SingleValuedAssociationPathExpression
()
{
$updateStatement
=
new
AST\UpdateStatement
(
$this
->
UpdateClause
());
$updateStatement
->
setWhereClause
(
$this
->
_lexer
->
isNextToken
(
Lexer
::
T_WHERE
)
?
$this
->
WhereClause
()
:
null
);
$pathExpr
=
$this
->
PathExpression
(
AST\PathExpression
::
TYPE_SINGLE_VALUED_ASSOCIATION
);
if
(
!
empty
(
$this
->
_deferredPathExpressionStacks
))
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$exprStack
[]
=
$pathExpr
;
array_push
(
$this
->
_deferredPathExpressionStacks
,
$exprStack
);
return
$updateStatement
;
return
$pathExpr
;
}
$this
->
_validateSingleValuedAssociationPathExpression
(
$pathExpr
);
return
$pathExpr
;
}
/**
* CollectionValuedPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* CollectionValuedAssociationField
*/
public
function
CollectionValuedPathExpression
()
{
$pathExpr
=
$this
->
PathExpression
(
AST\PathExpression
::
TYPE_COLLECTION_VALUED_ASSOCIATION
);
if
(
!
empty
(
$this
->
_deferredPathExpressionStacks
))
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$exprStack
[]
=
$pathExpr
;
array_push
(
$this
->
_deferredPathExpressionStacks
,
$exprStack
);
return
$pathExpr
;
}
$this
->
_validateCollectionValuedPathExpression
(
$pathExpr
);
return
$pathExpr
;
}
/**
* SimpleStateFieldPathExpression ::= IdentificationVariable "." StateField
*/
public
function
SimpleStateFieldPathExpression
()
{
$pathExpr
=
$this
->
PathExpression
(
AST\PathExpression
::
TYPE_STATE_FIELD
);
if
(
!
empty
(
$this
->
_deferredPathExpressionStacks
))
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$exprStack
[]
=
$pathExpr
;
array_push
(
$this
->
_deferredPathExpressionStacks
,
$exprStack
);
return
$pathExpr
;
}
$this
->
_validateStateFieldPathExpression
(
$pathExpr
);
return
$pathExpr
;
}
/**
* UpdateClause ::= "UPDATE" AbstractSchemaName [["AS"] AliasIdentificationVariable] "SET" UpdateItem {"," UpdateItem}*
*/
...
...
@@ -500,7 +843,9 @@ class Parser
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
$field
=
$this
->
_lexer
->
token
[
'value'
];
$this
->
match
(
'='
);
$newValue
=
$this
->
NewValue
();
$updateItem
=
new
AST\UpdateItem
(
$field
,
$newValue
);
...
...
@@ -536,19 +881,6 @@ class Parser
return
$this
->
SimpleArithmeticExpression
();
}
/**
* DeleteStatement ::= DeleteClause [WhereClause]
*/
public
function
DeleteStatement
()
{
$deleteStatement
=
new
AST\DeleteStatement
(
$this
->
DeleteClause
());
$deleteStatement
->
setWhereClause
(
$this
->
_lexer
->
isNextToken
(
Lexer
::
T_WHERE
)
?
$this
->
WhereClause
()
:
null
);
return
$deleteStatement
;
}
/**
* DeleteClause ::= "DELETE" ["FROM"] AbstractSchemaName [["AS"] AliasIdentificationVariable]
*/
...
...
@@ -641,7 +973,7 @@ class Parser
$expression
=
$this
->
IdentificationVariable
();
}
else
if
((
$isFunction
=
$this
->
_isFunction
())
!==
false
||
$this
->
_isSubselect
())
{
if
(
$isFunction
)
{
if
(
$this
->
isAggregateFunction
(
$this
->
_lexer
->
lookahead
[
'type'
]))
{
if
(
$this
->
_
isAggregateFunction
(
$this
->
_lexer
->
lookahead
[
'type'
]))
{
$expression
=
$this
->
AggregateExpression
();
}
else
{
$expression
=
$this
->
_Function
();
...
...
@@ -674,26 +1006,6 @@ class Parser
return
new
AST\SelectExpression
(
$expression
,
$fieldAliasIdentificationVariable
);
}
/**
* ResultVariable ::= identifier
*/
public
function
ResultVariable
()
{
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
return
$this
->
_lexer
->
token
[
'value'
];
}
/**
* IdentificationVariable ::= identifier
*/
public
function
IdentificationVariable
()
{
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
return
$this
->
_lexer
->
token
[
'value'
];
}
/**
* IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}*
...
...
@@ -734,35 +1046,15 @@ class Parser
// Building queryComponent
$queryComponent
=
array
(
'metadata'
=>
$classMetadata
,
'parent'
=>
null
,
'relation'
=>
null
,
'map'
=>
null
);
$this
->
_queryComponents
[
$aliasIdentificationVariable
]
=
$queryComponent
;
return
new
AST\RangeVariableDeclaration
(
$classMetadata
,
$aliasIdentificationVariable
);
}
/**
* AbstractSchemaName ::= identifier
*/
public
function
AbstractSchemaName
()
{
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
return
$this
->
_lexer
->
token
[
'value'
];
}
/**
* AliasIdentificationVariable = identifier
*/
public
function
AliasIdentificationVariable
()
{
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
'parent'
=>
null
,
'relation'
=>
null
,
'map'
=>
null
);
$this
->
_queryComponents
[
$aliasIdentificationVariable
]
=
$queryComponent
;
return
$this
->
_lexer
->
token
[
'value'
];
return
new
AST\RangeVariableDeclaration
(
$classMetadata
,
$aliasIdentificationVariable
);
}
/**
...
...
@@ -778,7 +1070,7 @@ class Parser
}
/**
* Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" Join
Association
PathExpression
* Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinPathExpression
* ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression]
*/
public
function
Join
()
...
...
@@ -848,20 +1140,6 @@ class Parser
return
$join
;
}
/**
* JoinPathExpression ::= IdentificationVariable "." (CollectionValuedAssociationField | SingleValuedAssociationField)
*/
public
function
JoinPathExpression
()
{
$identificationVariable
=
$this
->
IdentificationVariable
();
$this
->
match
(
'.'
);
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
return
new
AST\JoinPathExpression
(
$identificationVariable
,
$this
->
_lexer
->
token
[
'value'
]
);
}
/**
* IndexBy ::= "INDEX" "BY" SimpleStateFieldPathExpression
*/
...
...
@@ -878,236 +1156,6 @@ class Parser
return
$pathExp
;
}
/**
* StateFieldPathExpression ::= SimpleStateFieldPathExpression | SimpleStateFieldAssociationPathExpression
*/
public
function
StateFieldPathExpression
()
{
$pathExpr
=
$this
->
PathExpression
(
AST\PathExpression
::
TYPE_STATE_FIELD
);
if
(
!
empty
(
$this
->
_deferredPathExpressionStacks
))
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$exprStack
[]
=
$pathExpr
;
array_push
(
$this
->
_deferredPathExpressionStacks
,
$exprStack
);
return
$pathExpr
;
}
$this
->
_validateStateFieldPathExpression
(
$pathExpr
);
return
$pathExpr
;
}
/**
* SimpleStateFieldPathExpression ::= IdentificationVariable "." StateField
*/
public
function
SimpleStateFieldPathExpression
()
{
$pathExpr
=
$this
->
PathExpression
(
AST\PathExpression
::
TYPE_STATE_FIELD
);
if
(
!
empty
(
$this
->
_deferredPathExpressionStacks
))
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$exprStack
[]
=
$pathExpr
;
array_push
(
$this
->
_deferredPathExpressionStacks
,
$exprStack
);
return
$pathExpr
;
}
$this
->
_validateStateFieldPathExpression
(
$pathExpr
);
return
$pathExpr
;
}
/**
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* SingleValuedAssociationField
*/
public
function
SingleValuedAssociationPathExpression
()
{
$pathExpr
=
$this
->
PathExpression
(
AST\PathExpression
::
TYPE_SINGLE_VALUED_ASSOCIATION
);
if
(
!
empty
(
$this
->
_deferredPathExpressionStacks
))
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$exprStack
[]
=
$pathExpr
;
array_push
(
$this
->
_deferredPathExpressionStacks
,
$exprStack
);
return
$pathExpr
;
}
$this
->
_validateSingleValuedAssociationPathExpression
(
$pathExpr
);
return
$pathExpr
;
}
/**
* CollectionValuedPathExpression ::= IdentificationVariable "." {SingleValuedAssociationField "."}* CollectionValuedAssociationField
*/
public
function
CollectionValuedPathExpression
()
{
$pathExpr
=
$this
->
PathExpression
(
AST\PathExpression
::
TYPE_COLLECTION_VALUED_ASSOCIATION
);
if
(
!
empty
(
$this
->
_deferredPathExpressionStacks
))
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$exprStack
[]
=
$pathExpr
;
array_push
(
$this
->
_deferredPathExpressionStacks
,
$exprStack
);
return
$pathExpr
;
}
$this
->
_validateCollectionValuedPathExpression
(
$pathExpr
);
return
$pathExpr
;
}
/**
* SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression
*/
public
function
SingleValuedPathExpression
()
{
$pathExpr
=
$this
->
PathExpression
(
AST\PathExpression
::
TYPE_SINGLE_VALUED_PATH_EXPRESSION
);
if
(
!
empty
(
$this
->
_deferredPathExpressionStacks
))
{
$exprStack
=
array_pop
(
$this
->
_deferredPathExpressionStacks
);
$exprStack
[]
=
$pathExpr
;
array_push
(
$this
->
_deferredPathExpressionStacks
,
$exprStack
);
return
$pathExpr
;
}
$this
->
_validateSingleValuedPathExpression
(
$pathExpr
);
return
$pathExpr
;
}
/**
* Validates that the given <tt>PathExpression</tt> is a semantically correct
* CollectionValuedPathExpression as specified in the grammar.
*
* @param PathExpression $pathExpression
*/
private
function
_validateCollectionValuedPathExpression
(
AST\PathExpression
$pathExpression
)
{
$class
=
$this
->
_queryComponents
[
$pathExpression
->
getIdentificationVariable
()][
'metadata'
];
$collectionValuedField
=
null
;
foreach
(
$pathExpression
->
getParts
()
as
$field
)
{
if
(
$collectionValuedField
!==
null
)
{
$this
->
semanticalError
(
'Can not navigate through collection-valued field named '
.
$collectionValuedField
);
}
if
(
!
isset
(
$class
->
associationMappings
[
$field
]))
{
$this
->
semanticalError
(
'Class '
.
$class
->
name
.
' has no association field named '
.
$lastItem
);
}
if
(
$class
->
associationMappings
[
$field
]
->
isOneToOne
())
{
$class
=
$this
->
_em
->
getClassMetadata
(
$class
->
associationMappings
[
$field
]
->
targetEntityName
);
}
else
{
$collectionValuedField
=
$field
;
}
}
if
(
$collectionValuedField
===
null
)
{
$this
->
syntaxError
(
'SingleValuedAssociationField or CollectionValuedAssociationField'
);
}
}
/**
* Validates that the given <tt>PathExpression</tt> is a semantically correct
* SingleValuedAssociationPathExpression as specified in the grammar.
*
* @param PathExpression $pathExpression
*/
private
function
_validateSingleValuedAssociationPathExpression
(
AST\PathExpression
$pathExpression
)
{
$class
=
$this
->
_queryComponents
[
$pathExpression
->
getIdentificationVariable
()][
'metadata'
];
foreach
(
$pathExpression
->
getParts
()
as
$field
)
{
if
(
!
isset
(
$class
->
associationMappings
[
$field
]))
{
$this
->
semanticalError
(
'Class '
.
$class
->
name
.
' has no association field named '
.
$field
);
}
if
(
$class
->
associationMappings
[
$field
]
->
isOneToOne
())
{
$class
=
$this
->
_em
->
getClassMetadata
(
$class
->
associationMappings
[
$field
]
->
targetEntityName
);
}
else
{
$this
->
syntaxError
(
'SingleValuedAssociationField'
);
}
}
}
/**
* Validates that the given <tt>PathExpression</tt> is a semantically correct
* StateFieldPathExpression as specified in the grammar.
*
* @param PathExpression $pathExpression
*/
private
function
_validateStateFieldPathExpression
(
AST\PathExpression
$pathExpression
)
{
$stateFieldSeen
=
$this
->
_validateSingleValuedPathExpression
(
$pathExpression
);
if
(
!
$stateFieldSeen
)
{
$this
->
semanticalError
(
'Invalid StateFieldPathExpression. Must end in a state field.'
);
}
}
/**
* Validates that the given <tt>PathExpression</tt> is a semantically correct
* SingleValuedPathExpression as specified in the grammar.
*
* @param PathExpression $pathExpression
* @return boolean
*/
private
function
_validateSingleValuedPathExpression
(
AST\PathExpression
$pathExpression
)
{
$class
=
$this
->
_queryComponents
[
$pathExpression
->
getIdentificationVariable
()][
'metadata'
];
$stateFieldSeen
=
false
;
foreach
(
$pathExpression
->
getParts
()
as
$field
)
{
if
(
$stateFieldSeen
)
{
$this
->
semanticalError
(
'Cannot navigate through state field.'
);
}
if
(
isset
(
$class
->
associationMappings
[
$field
])
&&
$class
->
associationMappings
[
$field
]
->
isOneToOne
())
{
$class
=
$this
->
_em
->
getClassMetadata
(
$class
->
associationMappings
[
$field
]
->
targetEntityName
);
}
else
if
(
isset
(
$class
->
fieldMappings
[
$field
]))
{
$stateFieldSeen
=
true
;
}
else
{
$this
->
semanticalError
(
'SingleValuedAssociationField or StateField expected.'
);
}
}
// We need to force the type in PathExpression since SingleValuedPathExpression is in a
// state of recognition of what's is the dealed type (StateField or SingleValuedAssociation)
$pathExpression
->
setType
(
(
$stateFieldSeen
)
?
AST\PathExpression
::
TYPE_STATE_FIELD
:
AST\PathExpression
::
TYPE_SINGLE_VALUED_ASSOCIATION
);
return
$stateFieldSeen
;
}
/**
* Parses an arbitrary path expression without any semantic validation.
*
* PathExpression ::= IdentificationVariable "." {identifier "."}* identifier
*
* @return PathExpression
*/
public
function
PathExpression
(
$type
)
{
$identificationVariable
=
$this
->
IdentificationVariable
();
$parts
=
array
();
do
{
$this
->
match
(
'.'
);
$this
->
match
(
Lexer
::
T_IDENTIFIER
);
$parts
[]
=
$this
->
_lexer
->
token
[
'value'
];
}
while
(
$this
->
_lexer
->
isNextToken
(
'.'
));
return
new
AST\PathExpression
(
$type
,
$identificationVariable
,
$parts
);
}
/**
* EntityExpression ::= SingleValuedAssociationPathExpression | SimpleEntityExpression
*/
...
...
@@ -1574,23 +1622,6 @@ class Parser
return
isset
(
self
::
$_DATETIME_FUNCTIONS
[
strtolower
(
$funcName
)]);
}
/**
* Peeks beyond the specified token and returns the first token after that one.
*/
private
function
_peekBeyond
(
$token
)
{
$peek
=
$this
->
_lexer
->
peek
();
while
(
$peek
[
'value'
]
!=
$token
)
{
$peek
=
$this
->
_lexer
->
peek
();
}
$peek
=
$this
->
_lexer
->
peek
();
$this
->
_lexer
->
resetPeek
();
return
$peek
;
}
/**
* Checks whether the given token is a comparison operator.
*/
...
...
@@ -1985,7 +2016,7 @@ class Parser
$peek
=
$this
->
_lexer
->
glimpse
();
if
(
$peek
[
'value'
]
==
'('
)
{
if
(
$this
->
isAggregateFunction
(
$this
->
_lexer
->
lookahead
[
'type'
]))
{
if
(
$this
->
_
isAggregateFunction
(
$this
->
_lexer
->
lookahead
[
'type'
]))
{
return
$this
->
AggregateExpression
();
}
...
...
@@ -2046,16 +2077,6 @@ class Parser
return
$function
;
}
/**
* Checks whether the given token type indicates an aggregate function.
*/
public
function
isAggregateFunction
(
$tokenType
)
{
return
$tokenType
==
Lexer
::
T_AVG
||
$tokenType
==
Lexer
::
T_MIN
||
$tokenType
==
Lexer
::
T_MAX
||
$tokenType
==
Lexer
::
T_SUM
||
$tokenType
==
Lexer
::
T_COUNT
;
}
/**
* ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
*/
...
...
@@ -2180,22 +2201,12 @@ class Parser
$this
->
match
(
Lexer
::
T_INPUT_PARAMETER
);
return
new
AST\InputParameter
(
$this
->
_lexer
->
token
[
'value'
]);
}
else
if
(
$this
->
isAggregateFunction
(
$this
->
_lexer
->
lookahead
[
'type'
]))
{
}
else
if
(
$this
->
_
isAggregateFunction
(
$this
->
_lexer
->
lookahead
[
'type'
]))
{
return
$this
->
AggregateExpression
();
}
$this
->
syntaxError
(
'StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression'
);
}
/**
* Sets the list of custom tree walkers.
*
* @param array $treeWalkers
*/
public
function
setSqlTreeWalker
(
$treeWalker
)
{
$this
->
_treeWalker
=
$treeWalker
;
}
/**
* Registers a custom function that returns strings.
...
...
tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php
View file @
b3d110ba
...
...
@@ -47,7 +47,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
}
$parser
=
new
\Doctrine\ORM\Query\Parser
(
$query
);
$parser
->
setSqlTreeWalker
(
new
\Doctrine\Tests\Mocks\MockTreeWalker
);
//
$parser->setSqlTreeWalker(new \Doctrine\Tests\Mocks\MockTreeWalker);
return
$parser
->
parse
();
}
...
...
@@ -67,11 +67,6 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
$this
->
assertValidDql
(
'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u'
);
}
public
function
testInvalidSelectSingleComponentWithAsterisk
()
{
//$this->assertInvalidDql('SELECT p FROM Doctrine\Tests\Models\CMS\CmsUser u', true);
}
public
function
testSelectSingleComponentWithMultipleColumns
()
{
$this
->
assertValidDql
(
'SELECT u.name, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u'
);
...
...
@@ -247,14 +242,14 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
{
$this
->
assertValidDql
(
"SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name LIKE 'z|%' ESCAPE '|'"
);
}
/*
public function testImplicitJoinInWhereOnSingleValuedAssociationPathExpression()
/*
public function testImplicitJoinInWhereOnSingleValuedAssociationPathExpression()
{
// This should be allowed because avatar is a single-value association.
// SQL: SELECT ... FROM forum_user fu INNER JOIN forum_avatar fa ON fu.avatar_id = fa.id WHERE fa.id = ?
$this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.avatar.id = ?");
}
*/
}
*/
public
function
testImplicitJoinInWhereOnCollectionValuedPathExpression
()
{
// This should be forbidden, because articles is a collection
...
...
@@ -264,8 +259,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
public
function
testInvalidSyntaxIsRejected
()
{
$this
->
assertInvalidDql
(
"FOOBAR CmsUser"
);
//$this->assertInvalidDql("DELETE FROM Doctrine\Tests\Models\CMS\CmsUser.articles");
//$this->assertInvalidDql("DELETE FROM Doctrine\Tests\Models\CMS\CmsUser cu WHERE cu.articles.id > ?");
$this
->
assertInvalidDql
(
"DELETE FROM Doctrine\Tests\Models\CMS\CmsUser.articles"
);
$this
->
assertInvalidDql
(
"SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.articles.comments"
);
// Currently UNDEFINED OFFSET error
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment