Passed
Push — master ( 9cfb91...8e2b60 )
by Andreas
09:24
created

midcom_services_indexer::getSubscribedEvents()   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.services
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
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
10
use midcom\events\dbaevent;
11
12
/**
13
 * This class is the main access point into the MidCOM Indexer subsystem.
14
 *
15
 * It allows you to maintain and query the document index.
16
 *
17
 * Do not instantiate this class directly. Instead use the get_service
18
 * method on midcom_application using the service name 'indexer' to obtain
19
 * a running instance.
20
 *
21
 * @see midcom_services_indexer_document
22
 * @see midcom_services_indexer_backend
23
 * @see midcom_services_indexer_filter
24
 *
25
 * @todo Write code examples
26
 * @todo More elaborate class introduction.
27
 * @package midcom.services
28
 */
29
class midcom_services_indexer implements EventSubscriberInterface
30
{
31
    private ?midcom_services_indexer_backend $_backend;
32
33
    private bool $_disabled;
34
35
    /**
36
     * Initialization
37
     */
38 1
    public function __construct(midcom_services_indexer_backend $backend = null)
39
    {
40 1
        $this->_backend = $backend;
41 1
        $this->_disabled = $this->_backend === null;
42
    }
43
44
    public static function getSubscribedEvents()
45
    {
46
        return [dbaevent::DELETE => ['handle_delete']];
47
    }
48
49
    public function handle_delete(dbaevent $event)
50
    {
51
        $this->delete($event->get_object()->guid);
0 ignored issues
show
Bug introduced by
$event->get_object()->guid of type string is incompatible with the type array expected by parameter $RIs of midcom_services_indexer::delete(). ( Ignorable by Annotation )

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

51
        $this->delete(/** @scrutinizer ignore-type */ $event->get_object()->guid);
Loading history...
52
    }
53
54
    /**
55
     * Simple helper, returns true if the indexer service is online, false if it is disabled.
56
     */
57 9
    public function enabled() : bool
58
    {
59 9
        return !$this->_disabled;
60
    }
61
62
    /**
63
     * Adds a document to the index.
64
     *
65
     * A finished document object must be passed to this object. If the index
66
     * already contains a record with the same Resource Identifier, the record
67
     * is replaced.
68
     *
69
     * Support of batch-indexing using an Array of documents instead of a single
70
     * document is possible (and strongly advised for performance reasons).
71
     *
72
     * @param mixed $documents One or more documents to be indexed, so this is either a
73
     *           midcom_services_indexer_document or an Array of these objects.
74
     * @return boolean Indicating success.
75
     */
76 6
    public function index($documents) : bool
77
    {
78 6
        if ($this->_disabled) {
79 6
            return true;
80
        }
81
82
        $batch = is_array($documents);
83
        if (!$batch) {
84
            $documents = [$documents];
85
        }
86
        if (empty($documents)) {
87
            // Nothing to do.
88
            return true;
89
        }
90
91
        foreach ($documents as $value) {
92
            $value->members_to_fields();
93
        }
94
95
        try {
96
            $this->_backend->index($documents);
0 ignored issues
show
Bug introduced by
The method index() does not exist on null. ( Ignorable by Annotation )

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

96
            $this->_backend->/** @scrutinizer ignore-call */ 
97
                             index($documents);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
97
            return true;
98
        } catch (Exception $e) {
99
            if ($batch) {
100
                throw $e;
101
            }
102
            debug_add("Indexing error: " . $e->getMessage(), MIDCOM_LOG_ERROR);
103
            return false;
104
        }
105
    }
106
107
    /**
108
     * Removes the document(s) with the given resource identifier(s) from the index.
109
     * Using GUIDs instead of RIs will delete all language versions
110
     *
111
     * @param array $RIs The resource identifier(s) of the document(s) that should be deleted.
112
     * @return boolean Indicating success.
113
     */
114
    public function delete($RIs) : bool
115
    {
116
        if ($this->_disabled) {
117
            return true;
118
        }
119
        $RIs = (array) $RIs;
120
        if (empty($RIs)) {
121
            // Nothing to do.
122
            return true;
123
        }
124
        try {
125
            $this->_backend->delete($RIs);
126
            return true;
127
        } catch (Exception $e) {
128
            debug_add("Deleting error: " . $e->getMessage(), MIDCOM_LOG_ERROR);
129
            return false;
130
        }
131
    }
132
133
    /**
134
     * Clear the index completely.
135
     *
136
     * This will drop the current index.
137
     */
138
    public function delete_all(string $constraint = '') : bool
139
    {
140
        if ($this->_disabled) {
141
            return true;
142
        }
143
144
        try {
145
            $this->_backend->delete_all($constraint);
146
            return true;
147
        } catch (Exception $e) {
148
            debug_add("Deleting error: " . $e->getMessage(), MIDCOM_LOG_ERROR);
149
            return false;
150
        }
151
    }
152
153
    /**
154
     * Query the index and, if set, restrict the query by a given filter.
155
     *
156
     * The filter argument is optional and may be a subclass of indexer_filter.
157
     * The backend determines what filters are supported and how they are
158
     * treated.
159
     *
160
     * The query syntax is also dependent on the backend. Refer to its documentation
161
     * how queries should be built.
162
     *
163
     * @param string $query The query, which must suit the backends query syntax. It is assumed to be in the site charset.
164
     * @param array $options Options that are passed straight to the backend
165
     * @return midcom_services_indexer_document[] An array of documents matching the query
166
     * @todo Refactor into multiple methods
167
     */
168 1
    public function query(string $query, midcom_services_indexer_filter $filter = null, array $options = []) : array
169
    {
170 1
        $result = [];
171 1
        if ($this->_disabled) {
172 1
            return $result;
173
        }
174
175
        // Do charset translations
176
        $query = midcom::get()->i18n->convert_to_utf8($query);
177
178
        try {
179
            $result_raw = $this->_backend->query($query, $filter, $options);
180
        } catch (Exception $e) {
181
            debug_add("Query error: " . $e->getMessage(), MIDCOM_LOG_ERROR);
182
            return $result;
183
        }
184
185
        foreach ($result_raw as $document) {
186
            $document->fields_to_members();
187
            /**
188
             * FIXME: Rethink program flow and especially take into account that not all documents are
189
             * created by midcom or even served by midgard
190
             */
191
192
            // midgard:read verification, we simply try to create an object instance
193
            // In the case, we distinguish between MidCOM documents, where we can check
194
            // the RI identified object directly, and pure documents, where we use the
195
            // topic instead.
196
197
            // Try to check topic only if the guid is actually set
198
            if (!empty($document->topic_guid)) {
199
                try {
200
                    midcom_db_topic::get_cached($document->topic_guid);
201
                } catch (midcom_error $e) {
202
                    // Skip document, the object is hidden.
203
                    debug_add("Skipping the generic document {$document->title}, its topic seems to be invisible, we cannot proceed.");
204
                    continue;
205
                }
206
            }
207
208
            // this checks acls!
209
            if ($document->is_a('midcom')) {
210
                // Try to retrieve object:
211
                // Strip language code from end of RI if it looks like "<GUID>_<LANG>"
212
                try {
213
                    midcom::get()->dbfactory->get_object_by_guid(preg_replace('/^([0-9a-f]{32,80})_[a-z]{2}$/', '\\1', $document->RI));
214
                } catch (midcom_error $e) {
215
                    // Skip document, the object is hidden, deleted or otherwise unavailable.
216
                    //@todo Maybe nonexistent objects should be removed from index?
217
                    continue;
218
                }
219
            }
220
            $result[] = $document;
221
        }
222
        return $result;
223
    }
224
225
    /**
226
     * Try to instantiate the most specific document class for the object given in the parameter.
227
     *
228
     * This factory method will work even if the indexer is disabled. You can check this
229
     * with the enabled() method of this class.
230
     *
231
     * @todo Move to a full factory pattern here to save document php file parsings where possible.
232
     *     This means that all document creations will in the future be handled by this method.
233
     */
234 2
    public function new_document(object $object) : midcom_services_indexer_document
235
    {
236
        // Scan for datamanager instances.
237 2
        if (is_a($object, 'midcom\datamanager\datamanager')) {
238 2
            return new midcom\datamanager\indexer\document($object);
239
        }
240
        if (is_a($object, 'midcom_helper_datamanager2_datamanager')) {
241
            return new midcom_helper_datamanager2_indexer_document($object);
0 ignored issues
show
Bug introduced by
The type midcom_helper_datamanager2_indexer_document was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
242
        }
243
244
        if ($object instanceof midcom_core_dbaobject) {
245
            return new midcom_services_indexer_document_midcom($object);
246
        }
247
        throw new midcom_error('Unsupported object type');
248
    }
249
}
250