Skip to content

Commit 3eb2186

Browse files
authored
Merge pull request #45 from moufmouf/perf_improvement_fix
Performance improvement fix
2 parents 6295522 + 40c2910 commit 3eb2186

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+218
-124
lines changed

.travis.yml

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
11
language: php
22

3-
php:
4-
- 5.6
5-
- 7.0
6-
- 7.1
7-
- 7.2
8-
9-
env:
10-
matrix:
11-
- PREFER_LOWEST=""
12-
- PREFER_LOWEST="--prefer-lowest"
13-
14-
before_script:
15-
- composer update --no-interaction $PREFER_LOWEST
16-
17-
script:
18-
- vendor/bin/phpunit
19-
20-
after_script:
21-
- php vendor/bin/coveralls -v
3+
cache:
4+
directories:
5+
- $HOME/.composer/cache
6+
- .php_cs
7+
jobs:
8+
include:
9+
- stage: test
10+
php: 7.2
11+
env: PREFER_LOWEST=""
12+
before_script:
13+
- &composerupdate
14+
composer update --prefer-dist $PREFER_LOWEST
15+
script:
16+
- &phpunit
17+
"./vendor/bin/phpunit"
18+
- composer phpstan
19+
after_script:
20+
- ./vendor/bin/coveralls -v
21+
- stage: test
22+
php: 7.2
23+
env: PREFER_LOWEST="--prefer-lowest"
24+
before_script:
25+
- *composerupdate
26+
script:
27+
- *phpunit
28+
- stage: test
29+
php: 7.1
30+
env: PREFER_LOWEST=""
31+
before_script:
32+
- *composerupdate
33+
script:
34+
- *phpunit

composer.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
}
1414
],
1515
"require": {
16-
"php": ">=5.3.0",
16+
"php": ">=7.1.0",
1717
"mouf/utils.common.conditioninterface": "~2.0",
1818
"mouf/utils.value.value-interface": "~1.0",
1919
"mouf/utils.common.paginable-interface": "~1.0",
@@ -24,9 +24,10 @@
2424
"doctrine/cache": "^1.5"
2525
},
2626
"require-dev": {
27-
"phpunit/phpunit": "~5.0",
27+
"phpunit/phpunit": "^7.5.10",
2828
"satooshi/php-coveralls": "~1.0",
29-
"doctrine/dbal": "~2.5"
29+
"doctrine/dbal": "~2.5",
30+
"phpstan/phpstan": "^0.11.7"
3031
},
3132
"suggest": {
3233
"doctrine/dbal": "To support more databases than just MySQL and to use MagicJoin feature",
@@ -64,5 +65,8 @@
6465
}
6566
},
6667
"minimum-stability": "dev",
67-
"prefer-stable": true
68+
"prefer-stable": true,
69+
"scripts": {
70+
"phpstan": "vendor/bin/phpstan analyse -c phpstan.neon src"
71+
}
6872
}

