Completed
Push — master ( a3828d...5ff92f )
by Gabriel
02:06 queued 11s
created

DoctrineExtension::formatSql()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
namespace Doctrine\Bundle\DoctrineBundle\Twig;
4
5
use SqlFormatter;
6
use Symfony\Component\VarDumper\Cloner\Data;
7
use Twig\Extension\AbstractExtension;
8
use Twig\TwigFilter;
9
10
/**
11
 * This class contains the needed functions in order to do the query highlighting
12
 */
13
class DoctrineExtension extends AbstractExtension
14
{
15
    /**
16
     * Define our functions
17
     *
18
     * @return TwigFilter[]
19
     */
20
    public function getFilters()
21
    {
22
        return [
23
            new TwigFilter('doctrine_pretty_query', [$this, 'formatQuery'], ['is_safe' => ['html'], 'deprecated' => true]),
24
            new TwigFilter('doctrine_prettify_sql', [$this, 'prettifySql'], ['is_safe' => ['html']]),
25
            new TwigFilter('doctrine_format_sql', [$this, 'formatSql'], ['is_safe' => ['html']]),
26
            new TwigFilter('doctrine_replace_query_parameters', [$this, 'replaceQueryParameters']),
27
        ];
28
    }
29
30
    /**
31
     * Get the possible combinations of elements from the given array
32
     */
33
    private function getPossibleCombinations(array $elements, int $combinationsLevel) : array
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
34
    {
35
        $baseCount = count($elements);
36
        $result    = [];
37
38
        if ($combinationsLevel === 1) {
39
            foreach ($elements as $element) {
40
                $result[] = [$element];
41
            }
42
43
            return $result;
44
        }
45
46
        $nextLevelElements = $this->getPossibleCombinations($elements, $combinationsLevel - 1);
47
48
        foreach ($nextLevelElements as $nextLevelElement) {
49
            $lastElement = $nextLevelElement[$combinationsLevel - 2];
50
            $found       = false;
51
52
            foreach ($elements as $key => $element) {
53
                if ($element === $lastElement) {
54
                    $found = true;
55
                    continue;
56
                }
57
58
                if ($found !== true || $key >= $baseCount) {
59
                    continue;
60
                }
61
62
                $tmp              = $nextLevelElement;
63
                $newCombination   = array_slice($tmp, 0);
64
                $newCombination[] = $element;
65
                $result[]         = array_slice($newCombination, 0);
66
            }
67
        }
68
69
        return $result;
70
    }
71
72
    /**
73
     * Escape parameters of a SQL query
74
     * DON'T USE THIS FUNCTION OUTSIDE ITS INTENDED SCOPE
75
     *
76
     * @internal
77
     *
78
     * @param mixed $parameter
79
     *
80
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
81
     */
82
    public static function escapeFunction($parameter)
83
    {
84
        $result = $parameter;
85
86
        switch (true) {
87
            // Check if result is non-unicode string using PCRE_UTF8 modifier
88
            case is_string($result) && ! preg_match('//u', $result):
89
                $result = '0x' . strtoupper(bin2hex($result));
90
                break;
91
92
            case is_string($result):
93
                $result = "'" . addslashes($result) . "'";
94
                break;
95
96
            case is_array($result):
97
                foreach ($result as &$value) {
98
                    $value = static::escapeFunction($value);
99
                }
100
101
                $result = implode(', ', $result);
102
                break;
103
104
            case is_object($result):
105
                $result = addslashes((string) $result);
106
                break;
107
108
            case $result === null:
109
                $result = 'NULL';
110
                break;
111
112
            case is_bool($result):
113
                $result = $result ? '1' : '0';
114
                break;
115
        }
116
117
        return $result;
118
    }
119
120
    /**
121
     * Return a query with the parameters replaced
122
     *
123
     * @param string     $query
124
     * @param array|Data $parameters
125
     *
126
     * @return string
127
     */
128
    public function replaceQueryParameters($query, $parameters)
129
    {
130
        if ($parameters instanceof Data) {
131
            $parameters = $parameters->getValue(true);
132
        }
133
134
        $i = 0;
135
136
        if (! array_key_exists(0, $parameters) && array_key_exists(1, $parameters)) {
137
            $i = 1;
138
        }
139
140
        return preg_replace_callback(
141
            '/\?|((?<!:):[a-z0-9_]+)/i',
142
            static function ($matches) use ($parameters, &$i) {
143
                $key = substr($matches[0], 1);
144
145
                if (! array_key_exists($i, $parameters) && ($key === false || ! array_key_exists($key, $parameters))) {
146
                    return $matches[0];
147
                }
148
149
                $value  = array_key_exists($i, $parameters) ? $parameters[$i] : $parameters[$key];
150
                $result = DoctrineExtension::escapeFunction($value);
151
                $i++;
152
153
                return $result;
154
            },
155
            $query
156
        );
157
    }
158
159
    /**
160
     * Formats and/or highlights the given SQL statement.
161
     *
162
     * @param  string $sql
163
     * @param  bool   $highlightOnly If true the query is not formatted, just highlighted
164
     *
165
     * @return string
166
     */
167
    public function formatQuery($sql, $highlightOnly = false)
168
    {
169
        @trigger_error(sprintf('The "%s()" method is deprecated and will be removed in DoctrineBundle 3.0.', __METHOD__), E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
170
171
        $this->setUpSqlFormatter();
172
173
        if ($highlightOnly) {
174
            $html = SqlFormatter::highlight($sql);
175
            $html = preg_replace('/<pre class=".*">([^"]*+)<\/pre>/Us', '\1', $html);
176
        } else {
177
            $html = SqlFormatter::format($sql);
178
            $html = preg_replace('/<pre class="(.*)">([^"]*+)<\/pre>/Us', '<div class="\1"><pre>\2</pre></div>', $html);
179
        }
180
181
        return $html;
182
    }
183
184
    public function prettifySql(string $sql) : string
185
    {
186
        $this->setUpSqlFormatter();
187
188
        return SqlFormatter::highlight($sql);
189
    }
190
191
    public function formatSql(string $sql, bool $highlight) : string
192
    {
193
        $this->setUpSqlFormatter();
194
195
        return SqlFormatter::format($sql, $highlight);
196
    }
197
198
    private function setUpSqlFormatter() : void
199
    {
200
        SqlFormatter::$pre_attributes            = 'class="highlight highlight-sql"';
201
        SqlFormatter::$quote_attributes          = 'class="string"';
202
        SqlFormatter::$backtick_quote_attributes = 'class="string"';
203
        SqlFormatter::$reserved_attributes       = 'class="keyword"';
204
        SqlFormatter::$boundary_attributes       = 'class="symbol"';
205
        SqlFormatter::$number_attributes         = 'class="number"';
206
        SqlFormatter::$word_attributes           = 'class="word"';
207
        SqlFormatter::$error_attributes          = 'class="error"';
208
        SqlFormatter::$comment_attributes        = 'class="comment"';
209
        SqlFormatter::$variable_attributes       = 'class="variable"';
210
    }
211
212
    /**
213
     * Get the name of the extension
214
     *
215
     * @return string
216
     */
217
    public function getName()
218
    {
219
        return 'doctrine_extension';
220
    }
221
}
222