Skip to content

Commit

Permalink
Merge pull request #23 from moufmouf/fix_null_equals
Browse files Browse the repository at this point in the history
Transforms automatically '= null' into 'IS NULL'.
  • Loading branch information
moufmouf committed Apr 4, 2016
2 parents edfb576 + 6461623 commit 88a35ec
Show file tree
Hide file tree
Showing 59 changed files with 375 additions and 277 deletions.
54 changes: 37 additions & 17 deletions src/Mouf/Database/MagicQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ class MagicQuery
private $enableTwig = false;

/**
* @param \Doctrine\DBAL\Connection $connection
* @param \Doctrine\DBAL\Connection $connection
* @param \Doctrine\Common\Cache\Cache $cache
* @param SchemaAnalyzer $schemaAnalyzer (optional). If not set, it is initialized from the connection.
* @param SchemaAnalyzer $schemaAnalyzer (optional). If not set, it is initialized from the connection.
*/
public function __construct($connection = null, $cache = null, SchemaAnalyzer $schemaAnalyzer = null)
{
Expand All @@ -54,11 +54,15 @@ public function __construct($connection = null, $cache = null, SchemaAnalyzer $s
/**
* Whether Twig parsing should be enabled or not.
* Defaults to false.
*
* @param bool $enableTwig
*
* @return $this
*/
public function setEnableTwig($enableTwig = true) {
public function setEnableTwig($enableTwig = true)
{
$this->enableTwig = $enableTwig;

return $this;
}

Expand All @@ -79,6 +83,7 @@ public function build($sql, array $parameters = array())
$sql = $this->getTwigEnvironment()->render($sql, $parameters);
}
$select = $this->parse($sql);

return $this->toSql($select, $parameters);
}

Expand All @@ -87,13 +92,16 @@ public function build($sql, array $parameters = array())
* This tree representation can be used to manipulate the SQL.
*
* @param string $sql
*
* @return NodeInterface
*
* @throws MagicQueryMissingConnectionException
* @throws MagicQueryParserException
*/
public function parse($sql) {
public function parse($sql)
{
// We choose md4 because it is fast.
$cacheKey = "request_".hash("md4", $sql);
$cacheKey = 'request_'.hash('md4', $sql);
$select = $this->cache->fetch($cacheKey);

if ($select === false) {
Expand All @@ -111,27 +119,32 @@ public function parse($sql) {
// Let's store the tree
$this->cache->save($cacheKey, $select);
}

return $select;
}

/**
* Transforms back a tree of SQL node into a SQL string.
*
* @param NodeInterface $sqlNode
* @param array $parameters
* @param array $parameters
*
* @return string
*/
public function toSql(NodeInterface $sqlNode, array $parameters = array()) {
public function toSql(NodeInterface $sqlNode, array $parameters = array())
{
return $sqlNode->toSql($parameters, $this->connection, 0, SqlRenderInterface::CONDITION_GUESS);
}

/**
* Scans the SQL statement and replaces the "magicjoin" part with the correct joins.
*
* @param NodeInterface $select
*
* @throws MagicQueryMissingConnectionException
*/
private function magicJoin(NodeInterface $select) {
private function magicJoin(NodeInterface $select)
{
// Let's find if this is a MagicJoin query.
$magicJoinDetector = new DetectMagicJoinSelectVisitor();
$nodeTraverser = new NodeTraverser();
Expand All @@ -147,11 +160,14 @@ private function magicJoin(NodeInterface $select) {
}

/**
* For one given MagicJoin select, let's apply MagicJoin
* For one given MagicJoin select, let's apply MagicJoin.
*
* @param MagicJoinSelect $magicJoinSelect
*
* @return Select
*/
private function magicJoinOnOneQuery(MagicJoinSelect $magicJoinSelect) {
private function magicJoinOnOneQuery(MagicJoinSelect $magicJoinSelect)
{
$tableSearchNodeTraverser = new NodeTraverser();
$detectTableVisitor = new DetectTablesVisitor($magicJoinSelect->getMainTable());
$tableSearchNodeTraverser->addVisitor($detectTableVisitor);
Expand Down Expand Up @@ -183,7 +199,7 @@ private function magicJoinOnOneQuery(MagicJoinSelect $magicJoinSelect) {
$tableNode = new Table();
$tableNode->setTable($mainTable);
$tables = [
$mainTable => $tableNode
$mainTable => $tableNode,
];

foreach ($completePath as $foreignKey) {
Expand All @@ -202,7 +218,7 @@ private function magicJoinOnOneQuery(MagicJoinSelect $magicJoinSelect) {
$onNode->setRightOperand($rightCol);

$tableNode = new Table();
$tableNode->setJoinType("LEFT JOIN");
$tableNode->setJoinType('LEFT JOIN');
$tableNode->setRefClause($onNode);

if (isset($tables[$foreignKey->getLocalTableName()])) {
Expand All @@ -215,34 +231,38 @@ private function magicJoinOnOneQuery(MagicJoinSelect $magicJoinSelect) {
}

$select->setFrom($tables);

}

/**
* @return SchemaAnalyzer
*/
private function getSchemaAnalyzer() {
private function getSchemaAnalyzer()
{
if ($this->schemaAnalyzer === null) {
if (!$this->connection) {
throw new MagicQueryMissingConnectionException('In order to use MagicJoin, you need to configure a DBAL connection.');
}

$this->schemaAnalyzer = new SchemaAnalyzer($this->connection->getSchemaManager(), $this->cache, $this->getConnectionUniqueId());
}

return $this->schemaAnalyzer;
}

private function getConnectionUniqueId() {
return hash('md4', $this->connection->getHost()."-".$this->connection->getPort()."-".$this->connection->getDatabase()."-".$this->connection->getDriver()->getName());
private function getConnectionUniqueId()
{
return hash('md4', $this->connection->getHost().'-'.$this->connection->getPort().'-'.$this->connection->getDatabase().'-'.$this->connection->getDriver()->getName());
}

/**
* @return \Twig_Environment
*/
private function getTwigEnvironment() {
private function getTwigEnvironment()
{
if ($this->twigEnvironment === null) {
$this->twigEnvironment = SqlTwigEnvironmentFactory::getTwigEnvironment();
}

return $this->twigEnvironment;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?php

namespace Mouf\Database\MagicQuery\Twig;

use Mouf\Database\MagicQueryException;

class ForbiddenTwigParameterInSqlException extends MagicQueryException
{

}
}
17 changes: 9 additions & 8 deletions src/Mouf/Database/MagicQuery/Twig/SqlTwigEnvironmentFactory.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<?php
namespace Mouf\Database\MagicQuery\Twig;

use Doctrine\DBAL\Connection;
namespace Mouf\Database\MagicQuery\Twig;

/**
* Class in charge of creating the Twig environment
* Class in charge of creating the Twig environment.
*/
class SqlTwigEnvironmentFactory
{
private static $twig;

public static function getTwigEnvironment() {
public static function getTwigEnvironment()
{
if (self::$twig) {
return self::$twig;
}
Expand All @@ -20,14 +20,14 @@ public static function getTwigEnvironment() {
$options = array(
// The cache directory is in the temporary directory and reproduces the path to the directory (to avoid cache conflict between apps).
'cache' => self::getCacheDirectory(),
'strict_variables' => true
'strict_variables' => true,
);

$twig = new \Twig_Environment($stringLoader, $options);

// Default escaper will throw an exception. This is because we want to use SQL parameters instead of Twig.
// This ahs a number of advantages, especially in terms of caching.
$twig->getExtension('core')->setEscaper('sql', function() {
$twig->getExtension('core')->setEscaper('sql', function () {
throw new ForbiddenTwigParameterInSqlException('You cannot use Twig expressions (like "{{ id }}"). Instead, you should use SQL parameters (like ":id"). Twig integration is limited to Twig statements (like "{% for .... %}"');
});

Expand All @@ -39,7 +39,8 @@ public static function getTwigEnvironment() {
return $twig;
}

private static function getCacheDirectory() {
private static function getCacheDirectory()
{
// If we are running on a Unix environment, let's prepend the cache with the user id of the PHP process.
// This way, we can avoid rights conflicts.

Expand All @@ -50,7 +51,7 @@ private static function getCacheDirectory() {
$posixGetuid = '';
}
// @codeCoverageIgnoreEnd
$cacheDirectory = rtrim(sys_get_temp_dir(), '/\\').'/magicquerysqltwigtemplate'.$posixGetuid.str_replace(":", "", __DIR__);
$cacheDirectory = rtrim(sys_get_temp_dir(), '/\\').'/magicquerysqltwigtemplate'.$posixGetuid.str_replace(':', '', __DIR__);

return $cacheDirectory;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Mouf/Database/MagicQuery/Twig/StringLoader.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
namespace Mouf\Database\MagicQuery\Twig;

namespace Mouf\Database\MagicQuery\Twig;

/**
* This loader completely bypasses the loader mechanism, by directly passing the key as a template.
Expand Down
5 changes: 2 additions & 3 deletions src/Mouf/Database/MagicQueryMissingConnectionException.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<?php
namespace Mouf\Database;

namespace Mouf\Database;

class MagicQueryMissingConnectionException extends MagicQueryException
{

}
}
1 change: 0 additions & 1 deletion src/SQLParser/ExpressionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/

namespace SQLParser;

class ExpressionType
Expand Down
1 change: 0 additions & 1 deletion src/SQLParser/InvalidParameterException.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/

namespace SQLParser;

class InvalidParameterException extends \InvalidArgumentException
Expand Down
1 change: 0 additions & 1 deletion src/SQLParser/LexerSplitter.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/

namespace SQLParser;

class LexerSplitter
Expand Down
4 changes: 3 additions & 1 deletion src/SQLParser/Node/AbstractManyInstancesOperator.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ public function toSql(array $parameters = array(), Connection $dbConnection = nu
*
* @param VisitorInterface $visitor
*/
public function walk(VisitorInterface $visitor) {
public function walk(VisitorInterface $visitor)
{
$node = $this;
$result = $visitor->enterNode($node);
if ($result instanceof NodeInterface) {
Expand All @@ -97,6 +98,7 @@ public function walk(VisitorInterface $visitor) {
}
}
}

return $visitor->leaveNode($node);
}

Expand Down
18 changes: 13 additions & 5 deletions src/SQLParser/Node/AbstractTwoOperandsOperator.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,22 +117,30 @@ public function toSql(array $parameters = array(), Connection $dbConnection = nu
}
}
if ($conditionsMode == self::CONDITION_IGNORE || !$this->condition || $this->condition->isOk($parameters)) {
$sql = NodeFactory::toSql($this->leftOperand, $dbConnection, $parameters, ' ', false, $indent, $conditionsMode);
$sql .= ' '.$this->getOperatorSymbol().' ';
$sql .= NodeFactory::toSql($this->rightOperand, $dbConnection, $parameters, ' ', false, $indent, $conditionsMode);
$sql = $this->getSql($parameters, $dbConnection, $indent, $conditionsMode);
} else {
$sql = null;
}

return $sql;
}

protected function getSql(array $parameters = array(), Connection $dbConnection = null, $indent = 0, $conditionsMode = self::CONDITION_APPLY)
{
$sql = NodeFactory::toSql($this->leftOperand, $dbConnection, $parameters, ' ', false, $indent, $conditionsMode);
$sql .= ' '.$this->getOperatorSymbol().' ';
$sql .= NodeFactory::toSql($this->rightOperand, $dbConnection, $parameters, ' ', false, $indent, $conditionsMode);

return $sql;
}

/**
* Walks the tree of nodes, calling the visitor passed in parameter.
*
* @param VisitorInterface $visitor
*/
public function walk(VisitorInterface $visitor) {
public function walk(VisitorInterface $visitor)
{
$node = $this;
$result = $visitor->enterNode($node);
if ($result instanceof NodeInterface) {
Expand All @@ -153,10 +161,10 @@ public function walk(VisitorInterface $visitor) {
$this->rightOperand = $result2;
}
}

return $visitor->leaveNode($node);
}


/**
* Returns the symbol for this operator.
*/
Expand Down
5 changes: 3 additions & 2 deletions src/SQLParser/Node/AggregateFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/

namespace SQLParser\Node;

use Doctrine\DBAL\Connection;
Expand Down Expand Up @@ -158,7 +157,8 @@ public function toSql(array $parameters = array(), Connection $dbConnection = nu
*
* @param VisitorInterface $visitor
*/
public function walk(VisitorInterface $visitor) {
public function walk(VisitorInterface $visitor)
{
$node = $this;
$result = $visitor->enterNode($node);
if ($result instanceof NodeInterface) {
Expand All @@ -174,6 +174,7 @@ public function walk(VisitorInterface $visitor) {
}
}
}

return $visitor->leaveNode($node);
}
}
1 change: 0 additions & 1 deletion src/SQLParser/Node/AndOp.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/

namespace SQLParser\Node;

/**
Expand Down
Loading

0 comments on commit 88a35ec

Please sign in to comment.