Passed
Push — master ( 57a957...80c517 )
by Andreas
70:50 queued 45:35
created

midcom_core_collector::destroy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package midcom
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
/**
10
 * MidCOM DBA level wrapper for the Midgard Collector.
11
 *
12
 * This class must be used anyplace within MidCOM instead of the real
13
 * midgard_collector object within the MidCOM Framework. This wrapper is
14
 * required for the correct operation of many MidCOM services.
15
 *
16
 * It essentially wraps the calls to {@link midcom_helper__dbfactory::new_collector()}.
17
 *
18
 * Normally you should never have to create an instance of this type directly,
19
 * instead use the new_collector() method available in the MidCOM DBA API or the
20
 * midcom_helper__dbfactory::new_collector() method which is still available.
21
 *
22
 * If you have to do create the instance manually however, do not forget to call the
23
 * {@link initialize()} function after construction, or the creation callbacks will fail.
24
 *
25
 * @package midcom
26
 */
27
class midcom_core_collector extends midcom_core_query
28
{
29
    /**
30
     * The applied ordering instructions (for reuse in get_objects)
31
     *
32
     * @var array
33
     */
34
    private $orders = [];
35
36
37
    /**
38
     * The initialization routine
39
     */
40 148
    public function __construct(string $classname, ?string $domain = null, $value = null)
41
    {
42 148
        $mgdschemaclass = $this->_convert_class($classname);
43
44 148
        $this->_query = new midgard_collector($mgdschemaclass, $domain, $value);
45
46
        // MidCOM's collector always uses the GUID as the key for ACL purposes
47 148
        $this->_query->set_key_property('guid');
48 148
    }
49
50
    /**
51
     * @inheritdoc
52
     */
53 63
    public function add_order(string $field, string $direction = 'ASC') : bool
54
    {
55 63
        if ($stat = parent::add_order($field, $direction)) {
56 63
            $this->orders[] = [
57 63
                'field' => $field,
58 63
                'direction' => $direction
59
            ];
60
        }
61
62 63
        return $stat;
63
    }
64
65
    /**
66
     * Execute the Querybuilder and call the appropriate callbacks from the associated
67
     * class. This way, class authors have full control over what is actually returned to the application.
68
     *
69
     * The calling sequence of all event handlers of the associated class is like this:
70
     *
71
     * 1. boolean _on_execute() is called before the actual query execution. Return false to
72
     *    abort the operation.
73
     *
74
     * @return boolean True if the query was executed, false otherwise (e.g. if it had been executed already)
75
     * @see midgard_collector::execute()
76
     */
77 142
    public function execute()
78
    {
79 142
        if ($this->prepare_execute()) {
80 142
            $this->_add_visibility_checks();
81 142
            return $this->_query->execute();
82
        }
83
        return false;
84
    }
85
86
    /**
87
     * Runs a query where limit and offset is taken into account <i>prior</i> to
88
     * execution in the core.
89
     *
90
     * This is useful in cases where you can safely assume read privileges on all
91
     * objects, and where you would otherwise have to deal with huge resultsets.
92
     *
93
     * Be aware that this might lead to empty resultsets "in the middle" of the
94
     * actual full resultset when read privileges are missing.
95
     *
96
     * @see list_keys()
97
     */
98
    public function list_keys_unchecked() : array
99
    {
100
        $this->_reset();
101
102
        // Add the limit / offsets
103
        if ($this->_limit) {
104
            $this->_query->set_limit($this->_limit);
105
        }
106
        $this->_query->set_offset($this->_offset);
107
108
        $newresult = $this->_list_keys_and_check_privileges(false);
109
110
        $this->_real_class::_on_process_collector_result($newresult);
111
112
        $this->count = count($newresult);
113
114
        return $newresult;
115
    }
116
117 142
    private function _list_keys_and_check_privileges(bool $apply_offset_limit = true) : array
118
    {
119 142
        $this->execute();
120 142
        $result = $this->_query->list_keys();
0 ignored issues
show
Bug introduced by
The method list_keys() does not exist on midgard\portable\query. It seems like you code against a sub-type of midgard\portable\query such as midgard_collector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

120
        /** @scrutinizer ignore-call */ 
121
        $result = $this->_query->list_keys();
Loading history...
121 142
        $newresult = [];
122 142
        $counter = 0;
123
124 142
        foreach ($result as $object_guid => $empty_copy) {
125 63
            if (!$this->is_readable($object_guid)) {
126
                debug_add("Failed to load result, read privilege on {$object_guid} not granted for the current user.", MIDCOM_LOG_INFO);
127
                continue;
128
            }
129
130 63
            if ($apply_offset_limit) {
131 63
                $counter++;
132 63
                if ($counter <= $this->_offset) {
133
                    continue;
134
                }
135 63
                if (   $this->_limit
136 63
                    && $counter > ($this->_offset + $this->_limit)) {
137 3
                    break;
138
                }
139
            }
140
141
            // Register the GUID as loaded in this request
142 63
            midcom::get()->cache->content->register($object_guid);
143
144 63
            $newresult[$object_guid] = [];
145
        }
146 142
        return $newresult;
147
    }
148
149
    /**
150
     * Convenience function to get all values of a specific column, indexed by GUID
151
     */
152 61
    public function get_values(string $field) : array
153
    {
154 61
        $this->add_value_property($field);
155 61
        $this->execute();
156 61
        $results = $this->list_keys();
157 61
        foreach ($results as $guid => &$value) {
158 19
            $value = $this->get_subkey($guid, $field);
159
        }
160 61
        return $results;
161
    }
162
163
    /**
164
     * Convenience function to get all values of a number of columns
165
     * They are indexed by GUID unless you specify something else
166
     */
167 75
    public function get_rows(array $fields, string $indexed_by = 'guid') : array
168
    {
169 75
        array_map([$this, 'add_value_property'], $fields);
170
171 75
        if ($indexed_by !== 'guid') {
172 1
            $this->add_value_property($indexed_by);
173
        }
174
175 75
        $this->execute();
176 75
        $results = [];
177 75
        $keys = $this->list_keys();
178 75
        foreach ($keys as $guid => $values) {
179 37
            $values = $this->get($guid);
180 37
            $index = $guid;
181 37
            if ($indexed_by !== 'guid') {
182 1
                $index = $values[$indexed_by];
183
            }
184 37
            $results[$index] = $values;
185
        }
186 75
        return $results;
187
    }
188
189
    /**
190
     * implements midgard_collector::list_keys with ACL checking
191
     */
192 142
    public function list_keys() : array
193
    {
194 142
        $result = $this->_list_keys_and_check_privileges();
195
196 142
        $this->_real_class::_on_process_collector_result($result);
197
198 142
        $this->count = count($result);
199
200 142
        return $result;
201
    }
202
203 20
    public function get_subkey(string $key, string $property)
204
    {
205 20
        if (!$this->is_readable($key)) {
206
            midcom_connection::set_error(MGD_ERR_ACCESS_DENIED);
207
            return false;
208
        }
209 20
        return $this->_query->get_subkey($key, $property);
0 ignored issues
show
Bug introduced by
The method get_subkey() does not exist on midgard\portable\query. It seems like you code against a sub-type of midgard\portable\query such as midgard_collector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

209
        return $this->_query->/** @scrutinizer ignore-call */ get_subkey($key, $property);
Loading history...
210
    }
211
212 45
    public function get(string $key)
213
    {
214 45
        if (!$this->is_readable($key)) {
215
            midcom_connection::set_error(MGD_ERR_ACCESS_DENIED);
216
            return false;
217
        }
218 45
        return $this->_query->get($key);
0 ignored issues
show
Bug introduced by
The method get() does not exist on midgard\portable\query. It seems like you code against a sub-type of midgard\portable\query such as midgard_collector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

218
        return $this->_query->/** @scrutinizer ignore-call */ get($key);
Loading history...
219
    }
220
221 125
    public function add_value_property(string $property) : bool
222
    {
223 125
        if (!$this->_query->add_value_property($property)) {
0 ignored issues
show
Bug introduced by
The method add_value_property() does not exist on midgard\portable\query. It seems like you code against a sub-type of midgard\portable\query such as midgard_collector. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

223
        if (!$this->_query->/** @scrutinizer ignore-call */ add_value_property($property)) {
Loading history...
224
            debug_add("Failed to execute add_value_property '{$property}' for {$this->_real_class}.", MIDCOM_LOG_ERROR);
225
226
            return false;
227
        }
228
229 125
        return true;
230
    }
231
232
    /**
233
     * Returns the number of elements matching the current query.
234
     *
235
     * Due to ACL checking we must first execute the full query
236
     */
237 40
    public function count() : int
238
    {
239 40
        if ($this->count == -1) {
240 40
            $this->execute();
241 40
            $this->list_keys();
242
        }
243 40
        return $this->count;
244
    }
245
246
    /**
247
     * This is a mapping to the real count function of the Midgard Collector. It is mainly
248
     * intended when speed is important over accuracy, as it bypasses access control to get a
249
     * fast impression of how many objects are available in a given query. It should always
250
     * be kept in mind that this is a preliminary number, not a final one.
251
     *
252
     * Use this function with care. The information you obtain in general is negligible, but a creative
253
     * mind might nevertheless be able to take advantage of it.
254
     */
255
    public function count_unchecked() : int
256
    {
257
        if ($this->_limit) {
258
            $this->_query->set_limit($this->_limit);
259
        }
260
        if ($this->_offset) {
261
            $this->_query->set_offset($this->_offset);
262
        }
263
        return $this->_query->count();
264
    }
265
266 7
    public function get_objects() : array
267
    {
268 7
        $this->execute();
269 7
        $guids = $this->list_keys();
270
271 7
        if (empty($guids)) {
272 4
            return [];
273
        }
274
275 3
        $qb = new midcom_core_querybuilder($this->_real_class);
276 3
        $qb->hide_invisible = $this->hide_invisible;
277 3
        $qb->add_constraint('guid', 'IN', array_keys($guids));
278 3
        foreach ($this->orders as $order) {
279 2
            $qb->add_order($order['field'], $order['direction']);
280
        }
281
282 3
        return $qb->execute();
283
    }
284
}
285