Passed
Push — master ( 5da13c...489a75 )
by Andreas
23:20
created

midcom_services_indexer_document::_add_field()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 4
dl 0
loc 6
ccs 0
cts 5
cp 0
crap 6
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
/**
10
 * This class encapsulates a single indexer document. It is used for both indexing
11
 * and retrieval.
12
 *
13
 * A document consists of a number of fields, each field has different properties
14
 * when handled by the indexer (exact behavior depends, as always, on the indexer
15
 * backend in use). On retrieval, this field information is lost, all fields being
16
 * of the same type (naturally). The core indexer backend supports these field
17
 * types:
18
 *
19
 * - <i>date</i> is a date-wrapped field suitable for use with the Date Filter.
20
 * - <i>keyword</i> is store and indexed, but not tokenized.
21
 * - <i>unindexed</i> is stored but neither indexed nor tokenized.
22
 * - <i>unstored</i> is not stored, but indexed and tokenized.
23
 * - <i>text</i> is stored, indexed and tokenized.
24
 *
25
 * This class should not be instantiated directly, a new instance of this class
26
 * can be obtained using the midcom_services_indexer class.
27
 *
28
 * A number of predefined fields are available using member fields. These fields
29
 * are all meta-fields. See their individual documentation for details. All fields
30
 * are mandatory unless mentioned otherwise explicitly and, as always, assumed to
31
 * be in the local charset.
32
 *
33
 * Remember, that both date and unstored fields are not available on retrieval.
34
 * For the core fields, all timestamps are stored twice therefore, once as searchable
35
 * field, and once as readable timestamp.
36
 *
37
 * The class will automatically pass all data to the i18n charset conversion functions,
38
 * thus you work using your site's charset like usual. UTF-8 conversion is done
39
 * implicitly.
40
 *
41
 * @package midcom.services
42
 * @see midcom_services_indexer
43
 * @todo The Type field is not yet handled properly.
44
 */