doc/discard_unused_parameters.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ $sql = $magicQuery->build($sql, []);
9898
// $sql == "SELECT * FROM products p WHERE status2 = null"
9999
```
100100

101+
####Working with prepared statements
102+
103+
The `MagicQuery::build` method will remove unused parameters AND replace placeholders with the values.
104+
If you are looking for the best possible performance, you can use the alternative `MagicQuery::buildPreparedStatement` method.
105+
106+
The "buildPreparedStatement" method is removing unused parameters BUT it does not replace placeholders.
107+
As a result, MagicQuery can perform some more internal caching and if you have many similar requests,
108+
you will get some performance gains.
109+
101110
###Other alternatives
102111

103112
To avoid concatenating strings, frameworks and libraries have used different strategies. Using a full ORM (like

phpstan.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
parameters:
2+
level: 2
3+
ignoreErrors:
4+
- "#Mouf\\\\MoufManager#"
5+
- "#Mouf\\\\MoufInstanceDescriptor#"

phpunit.xml.dist

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
convertWarningsToExceptions="true"
99
processIsolation="false"
1010
stopOnFailure="false"
11-
syntaxCheck="false"
1211
bootstrap="tests/bootstrap.php"
1312
>
1413

@@ -31,7 +30,7 @@
3130
</whitelist>
3231
</filter>
3332
<logging>
34-
<log type="coverage-html" target="build/coverage" charset="UTF-8" yui="true" highlight="true"/>
33+
<log type="coverage-html" target="build/coverage" />
3534
<log type="coverage-clover" target="build/logs/clover.xml"/>
3635
</logging>
3736
</phpunit>

src/Mouf/Database/MagicQuery.php

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
namespace Mouf\Database;
44

5+
use function array_filter;
6+
use function array_keys;
57
use Doctrine\Common\Cache\VoidCache;
8+
use function hash;
9+
use function implode;
610
use Mouf\Database\MagicQuery\Twig\SqlTwigEnvironmentFactory;
711
use Mouf\Database\SchemaAnalyzer\SchemaAnalyzer;
812
use PHPSQLParser\PHPSQLParser;
@@ -83,9 +87,42 @@ public function build($sql, array $parameters = array())
8387
if ($this->enableTwig) {
8488
$sql = $this->getTwigEnvironment()->render($sql, $parameters);
8589
}
90+
8691
$select = $this->parse($sql);
92+
$newSql = $this->toSql($select, $parameters, true);
8793

88-
return $this->toSql($select, $parameters);
94+
return $newSql;
95+
}
96+
97+
/**
98+
* Returns modified SQL from $sql and $parameters. Any parameters not available will be striped down
99+
* from the SQL. Unlike with the `build` method, the parameters are NOT merged into the SQL.
100+
* This method is more efficient than `build` (because result is cached and statements interpolation
101+
* can be delegated to the database.
102+
*
103+
* @param string $sql
104+
* @param array $parameters
105+
*
106+
* @return string
107+
*/
108+
public function buildPreparedStatement(string $sql, array $parameters = []): string
109+
{
110+
if ($this->enableTwig) {
111+
$sql = $this->getTwigEnvironment()->render($sql, $parameters);
112+
}
113+
114+
$availableParameterKeys = array_keys(array_filter($parameters, static function($param) { return $param !== null;}));
115+
// We choose md4 because it is fast.
116+
$cacheKey = 'request_build_'.hash('md4', $sql.'__'.implode('_/_', $availableParameterKeys));
117+
$newSql = $this->cache->fetch($cacheKey);
118+
if ($newSql === false) {
119+
$select = $this->parse($sql);
120+
$newSql = $this->toSql($select, $parameters, false);
121+
122+
$this->cache->save($cacheKey, $newSql);
123+
}
124+
125+
return $newSql;
89126
}
90127

91128
/**
@@ -129,13 +166,14 @@ public function parse($sql)
129166
* Transforms back a tree of SQL node into a SQL string.
130167
*
131168
* @param NodeInterface $sqlNode
132-
* @param array $parameters
133-
*
169+
* @param array $parameters
170+
* @param bool $extrapolateParameters Whether the parameters should be fed into the returned SQL query
171+
134172
* @return string
135173
*/
136-
public function toSql(NodeInterface $sqlNode, array $parameters = array())
174+
public function toSql(NodeInterface $sqlNode, array $parameters = array(), bool $extrapolateParameters = true)
137175
{
138-
return $sqlNode->toSql($parameters, $this->connection, 0, SqlRenderInterface::CONDITION_GUESS);
176+
return $sqlNode->toSql($parameters, $this->connection, 0, SqlRenderInterface::CONDITION_GUESS, $extrapolateParameters);
139177
}
140178

141179
/**

src/Mouf/Database/MagicQuery/Twig/SqlTwigEnvironmentFactory.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Mouf\Database\MagicQuery\Twig;
44

5+
use Twig_Extension_Core;
6+
57
/**
68
* Class in charge of creating the Twig environment.
79
*/
@@ -28,7 +30,10 @@ public static function getTwigEnvironment()
2830

2931
// Default escaper will throw an exception. This is because we want to use SQL parameters instead of Twig.
3032
// This has a number of advantages, especially in terms of caching.
31-
$twig->getExtension('Twig_Extension_Core')->setEscaper('sql', function () {
33+
34+
/** @var Twig_Extension_Core $twigExtensionCore */
35+
$twigExtensionCore = $twig->getExtension('Twig_Extension_Core');
36+
$twigExtensionCore->setEscaper('sql', function () {
3237
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 .... %}"');
3338
});
3439

src/Mouf/Database/QueryWriter/Condition/ParamAvailableCondition.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ public function __construct($parameterName)
2323
$this->parameterName = $parameterName;
2424
}
2525

26-
/**
27-
* @param string $caller
28-
*/
2926
public function isOk($parameters = null)
3027
{
3128
return isset($parameters[$this->parameterName]) && !empty($parameters[$this->parameterName]);

src/Mouf/Database/QueryWriter/Condition/ParamEqualsCondition.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ public function __construct($parameterName, $value)
2828
$this->value = $value;
2929
}
3030

31-
/**
32-
* @param string $caller
33-
*/
3431
public function isOk($parameters = null)
3532
{
3633
return isset($parameters[$this->parameterName]) && $parameters[$this->parameterName] == ValueUtils::val($this->value);

src/Mouf/Database/QueryWriter/Condition/ParamNotAvailableCondition.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@ public function __construct($parameterName)
2323
$this->parameterName = $parameterName;
2424
}
2525

26-
/**
27-
* @param string $caller
28-
*/
2926
public function isOk($parameters = null)
3027
{
3128
return !isset($parameters[$this->parameterName]) || empty($parameters[$this->parameterName]);

0 commit comments

Comments
 (0)