Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
declare(strict_types=1);
18
 
19
namespace core_reportbuilder\local\helpers;
20
 
21
use coding_exception;
22
use core_text;
23
 
24
/**
25
 * Helper functions for DB manipulations
26
 *
27
 * @package     core_reportbuilder
28
 * @copyright   2019 Marina Glancy
29
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30
 */
31
class database {
32
 
33
    /** @var string Prefix for generated aliases */
34
    private const GENERATE_ALIAS_PREFIX = 'rbalias';
35
 
36
    /** @var string Prefix for generated param names names */
37
    private const GENERATE_PARAM_PREFIX = 'rbparam';
38
 
39
    /**
40
     * Generates unique table/column alias that must be used in generated SQL
41
     *
42
     * @param string $suffix Optional string to append to alias
43
     * @return string
44
     */
45
    public static function generate_alias(string $suffix = ''): string {
46
        static $aliascount = 0;
47
 
48
        return static::GENERATE_ALIAS_PREFIX . ($aliascount++) . $suffix;
49
    }
50
 
51
    /**
52
     * Generate multiple unique table/column aliases, see {@see generate_alias} for info
53
     *
54
     * @param int $count
55
     * @param string $suffix
56
     * @return string[]
57
     */
58
    public static function generate_aliases(int $count, string $suffix = ''): array {
59
        return array_map([static::class, 'generate_alias'], array_fill(0, $count, $suffix));
60
    }
61
 
62
    /**
63
     * Generates unique parameter name that must be used in generated SQL
64
     *
65
     * When passing the returned value to {@see \moodle_database::get_in_or_equal} it's recommended to define the suffix
66
     *
67
     * @param string $suffix Optional string to append to parameter name
68
     * @return string
69
     */
70
    public static function generate_param_name(string $suffix = ''): string {
71
        static $paramcount = 0;
72
 
73
        return static::GENERATE_PARAM_PREFIX . ($paramcount++) . $suffix;
74
    }
75
 
76
    /**
77
     * Generate multiple unique parameter names, see {@see generate_param_name} for info
78
     *
79
     * @param int $count
80
     * @param string $suffix
81
     * @return string[]
82
     */
83
    public static function generate_param_names(int $count, string $suffix = ''): array {
84
        return array_map([static::class, 'generate_param_name'], array_fill(0, $count, $suffix));
85
    }
86
 
87
    /**
88
     * Validate that parameter names were generated using {@see generate_param_name}.
89
     *
90
     * @param array $params
91
     * @return bool
92
     * @throws coding_exception For invalid params.
93
     */
94
    public static function validate_params(array $params): bool {
95
        $nonmatchingkeys = array_filter($params, static function($key): bool {
96
            return !preg_match('/^' . static::GENERATE_PARAM_PREFIX . '[\d]+/', $key);
97
        }, ARRAY_FILTER_USE_KEY);
98
 
99
        if (!empty($nonmatchingkeys)) {
100
            throw new coding_exception('Invalid parameter names', implode(', ', array_keys($nonmatchingkeys)));
101
        }
102
 
103
        return true;
104
    }
105
 
106
    /**
107
     * Replace parameter names within given SQL expression, allowing caller to specify callback to handle their replacement
108
     * primarily to ensure uniqueness when the expression is to be used as part of a larger query
109
     *
110
     * @param string $sql
111
     * @param array $params Parameter names
112
     * @param callable $callback Method that takes a single string parameter, and returns another string
113
     * @return string
114
     */
115
    public static function sql_replace_parameter_names(string $sql, array $params, callable $callback): string {
116
        foreach ($params as $param) {
117
 
118
            // Pattern to look for param within the SQL.
119
            $pattern = '/:(?<param>' . preg_quote($param) . ')\b/';
120
 
121
            $sql = preg_replace_callback($pattern, function(array $matches) use ($callback): string {
122
                return ':' . $callback($matches['param']);
123
            }, $sql);
124
        }
125
 
126
        return $sql;
127
    }
128
 
129
    /**
130
     * Replace parameter names within given SQL expression, returning updated SQL and parameter elements
131
     *
132
     * {@see sql_replace_parameter_names}
133
     *
134
     * @param string $sql
135
     * @param array $params Parameter name/values
136
     * @param callable $callback
137
     * @return array [$sql, $params]
138
     */
139
    public static function sql_replace_parameters(string $sql, array $params, callable $callback): array {
140
        $transformedsql = static::sql_replace_parameter_names($sql, array_keys($params), $callback);
141
 
142
        $transformedparams = [];
143
        foreach ($params as $name => $value) {
144
            $transformedparams[$callback($name)] = $value;
145
        }
146
 
147
        return [$transformedsql, $transformedparams];
148
    }
149
 
150
    /**
151
     * Generate SQL expression for sorting group concatenated fields
152
     *
153
     * @param string $field The original field or SQL expression
154
     * @param string|null $sort A valid SQL ORDER BY to sort the concatenated fields, if omitted then $field will be used
155
     * @return string
156
     */
157
    public static function sql_group_concat_sort(string $field, string $sort = null): string {
158
        global $DB;
159
 
160
        // Fallback to sorting by the specified field, unless it contains parameters which would be duplicated.
161
        if ($sort === null && !preg_match('/[:?$]/', $field)) {
162
            $fieldsort = $field;
163
        } else {
164
            $fieldsort = $sort;
165
        }
166
 
167
        // Nothing to sort by.
168
        if ($fieldsort === null) {
169
            return '';
170
        }
171
 
172
        // If the sort specifies a direction, we need to handle that differently in Postgres.
173
        if ($DB->get_dbfamily() === 'postgres') {
174
            $fieldsortdirection = '';
175
 
176
            preg_match('/(?<direction>ASC|DESC)?$/i', $fieldsort, $matches);
177
            if (array_key_exists('direction', $matches)) {
178
                $fieldsortdirection = $matches['direction'];
179
                $fieldsort = core_text::substr($fieldsort, 0, -(core_text::strlen($fieldsortdirection)));
180
            }
181
 
182
            // Cast sort, stick the direction on the end.
183
            $fieldsort = $DB->sql_cast_to_char($fieldsort) . ' ' . $fieldsortdirection;
184
        }
185
 
186
        return $fieldsort;
187
    }
188
}