45
class midcom_services_indexer_document
46
{
47
    /**
48
     * An associative array containing all fields of the current document.
49
     *
50
     * Each field is indexed by its name (a string). The value is another
51
     * array containing the fields "name", type" and "content".
52
     *
53
     * @var Array
54
     */
55
    private $_fields = [];
56
57
    /**
58
     * The i18n service, used for charset conversion.
59
     *
60
     * @var midcom_services_i18n
61
     */
62
    protected $_i18n;
63
64
    /**
65
     * This is the score of this document. Only populated on resultset documents,
66
     * of course.
67
     *
68
     * @var double
69
     */
70
    public $score = 0.0;
71
72
    /* ------ START OF DOCUMENT FIELDS --------- */
73
74
    /**
75
     * The Resource Identifier of this document.
76
     *
77
     * Must be UTF-8 on assignment already.
78
     *
79
     * This field is mandatory.
80
     *
81
     * @var string
82
     */
83
    public $RI = '';
84
85
    /**
86
     * Two letter language code of the document content
87
     *
88
     * This field is optional.
89
     *
90
     * @var string
91
     */
92
    public $lang = '';
93
94
    /**
95
     * The GUID of the topic the document is assigned to.
96
     *
97
     * May be empty for non-midgard resources.
98
     *
99
     * This field is mandatory.
100
     *
101
     * @var string GUID
102
     */
103
    public $topic_guid = '';
104
105
    /**
106
     * The name of the component responsible for the document.
107
     *
108
     * May be empty for non-midgard resources.
109
     *
110
     * This field is mandatory.
111
     *
112
     * @var string
113
     */
114
    public $component = '';
115
116
    /**
117
     * The fully qualified URL to the document, this should be a PermaLink.
118
     *
119
     * This field is mandatory.
120
     *
121
     * @var string
122
     */
123
    public $document_url = '';
124
125
    /**
126
     * The time of document creation, this is a UNIX timestamp.
127
     *
128
     * This field is mandatory.
129
     *
130
     * @var int
131
     */
132
    public $created = 0;
133
134
    /**
135
     * The time of the last document modification, this is a UNIX timestamp.
136
     *
137
     * This field is mandatory.
138
     *
139
     * @var int
140
     */
141
    public $edited = 0;
142
143
    /**
144
     * The timestamp of indexing.
145
     *
146
     * This field is added automatically and to be considered read-only.
147
     *
148
     * @var int
149
     */
150
    public $indexed = 0;
151
152
    /**
153
     * The MidgardPerson who created the object.
154
     *
155
     * This is optional.
156
     *
157
     * @var midcom_db_person
158
     */
159
    public $creator;
160
161
    /**
162
     * The MidgardPerson who modified the object the last time.
163
     *
164
     * This is optional.
165
     *
166
     * @var midcom_db_person
167
     */
168
    public $editor;
169
170
    /**
171
     * The title of the document
172
     *
173
     * This is mandatory.
174
     *
175
     * @var string
176
     */
177
    public $title = '';
178
179
    /**
180
     * The content of the document
181
     *
182
     * This is mandatory.
183
     *
184
     * This field is empty on documents retrieved from the index.
185
     *
186
     * @var string
187
     */
188
    public $content = '';
189
190
    /**
191
     * The abstract of the document
192
     *
193
     * This is optional.
194
     *
195
     * @var string
196
     */
197
    public $abstract = '';
198
199
    /**
200
     * The author of the document
201
     *
202
     * This is optional.
203
     *
204
     * @var string
205
     */
206
    public $author = '';
207
208
    /**
209
     * An additional tag indicating the source of the document for use by the
210
     * component doing the indexing.
211
     *
212
     * This value is not indexed and should not be used by anybody except the
213
     * component doing the indexing.
214
     *
215
     * This is optional.
216
     *
217
     * @var string
218
     */
219
    public $source = '';
220
221
    /**
222
     * The full path to the topic that houses the document.
223
     *
224
     * For external resources, this should be either a MidCOM topic, to which this
225
     * resource is associated or some "directory" after which you could filter.
226
     * You may also leave it empty prohibiting it to appear on any topic-specific search.
227
     *
228
     * The value should be fully qualified, as returned by MIDCOM_NAV_FULLURL, including
229
     * a trailing slash, f.x. https://host/path/to/topic/
230
     *
231
     * This is optional.
232
     *
233
     * @var string
234
     */
235
    public $topic_url = '';
236
237
    /**
238
     * The type of the document, set by subclasses and added to the index
239
     * automatically.
240
     *
241
     * The type *must* reflect the original type hierarchy. It is to be set
242
     * using the $this->_set_type call <i>after</i> initializing the base class.
243
     *
244
     * @see is_a()
245
     * @see _set_type()
246
     * @var string
247
     */
248
    public $type = '';
249
250
    /**
251
     * This is have support for #651 without rewriting all components' index methods
252
     *
253
     * If set to false the indexer backend will silently skip this document.
254
     *
255
     * @see http://trac.midgard-project.org/ticket/651
256
     * @var boolean
257
     */
258
    public $actually_index = true;
259
260
    /* ------ END OF DOCUMENT FIELDS --------- */
261
262
    /**
263
     * Initialize the object, nothing fancy here.
264
     */
265 14
    public function __construct()
266
    {
267 14
        $this->_i18n = midcom::get()->i18n;
268 14
    }
269
270
    /**
271
     * Returns the contents of the field name or false on failure.
272
     *
273
     * @param string $name The name of the field.
274
     * @return mixed The content of the field or false on failure.
275
     */
276
    public function get_field($name)
277
    {
278
        if (!array_key_exists($name, $this->_fields)) {
279
            debug_add("Field {$name} not found in the document.", MIDCOM_LOG_INFO);
280
            return false;
281
        }
282
        return $this->_i18n->convert_from_utf8($this->_fields[$name]['content']);
283
    }
284
285
    /**
286
     * Returns the complete internal field record, including type and UTF-8 encoded
287
     * content.
288
     *
289
     * This should normally not be used from the outside, it is geared towards the
290
     * indexer backends, which need the full field information on indexing.
291
     *
292
     * @param string $name The name of the field.
293
     * @return Array The full content record.
294
     */
295
    public function get_field_record($name)
296
    {
297
        if (!array_key_exists($name, $this->_fields)) {
298
            debug_add("Field {$name} not found in the document.", MIDCOM_LOG_INFO);
299
            return false;
300
        }
301
        return $this->_fields[$name];
302
    }
303
304
    /**
305
     * Returns a list of all defined fields.
306
     */
307
    public function list_fields() : array
308
    {
309
        return array_keys($this->_fields);
310
    }
311
312
    /**
313
     * Remove a field from the list. Nonexistent fields are ignored silently.
314
     *
315
     * @param string $name The name of the field.
316
     */
317
    public function remove_field($name)
318
    {
319
        unset($this->_fields[$name]);
320
    }
321
322
    /**
323
     * Add a date field. A timestamp is expected, which is automatically
324
     * converted to a suitable ISO timestamp before storage.
325
     *
326
     * Direct specification of the ISO timestamp is not yet possible due
327
     * to lacking validation outside the timestamp range.
328
     *
329
     * If a field of the same name is already present, it is overwritten
330
     * silently.
331
     *
332
     * @param string $name The field's name.
333
     * @param int $timestamp The timestamp to store.
334
     */
335
    public function add_date($name, $timestamp)
336
    {
337
        // This is always UTF-8 conformant.
338
        $this->_add_field($name, 'date', gmstrftime('%Y-%m-%dT%H:%M:%SZ', $timestamp), true);
339
    }
340
341
    /**
342
     * Create a normal date field and an unindexed _TS-postfixed timestamp field at the same time.
343
     *
344
     * This is useful because the date fields are not in a readable format,
345
     * it can't even be determined that they were a date in the first place.
346
     * so the _TS field is quite useful if you need the original value for the
347
     * timestamp.
348
     *
349
     * @param string $name The field's name, "_TS" is appended for the plain-timestamp field.
350
     * @param int $timestamp The timestamp to store.
351
     */
352
    public function add_date_pair($name, $timestamp)
353
    {
354
        $this->add_date($name, $timestamp);
355
        $this->add_unindexed("{$name}_TS", $timestamp);
356
    }
357
358
    /**
359
     * Add a keyword field.
360
     *
361
     * @param string $name The field's name.
362
     * @param string $content The field's content.
363
     */
364
    public function add_keyword($name, $content)
365
    {
366
        $this->_add_field($name, 'keyword', $content);
367
    }
368
369
    /**
370
     * Add a unindexed field.
371
     *
372
     * @param string $name The field's name.
373
     * @param string $content The field's content.
374
     */
375
    public function add_unindexed($name, $content)
376
    {
377
        $this->_add_field($name, 'unindexed', $content);
378
    }
379
380
    /**
381
     * Add a unstored field.
382
     *
383
     * @param string $name The field's name.
384
     * @param string $content The field's content.
385
     */
386
    public function add_unstored($name, $content)
387
    {
388
        $this->_add_field($name, 'unstored', $this->html2text($content));
389
    }
390
391
    /**
392
     * Add a text field.
393
     *
394
     * @param string $name The field's name.
395
     * @param string $content The field's content.
396
     */
397
    public function add_text($name, $content)
398
    {
399
        $this->_add_field($name, 'text', $this->html2text($content));
400
    }
401
402
    /**
403
     * Add a search result field, this should normally not be done
404
     * manually, the indexer will call this function when creating a
405
     * document out of a search result.
406
     *
407
     * @param string $name The field's name.
408
     * @param string $content The field's content, which is <b>assumed to be UTF-8 already</b>
409
     */
410
    public function add_result($name, $content)
411
    {
412
        $this->_add_field($name, 'result', $content, true);
413
    }
414
415
    /**
416
     * Add a person field.
417
     *
418
     * @param string $name The field's name.
419
     * @param midcom_db_person $person The field's content.
420
     */
421
    private function add_person(string $name, $person)
422
    {
423
        if (!is_object($person)) {
424
            if ($person !== null) {
425
                debug_print_r("Warning, person is not an object:", $person, MIDCOM_LOG_INFO);
426
            }
427
            $this->add_text($name, '');
428
        } else {
429
            $this->add_text($name, $person->guid);
430
        }
431
    }
432
433
    /**
434
     * This will translate all member variables into appropriate
435
     * field records, the backend should call this immediately before
436
     * indexing.
437
     *
438
     * This call will automatically populate indexed with time()
439
     * and author with the name of the creator (if set).
440
     */
441
    public function members_to_fields()
442
    {
443
        // Complete fields
444
        $this->indexed = time();
445
        if (   $this->author == ''
446
            && isset($this->creator->name)) {
447
            $this->author = $this->creator->name;
448
        }
449
450
        // __RI does not need to be populated, this is done by backends.
451
        $this->add_unindexed('__LANG', $this->lang);
452
        $this->add_text('__TOPIC_GUID', $this->topic_guid);
453
        $this->add_text('__COMPONENT', $this->component);
454
        $this->add_unindexed('__DOCUMENT_URL', $this->document_url);
455
        $this->add_text('__TOPIC_URL', $this->topic_url);
456
        $this->add_date_pair('__CREATED', $this->created);
457
        $this->add_date_pair('__EDITED', $this->edited);
458
        $this->add_date_pair('__INDEXED', $this->indexed);
459
        $this->add_text('title', $this->title);
460
        $this->add_unstored('content', $this->content);
461
462
        $this->add_unindexed('__SOURCE', $this->source);
463
        $this->add_person('__CREATOR', $this->creator);
464
        $this->add_person('__EDITOR', $this->editor);
465
466
        $this->add_text('author', $this->author);
467
        $this->add_text('abstract', $this->abstract);
468
        $this->add_text('__TYPE', $this->type);
469
    }
470
471
    /**
472
     * Populate all relevant members with the respective values after
473
     * retrieving a document from the index
474
     */
475
    public function fields_to_members()
476
    {
477
        $this->RI = $this->get_field('__RI');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__RI') can also be of type false. However, the property $RI is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
478
        $this->lang = $this->get_field('__LANG');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__LANG') can also be of type false. However, the property $lang is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
479
        $this->topic_guid = $this->get_field('__TOPIC_GUID');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__TOPIC_GUID') can also be of type false. However, the property $topic_guid is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
480
        $this->component = $this->get_field('__COMPONENT');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__COMPONENT') can also be of type false. However, the property $component is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
481
        $this->document_url = $this->get_field('__DOCUMENT_URL');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__DOCUMENT_URL') can also be of type false. However, the property $document_url is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
482
        $this->topic_url = $this->get_field('__TOPIC_URL');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__TOPIC_URL') can also be of type false. However, the property $topic_url is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
483
        $this->created = $this->get_field('__CREATED_TS');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__CREATED_TS') of type false or string is incompatible with the declared type integer of property $created.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
484
        $this->edited = $this->get_field('__EDITED_TS');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__EDITED_TS') of type false or string is incompatible with the declared type integer of property $edited.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
485
        $this->indexed = $this->get_field('__INDEXED_TS');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__INDEXED_TS') of type false or string is incompatible with the declared type integer of property $indexed.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
486
        $this->title = $this->get_field('title');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('title') can also be of type false. However, the property $title is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
487
488
        $this->source = $this->get_field('__SOURCE');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__SOURCE') can also be of type false. However, the property $source is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
489
        $this->creator = $this->get_field('__CREATOR');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__CREATOR') of type false or string is incompatible with the declared type midcom_db_person of property $creator.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
490
        if ($this->creator != '') {
491
            $this->creator = $this->read_person($this->creator);
0 ignored issues
show
Bug introduced by
It seems like $this->creator can also be of type false; however, parameter $id of midcom_services_indexer_document::read_person() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

491
            $this->creator = $this->read_person(/** @scrutinizer ignore-type */ $this->creator);
Loading history...
492
        }
493
        $this->editor = $this->get_field('__EDITOR');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__EDITOR') of type false or string is incompatible with the declared type midcom_db_person of property $editor.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
494
        if ($this->editor != '') {
495
            $this->editor = $this->read_person($this->editor);
496
        }
497
        $this->author = $this->get_field('author');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('author') can also be of type false. However, the property $author is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
498
        $this->abstract = $this->get_field('abstract');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('abstract') can also be of type false. However, the property $abstract is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
499
        $this->type = $this->get_field('__TYPE');
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->get_field('__TYPE') can also be of type false. However, the property $type is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
500
    }
501
502
    /**
503
     * Internal helper which actually stores a field.
504
     *
505
     * @param string $name The field's name.
506
     * @param string $type The field's type.
507
     * @param string $content The field's content.
508
     * @param boolean $is_utf8 Set this to true explicitly, to override charset conversion and assume $content is UTF-8 already.
509
     */
510
    protected function _add_field($name, $type, $content, $is_utf8 = false)
511
    {
512
        $this->_fields[$name] = [
513
            'name' => $name,
514
            'type' => $type,
515
            'content' => ($is_utf8 ? $content : $this->_i18n->convert_to_utf8($content))
516
        ];
517
    }
518
519
    /**
520
     * Convert HTML to plain text (relatively simple):
521
     *
522
     * Basically, JavaScript blocks and
523
     * HTML Tags are stripped, and all HTML Entities
524
     * are converted to their native equivalents.
525
     *
526
     * Don't replace with an empty string but with a space, so that constructs like
527
     * <li>torben</li><li>nehmer</li> are recognized correctly.
528
     *
529
     * @param string $text The text to convert to text
530
     */
531 14
    public function html2text($text) : string
532
    {
533
        $search = [
534 14
            "'\s*<script[^>]*?>.*?</script>\s*'si", // Strip out javascript
535
            "'\s*<[\/\!]*?[^<>]*?>\s*'si", // Strip out html tags
536
        ];
537
        $replace = [
538 14
            ' ',
539
            ' ',
540
        ];
541 14
        $result = $this->_i18n->html_entity_decode(preg_replace($search, $replace, $text));
542 14
        return trim(preg_replace('/\s+/s', ' ', $result));
543
    }
544
545
    /**
546
     * Checks whether the given document is an instance of given document type.
547
     *
548
     * This is equivalent to the is_a object hierarchy check, except that it
549
     * works with MidCOM documents.
550
     *
551
     * @see $type
552
     * @see _set_type()
553
     * @param string $document_type The base type to search for.
554
     */
555
    public function is_a($document_type) : bool
556
    {
557
        return strpos($this->type, $document_type) === 0;
558
    }
559
560
    /**
561
     * Sets the type of the object, reflecting the inheritance hierarchy.
562
     *
563
     * @see $type
564
     * @see is_a()
565
     * @param string $type The name of this document type
566
     */
567 6
    protected function _set_type($type)
568
    {
569 6
        if (empty($this->type)) {
570 6
            $this->type = $type;
571
        } else {
572
            $this->type .= "_{$type}";
573
        }
574 6
    }
575
576
    /**
577
     * Tries to determine the topic GUID and component using NAPs reverse-lookup capabilities.
578
     *
579
     * If this fails, you have to set the members $topic_guid, $topic_url and
580
     * $component manually.
581
     */
582
    protected function process_topic()
583
    {
584
        $nav = new midcom_helper_nav();
585
        $object = $nav->resolve_guid($this->source, true);
586
        if (!$object) {
587
            debug_add("Failed to resolve the topic, skipping autodetection.");
588
            return;
589
        }
590
        if ($object[MIDCOM_NAV_TYPE] == 'leaf') {
591
            $object = $nav->get_node($object[MIDCOM_NAV_NODEID]);
592
        }
593
        $this->topic_guid = $object[MIDCOM_NAV_GUID];
594
        $this->topic_url = $object[MIDCOM_NAV_FULLURL];
595
        $this->component = $object[MIDCOM_NAV_COMPONENT];
596
    }
597
598
    /**
599
     * Tries to resolve created, revised, author, editor and creator for the document from Midgard object
600
     *
601
     * @param midgard\portable\api\mgdobject $object object to use as source for the info
602
     */
603 6
    public function read_metadata_from_object($object)
604
    {
605
        // Published is set to non-empty value, use it as creation data
606 6
        if (   !empty($object->metadata->published)
607 6
            && !preg_match('/0{1,4}-0{1,2}0{1,2}\s+0{1,2}:0{1,2}:0{1,2}/', $object->metadata->published)) {
608 6
            $this->created = $this->read_unixtime($object->metadata->published);
609
        } elseif (isset($object->metadata->created)) {
610
            $this->created = $this->read_unixtime($object->metadata->created);
611
        }
612
        // Revised
613 6
        if (isset($object->metadata->revised)) {
614 6
            $this->edited = $this->read_unixtime($object->metadata->revised);
615
        }
616
        // Heuristics to determine author
617 6
        if (!empty($object->metadata->authors)) {
618 4
            $this->author = $this->read_authorname($object->metadata->authors);
619 2
        } elseif (!empty($object->metadata->creator)) {
620 2
            $this->author = $this->read_authorname($object->metadata->creator);
621
        }
622
        // Creator
623 6
        if (isset($object->metadata->creator)) {
624 6
            $this->creator = $this->read_person($object->metadata->creator);
625
        }
626
        // Editor
627 6
        if (isset($object->metadata->revisor)) {
628 6
            $this->editor = $this->read_person($object->metadata->revisor);
629
        }
630 6
    }
631
632
    /**
633
     * Heuristics to determine how to convert given timestamp to local unixtime
634
     *
635
     * @param string $stamp ISO or unix datetime
636
     * @return int unixtime
637
     */
638 6
    private function read_unixtime($stamp)
639
    {
640 6
        if (preg_match('/[0-9]{4}-[0-9]{2}-[0-9]{2}/', $stamp)) {
641
            // ISO Datetime
642
            return @strtotime($stamp);
643
        }
644
        // Unix timestamp
645 6
        return (int)$stamp;
646
    }
647
648
    /**
649
     * Get person by given ID, caches results.
650
     *
651
     * @param string $id GUID or ID to get person for
652
     * @return midcom_db_person object
653
     */
654 6
    private function read_person($id)
655
    {
656
        try {
657 6
            return midcom_db_person::get_cached($id);
658
        } catch (midcom_error $e) {
659
            return false;
660
        }
661
    }
662
663
    /**
664
     * Gets person name for given ID (in case it's imploded_wrapped of multiple GUIDs it will use the first)
665
     *
666
     * @param string $id GUID or ID to get person for
667
     */
668 6
    private function read_authorname($id) : string
669
    {
670
        // Check for imploded_wrapped datamanager storage.
671 6
        if (strpos($id, '|') !== false) {
672 4
            $id_arr = array_filter(explode('|', $id));
673
            // Find first non-empty value in the array and use that
674 4
            $id = (!empty($id_arr)) ? array_shift($id_arr) : false;
675
        }
676
677 6
        return midcom::get()->auth->get_user($id)->name ?? '';
678
    }
679
}
680