Completed
Pull Request — master (#1032)
by Andreas
01:35
created

DoctrineExtension::shrinkParameters()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 34
rs 9.0648
cc 5
nc 9
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']]),
24
            new TwigFilter('doctrine_replace_query_parameters', [$this, 'replaceQueryParameters']),
25
        ];
26
    }
27
28
    /**
29
     * Get the possible combinations of elements from the given array
30
     *
31
     * @param array $elements
32
     * @param int   $combinationsLevel
33
     *
34
     * @return array
35
     */
36
    private function getPossibleCombinations(array $elements, $combinationsLevel)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
37
    {
38
        $baseCount = count($elements);
39
        $result    = [];
40
41
        if ($combinationsLevel === 1) {
42
            foreach ($elements as $element) {
43
                $result[] = [$element];
44
            }
45
46
            return $result;
47
        }
48
49
        $nextLevelElements = $this->getPossibleCombinations($elements, $combinationsLevel - 1);
50
51
        foreach ($nextLevelElements as $nextLevelElement) {
52
            $lastElement = $nextLevelElement[$combinationsLevel - 2];
53
            $found       = false;
54
55
            foreach ($elements as $key => $element) {
56
                if ($element === $lastElement) {
57
                    $found = true;
58
                    continue;
59
                }
60
61
                if ($found !== true || $key >= $baseCount) {
62
                    continue;
63
                }
64
65
                $tmp              = $nextLevelElement;
66
                $newCombination   = array_slice($tmp, 0);
67
                $newCombination[] = $element;
68
                $result[]         = array_slice($newCombination, 0);
69
            }
70
        }
71
72
        return $result;
73
    }
74
75
    /**
76
     * Escape parameters of a SQL query
77
     * DON'T USE THIS FUNCTION OUTSIDE ITS INTENDED SCOPE
78
     *
79
     * @internal
80
     *
81
     * @param mixed $parameter
82
     *
83
     * @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...
84
     */
85
    public static function escapeFunction($parameter)
86
    {
87
        $result = $parameter;
88
89
        switch (true) {
90
            // Check if result is non-unicode string using PCRE_UTF8 modifier
91
            case is_string($result) && ! preg_match('//u', $result):
92
                $result = '0x' . strtoupper(bin2hex($result));
93
                break;
94
95
            case is_string($result):
96
                $result = "'" . addslashes($result) . "'";
97
                break;
98
99
            case is_array($result):
100
                foreach ($result as &$value) {
101
                    $value = static::escapeFunction($value);
102
                }
103
104
                $result = implode(', ', $result);
105
                break;
106
107
            case is_object($result):
108
                $result = addslashes((string) $result);
109
                break;
110
111
            case $result === null:
112
                $result = 'NULL';
113
                break;
114
115
            case is_bool($result):
116
                $result = $result ? '1' : '0';
117
                break;
118
        }
119
120
        return $result;
121
    }
122
123
    /**
124
     * Return a query with the parameters replaced
125
     *
126
     * @param string     $query
127
     * @param array|Data $parameters
128
     *
129
     * @return string
130
     */
131
    public function replaceQueryParameters($query, $parameters)
132
    {
133
        if ($parameters instanceof Data) {
134
            $parameters = $parameters->getValue(true);
135
        }
136
137
        $i = 0;
138
139
        if (! array_key_exists(0, $parameters) && array_key_exists(1, $parameters)) {
140
            $i = 1;
141
        }
142
143
        return preg_replace_callback(
144
            '/\?|((?<!:):[a-z0-9_]+)/i',
145
            static function ($matches) use ($parameters, &$i) {
146
                $key = substr($matches[0], 1);
147
148
                if (! array_key_exists($i, $parameters) && ($key === false || ! array_key_exists($key, $parameters))) {
149
                    return $matches[0];
150
                }
151
152
                $value  = array_key_exists($i, $parameters) ? $parameters[$i] : $parameters[$key];
153
                $result = DoctrineExtension::escapeFunction($value);
154
                $i++;
155
156
                return $result;
157
            },
158
            $query
159
        );
160
    }
161
162
    /**
163
     * Formats and/or highlights the given SQL statement.
164
     *
165
     * @param  string $sql
166
     * @param  bool   $highlightOnly If true the query is not formatted, just highlighted
167
     *
168
     * @return string
169
     */
170
    public function formatQuery($sql, $highlightOnly = false)
171
    {
172
        SqlFormatter::$pre_attributes            = 'class="highlight highlight-sql"';
173
        SqlFormatter::$quote_attributes          = 'class="string"';
174
        SqlFormatter::$backtick_quote_attributes = 'class="string"';
175
        SqlFormatter::$reserved_attributes       = 'class="keyword"';
176
        SqlFormatter::$boundary_attributes       = 'class="symbol"';
177
        SqlFormatter::$number_attributes         = 'class="number"';
178
        SqlFormatter::$word_attributes           = 'class="word"';
179
        SqlFormatter::$error_attributes          = 'class="error"';
180
        SqlFormatter::$comment_attributes        = 'class="comment"';
181
        SqlFormatter::$variable_attributes       = 'class="variable"';
182
183
        if ($highlightOnly) {
184
            $html = SqlFormatter::highlight($sql);
185
            $html = preg_replace('/<pre class=".*">([^"]*+)<\/pre>/Us', '\1', $html);
186
        } else {
187
            $html = SqlFormatter::format($sql);
188
            $html = preg_replace('/<pre class="(.*)">([^"]*+)<\/pre>/Us', '<div class="\1"><pre>\2</pre></div>', $html);
189
        }
190
191
        return $html;
192
    }
193
194
    /**
195
     * Get the name of the extension
196
     *
197
     * @return string
198
     */
199
    public function getName()
200
    {
201
        return 'doctrine_extension';
202
    }
203
}
204