Completed
Push — master ( 3ffdee...a23ce2 )
by rugk
02:31
created

ThreemaGateway_Model_Messages::getAllMessageData()   B

Complexity

Conditions 8
Paths 3

Size

Total Lines 58
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 58
rs 7.1977
c 0
b 0
f 0
cc 8
eloc 24
nc 3
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Model for messages stored in database.
4
 *
5
 * @package ThreemaGateway
6
 * @author rugk
7
 * @copyright Copyright (c) 2015-2016 rugk
8
 * @license MIT
9
 */
10
11
class ThreemaGateway_Model_Messages extends XenForo_Model
12
{
13
    /**
14
     * @var string database table (prefix) for messages
15
     */
16
    const DB_TABLE_MESSAGES = 'xf_threemagw_messages';
17
18
    /**
19
     * @var string database table for files
20
     */
21
    const DB_TABLE_FILES = 'xf_threemagw_files';
22
23
    /**
24
     * @var string database table for acknowledged messages/delivery receipts
25
     */
26
    const DB_TABLE_DELIVERY_RECEIPT = 'xf_threemagw_ackmsgs';
27
28
    /**
29
     * @var int constant for type code
30
     */
31
    const TYPE_DELIVERY_MESSAGE = 0x80;
32
33
    /**
34
     * @var int constant for type code
35
     */
36
    const TYPE_FILE_MESSAGE = 0x17;
37
38
    /**
39
     * @var int constant for type code
40
     */
41
    const TYPE_IMAGE_MESSAGE = 0x02;
42
43
    /**
44
     * @var int constant for type code
45
     */
46
    const TYPE_TEXT_MESSAGE = 0x01;
47
48
    /**
49
     * @var array constant for type code
50
     */
51
    const ORDER_CHOICE = [
52
        'id' => 'message_id',
53
        'date_send' => 'date_send',
54
        'date_received' => 'date_received',
55
        'delivery_state' => 'message.receipt_type',
56
    ];
57
58
    /**
59
     * @var array data used when querying
60
     */
61
    protected $fetchOptions = [
62
        'where' => [],
63
        'params' => []
64
    ];
65
66
    /**
67
     * Execute this before any query.
68
     *
69
     * Sets internal values necessary for a correct connection to the database.
70
     */
71
    public function preQuery()
72
    {
73
        // set correct character encoding
74
        $this->_getDb()->query('SET NAMES utf8mb4');
75
    }
76
77
    /**
78
     * Inject or modify a fetch option manually.
79
     *
80
     * Sets internal values necessary for a correct connection to the database.
81
     * This should best be avoided, when it is not really necessary to change
82
     * the value directly.
83
     * It can e.g. be used to reset data. When you e.g. want to reset the where
84
     * option call it this way: injectFetchOption('where', []).
85
     *
86
     * @param string $option The option name to inject
87
     * @param mixed  $value  The value of the option to set.
88
     * @param mixed  $append If set to true, the value is not overriden, but
89
     *                       just appended as an array. (default: false)
90
     */
91
    public function injectFetchOption($option, $value, $append = false)
92
    {
93
        if ($append) {
94
            $this->fetchOptions[$option][] = $value;
95
        } else {
96
            $this->fetchOptions[$option] = $value;
97
        }
98
    }
99
100
    /**
101
     * Rests all fetch options. This is useful to prevent incorrect or
102
     * unexpected results when using one model for multiple queries (not
103
     * recommend) or for e.g. resetting the options before calling
104
     * {@link fetchAll()};.
105
     */
106
    public function resetFetchOptions()
107
    {
108
        $this->fetchOptions = [];
109
        // set empty data, which is required to prevent failing
110
        $this->fetchOptions['where']  = [];
111
        $this->fetchOptions['params'] = [];
112
    }
113
114
    /**
115
     * Sets the message ID(s) for the query.
116
     *
117
     * @param string|array $messageIds  one (string) or more (array) message IDs
118
     * @param string       $tablePrefix The table prefix (optional)
119
     */
120
    public function setMessageId($messageIds, $tablePrefix = null)
121
    {
122
        return $this->appendMixedCondition(
123
            ($tablePrefix ? $tablePrefix . '.' : '') . 'message_id',
124
            $messageIds
125
        );
126
    }
127
128
    /**
129
     * Sets the sender Threema ID(s) for querying it/them.
130
     *
131
     * @param string $threemaIds one (string) or more (array) Threema IDs
132
     */
133
    public function setSenderId($threemaIds)
134
    {
135
        return $this->appendMixedCondition(
136
            'metamessage.sender_threema_id',
137
            $threemaIds
138
        );
139
    }
140
141
    /**
142
     * Sets the type code(s) for querying only one (or a few) type.
143
     *
144
     * Please use the TYPE_* constants for specifying the type code(s).
145
     * You should avoid using this and rather use {@link getMessageDataByType()}
146
     * directly if you know the type code.
147
     * If you want to limit the types you want to query this method would be a
148
     * good way for you to use.
149
     *
150
     * @param string $typeCode one (string) or more (array) mtype codes
0 ignored issues
show
Documentation introduced by
There is no parameter named $typeCode. Did you maybe mean $typeCodes?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
151
     */
152
    public function setTypeCode($typeCodes)
153
    {
154
        return $this->appendMixedCondition(
155
            'metamessage.message_type_code',
156
            $typeCodes
157
        );
158
    }
159
160
    /**
161
     * Sets a string to look for when querying text messages.
162
     *
163
     * The string is processed by MySQL via the `LIKE` command and may
164
     * therefore contain some wildcards: % for none or any character and
165
     * _ for exactly one character.
166
     * Attention: This is only possible when using the text message type!
167
     * Otherwise your query will fail.
168
     *
169
     * @param string $keyword a keyword to look for
170
     */
171
    public function setKeyword($keyword)
172
    {
173
        $this->fetchOptions['where'][]  = 'message.text LIKE ?';
174
        $this->fetchOptions['params'][] = $keyword;
175
    }
176
177
    /**
178
     * Sets the a time limit for what messages should be queried.
179
     *
180
     * @param int|null $date_min  oldest date of messages (optional)
181
     * @param int|null $date_max  latest date of messages (optional)
182
     * @param string   $attribute Set the atttribute to apply this to.
183
     */
184
    public function setTimeLimit($date_min = null, $date_max = null, $attribute = 'metamessage.date_send')
185
    {
186
        if ($date_min) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $date_min of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
187
            $this->fetchOptions['where'][]  = $attribute . ' >= ?';
188
            $this->fetchOptions['params'][] = $date_min;
189
        }
190
        if ($date_max) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $date_max of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
191
            $this->fetchOptions['where'][]  = $attribute . ' <= ?';
192
            $this->fetchOptions['params'][] = $date_max;
193
        }
194
    }
195
196
    /**
197
     * Limit the result to a number of datasets.
198
     *
199
     * @param int $limit    oldest date of messages
200
     * @param int $date_max latest date of messages (optional)
0 ignored issues
show
Bug introduced by
There is no parameter named $date_max. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
201
     */
202
    public function setResultLimit($limit)
203
    {
204
        $this->fetchOptions['limit'] = $limit;
205
    }
206
207
    /**
208
     * Sets an order for the query.
209
     *
210
     * This function overwrites previous values if they were set as ordering by
211
     * multiple columns is not possible.
212
     *
213
     * @param int    $column    the column to order by (see {@link OrderChoice} for valid values)
214
     * @param string $direction asc or desc
215
     */
216
    public function setOrder($column, $direction = 'asc')
217
    {
218
        $this->fetchOptions['order']     = $column;
219
        $this->fetchOptions['direction'] = $direction;
220
    }
221
222
    /**
223
     * Queries all available data from a list of message IDs.
224
     *
225
     * Note that this requires one to have the meta data of the messages already
226
     * and therefore you have to run {@link getMessageMetaData()} before and
227
     * submit it as the first parameter.
228
     * This method also resets the conditions of the where clause
229
     * ($fetchOptions['where']) and the params ($fetchOptions['params']) based
230
     * on the results included in the meta data. Other fetch options however
231
     * remain and are still applied, so if you want to avoid this, use
232
     * {@link resetFetchOptions()}.
233
     * Note that the ordering values of different message types will not work as
234
     * this function internally needs to handle each message type differently.
235
     *
236
     * @param  array[string]     $metaData           The message meta data from
0 ignored issues
show
Documentation introduced by
The doc-type array[string] could not be parsed: Expected "]" at position 2, but found "string". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
237
     *                                               {@link getMessageMetaData()}
238
     *                                               (without grouping)
239
     * @param  bool              $groupByMessageType Set to true to group the
240
     *                                               return value via message
241
     *                                               types. (default: false)
242
     * @throws XenForo_Exception
243
     * @return null|array
244
     */
245
    public function getAllMessageData(array $metaData, $groupByMessageType = false)
246
    {
247
        // get grouped messages by type
248
        $messageTypes = $this->groupArray($metaData, 'message_type_code');
249
        // we always need to do this (regardless of message_type_code) as each
250
        // message type needs to be handled individually
251
252
        // query message types individually
253
        $output = null;
254
        foreach ($messageTypes as $messageType => $messages) {
255
            // get messages of current data type in groups
256
            $groupedMessages = $this->groupArray($messages, 'message_id', true);
257
258
            // overwrite conditions with message IDs we already know
259
            $this->fetchOptions['params'] = [];
260
            $this->fetchOptions['where']  = [];
261
            $this->setMessageId($this->getMessageIdsFromResult($messages), 'message');
262
263
            // query data
264
            $groupedResult = $this->getMessageDataByType($messageType, false);
265
            // skip processing if there are no results (most likely all
266
            // messages of this type have been deleted)
267
            if (!is_array($groupedResult)) {
268
                continue;
269
            }
270
271
            // go through each message to merge result with meta data
272
            foreach ($groupedMessages as $msgId => $msgMetaData) {
273
                // ignore non-exisiting key (might be deleted messages)
274
                if (!array_key_exists($msgId, $groupedResult)) {
275
                    continue;
276
                }
277
278
                // merge arrays
279
                $mergedArrays = $msgMetaData + $groupedResult[$msgId];
280
281
                // remove unnecessary message_id (the ID is already the key)
282
                if (array_key_exists('message_id', $mergedArrays)) {
283
                    unset($mergedArrays['message_id']);
284
                }
285
286
                // save as output
287
                if ($groupByMessageType) {
288
                    // remove unnecessary message_type_code (as it is already
289
                    // grouped by it)
290
                    if (array_key_exists('message_type_code', $mergedArrays)) {
291
                        unset($mergedArrays['message_type_code']);
292
                    }
293
294
                    $output[$messageType][$msgId] = $mergedArrays;
295
                } else {
296
                    $output[$msgId] = $mergedArrays;
297
                }
298
            }
299
        }
300
301
        return $output;
302
    }
303
304
    /**
305
     * Queries all available data for a message type.
306
     *
307
     * The return value should be an array in the same format as the one
308
     * returned by {@link getAllMessageData()} when $groupByMessageType is set
309
     * to false. Of course, however, only one message type is returned here.
310
     *
311
     * @param  int               $messageType     The message type the messages belong to
312
     * @param  bool              $includeMetaData Set to true to also include the main
313
     *                                            message table in your query. If you do so you
314
     *                                            will also get the meta data of the message.
315
     *                                            (default: true)
316
     * @throws XenForo_Exception
317
     * @return null|array
318
     */
319
    public function getMessageDataByType($messageType, $includeMetaData = true)
320
    {
321
        /** @var array $output */
322
        $output = [];
323
        /** @var Zend_Db_Table_Select $select */
324
        $select;
0 ignored issues
show
Bug introduced by
The variable $select seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
325
        /** @var string $resultIndex index to use for additional data from query */
326
        $resultIndex = '';
0 ignored issues
show
Unused Code introduced by
$resultIndex is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
327
328
        // prepare query
329
        /** @var array $limitOptions */
330
        $limitOptions = $this->prepareLimitFetchOptions($this->fetchOptions);
331
332
        // built query
333
        switch ($messageType) {
334
            case self::TYPE_DELIVERY_MESSAGE:
335
                $select = $this->_getDb()->select()
336
                    ->from(['message' => self::DB_TABLE_MESSAGES . '_delivery_receipt'])
337
                    ->joinInner(
338
                        ['ack_messages' => self::DB_TABLE_DELIVERY_RECEIPT],
339
                        'message.message_id = ack_messages.message_id'
340
                    );
341
342
                $resultIndex = 'ackmsgs';
343
                break;
344
345 View Code Duplication
            case self::TYPE_FILE_MESSAGE:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
346
                $select = $this->_getDb()->select()
347
                    ->from(['message' => self::DB_TABLE_MESSAGES . '_file'])
348
                    ->joinInner(
349
                        ['filelist' => self::DB_TABLE_FILES],
350
                        'filelist.message_id = message.message_id'
351
                    );
352
353
                $resultIndex = 'files';
354
                break;
355
356 View Code Duplication
            case self::TYPE_IMAGE_MESSAGE:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
357
                $select = $this->_getDb()->select()
358
                    ->from(['message' => self::DB_TABLE_MESSAGES . '_image'])
359
                    ->joinInner(
360
                        ['filelist' => self::DB_TABLE_FILES],
361
                        'filelist.message_id = message.message_id'
362
                    );
363
364
                $resultIndex = 'files';
365
                break;
366
367
            case self::TYPE_TEXT_MESSAGE:
368
            $select = $this->_getDb()->select()
369
                ->from(['message' => self::DB_TABLE_MESSAGES . '_text']);
370
371
                // although this is not strictly necessary, to ease the
372
                // processing the data later, we also index this
373
                $resultIndex = 'text';
374
                break;
375
376
            default:
377
                throw new XenForo_Exception(new XenForo_Phrase('threemagw_unknown_message_type'));
378
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
379
        }
380
381
        // add table if necessary
382
        if ($includeMetaData) {
383
            $select->joinInner(
384
                ['metamessage' => self::DB_TABLE_MESSAGES],
385
                'message.message_id = metamessage.message_id'
386
            );
387
        }
388
389
        // general options for query
390
        $select
391
            ->where($this->getConditionsForClause($this->fetchOptions['where']))
392
            ->order($this->getOrderByClause(self::ORDER_CHOICE, $this->fetchOptions));
393
394
        // execute query
395
        /** @var array|null $result database query result */
396
        $result = $this->_getDb()->fetchAll(
397
            $this->limitQueryResults(
398
                $select,
399
                $limitOptions['limit'], $limitOptions['offset']),
400
        $this->fetchOptions['params']);
401
402
        // throw error if data is missing
403
        if (!is_array($result)) {
404
            throw new XenForo_Exception(new XenForo_Phrase('threemagw_missing_database_data'));
405
        }
406
        // if there is no result, just return null
407
        if (empty($result)) {
408
            return null;
409
        }
410
411
        // group array by message ID
412
        $result = $this->groupArray($result, 'message_id');
413
414
        // attributes to remove/push
415
        $removeAttributes = [
416
            'message_id',
417
            'file_name',
418
            'mime_type',
419
            'file_size'
420
        ];
421
        if ($includeMetaData) {
422
            $removeAttributes = array_merge($removeAttributes, [
423
                'message_type_code',
424
                'sender_threema_id',
425
                'date_send',
426
                'date_received'
427
            ]);
428
        }
429
430
        // push general attributes one array up
431
        if (!$resultIndex) {
432
            throw new XenForo_Exception(new XenForo_Phrase('threemagw_unknown_message_type'));
433
            break;
434
        }
435
436
        // go through each message
437
        foreach ($result as $msgId => $resultForId) {
438
            $output[$msgId] = [];
439
            $output[$msgId] = $this->pushArrayKeys($output[$msgId],
440
                                    $resultForId,
441
                                    $removeAttributes);
442
            $output[$msgId][$resultIndex] = $resultForId;
443
444
            // remove unnecessary message_id (the ID is already the key)
445
            if (array_key_exists('message_id', $output[$msgId])) {
446
                unset($output[$msgId]['message_id']);
447
            }
448
        }
449
450
        if (!$output) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $output of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
451
            return null;
452
        }
453
454
        return $output;
455
    }
456
457
    /**
458
     * Returns only the meta data of one or more messages not depending on the
459
     * type of the message.
460
     *
461
     * @param  bool       $groupById     When true groups the data by the message ID (default: false)
462
     * @param  bool       $ignoreInvalid When true removes data sets where the message content may be deleted (default: true)
463
     * @return null|array
464
     */
465
    public function getMessageMetaData($groupById = false, $ignoreInvalid = true)
466
    {
467
        /** @var array $limitOptions */
468
        $limitOptions = $this->prepareLimitFetchOptions($this->fetchOptions);
469
470
        /** @var array $result query result */
471
        $result = $this->_getDb()->fetchAll(
472
            $this->limitQueryResults(
473
                $this->_getDb()->select()
474
                    ->from(['metamessage' => self::DB_TABLE_MESSAGES])
475
                    ->where($this->getConditionsForClause($this->fetchOptions['where']))
476
                    ->order($this->getOrderByClause(self::ORDER_CHOICE, $this->fetchOptions)),
477
            $limitOptions['limit'], $limitOptions['offset']),
478
        $this->fetchOptions['params']);
479
480
        // fail if there is no data
481
        if (!is_array($result) || !$result) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
482
            return null;
483
        }
484
485
        // remove invalid data sets (where message might be deleted)
486
        if ($ignoreInvalid) {
487
            foreach ($result as $i => $msgData) {
488
                if (!array_key_exists('message_type_code', $msgData) ||
489
                    !$msgData['message_type_code']) {
490
                    unset($result[$i]);
491
                }
492
            }
493
        }
494
495
        // group array by message ID if wanted
496
        if ($groupById) {
497
            $result = $this->groupArray($result, 'message_id');
498
        }
499
500
        return $result;
501
    }
502
503
    /**
504
     * Removes entries or meta data fields from the meta data message table.
505
     *
506
     * Note that if the message(s) has/have other data saved in the database
507
     * the full deletion will fail.
508
     * Note: The number of where and params-options must be equal. You can
509
     * submit additional conditions with the first parameter.
510
     * Attention: This ignores the limit/offset clause for simplicity.
511
     *
512
     * @param array $additionalConditions Add additional where conditions if
513
     *                                    neccessary.
514
     * @param array $removeOnlyField      When set only the passed fields are
515
     *                                    updated to "null" rather than deleting
516
     *                                    the whole record.
517
     */
518
    public function removeMetaData(array $additionalConditions = [], array $removeOnlyField = [])
519
    {
520
        if ($removeOnlyField) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $removeOnlyField of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
521
            $this->_getDb()->update(
522
                self::DB_TABLE_MESSAGES,
523
                array_fill_keys($removeOnlyField, null),
524
                array_merge(
525
                    array_combine($this->fetchOptions['where'], $this->fetchOptions['params']),
526
                    $additionalConditions
527
                )
528
            );
529
        } else {
530
            $this->_getDb()->delete(
531
                self::DB_TABLE_MESSAGES,
532
                array_merge(
533
                    array_combine($this->fetchOptions['where'], $this->fetchOptions['params']),
534
                    $additionalConditions
535
                )
536
            );
537
        }
538
    }
539
540
    /**
541
     * Returns all available data from a list of message IDs.
542
     *
543
     * @param  array[string]     $messages The message result
0 ignored issues
show
Documentation introduced by
The doc-type array[string] could not be parsed: Expected "]" at position 2, but found "string". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
544
     * @throws XenForo_Exception
545
     * @return null|array
546
     */
547
    protected function getMessageIdsFromResult(array $messages)
548
    {
549
        // use PHP function if available (>= PHP 5.5.0)
550
        if (function_exists('array_column')) {
551
            return array_column($messages, 'message_id');
552
        }
553
554
        // manually extract message_id from array
555
        $output = [];
556
        foreach ($messages as $message) {
557
            $output[] = $message['message_id'];
558
        }
559
560
        return $output;
561
    }
562
563
    /**
564
     * Removes the specified keys from the second array and pushes them into
565
     * the first base array.
566
     * The subarray must be indexed by integers, where each index contains an
567
     * associative array with the keys to remove.
568
     * It assumes that the 0-index of $subArray is there, including the data,
569
     * which should be pushed to $baseArray.
570
     *
571
     * @param array $baseArray  the main array, where the key/value pairs get to
572
     * @param array $subArray   the array, which keys should be removed
573
     * @param array $removeKeys an array of keys, which should be removed
574
     *
575
     * @throws XenForo_Exception
576
     * @return false|array
577
     */
578
    protected function pushArrayKeys(array &$baseArray, array &$subArray, array $removeKeys)
579
    {
580
        foreach ($removeKeys as $key) {
581
            // skip invalid keys
582
            if (!array_key_exists($key, $subArray[0])) {
583
                continue;
584
            }
585
586
            // move value from subarray to base array
587
            $baseArray[$key] = $subArray[0][$key];
588
589
            // then delete it from sub array
590
            /** @var int $subArrayCount */
591
            $subArrayCount = count($subArray);
592
            for ($i = 0; $i < $subArrayCount; $i++) {
593
                unset($subArray[$i][$key]);
594
            }
595
        }
596
597
        return $baseArray;
598
    }
599
600
    /**
601
     * Groups an array by using the value of a specific index in it.
602
     *
603
     * @param array      $array       the array, which is sued as the base
604
     * @param string|int $indexKey    the value of the key, which should be used
605
     *                                for indexing
606
     * @param bool       $ignoreIndex Set to true to ignore multiple values in
607
     *                                $array. If activated only the last key of
608
     *                                $array will be placed into the group and
609
     *                                it will be the only key. This is only
610
     *                                useful if you know for sure that only one
611
     *                                key is available.
612
     *
613
     * @return array
614
     */
615
    public function groupArray(array $array, $indexKey, $ignoreIndex = false)
616
    {
617
        /** @var array $output */
618
        $output = [];
619
        foreach ($array as $i => $value) {
620
            if ($ignoreIndex) {
621
                $output[$value[$indexKey]] = $value;
622
            } else {
623
                $output[$value[$indexKey]][] = $value;
624
            }
625
        }
626
627
        return $output;
628
    }
629
630
    /**
631
     * Appends a WHERE condition for either a string or an array.
632
     *
633
     * It automatically chooses between a simple `this = ?` or a more complex
634
     * `this IN (?, ?, ...)`.
635
     *
636
     * @param string       $attName  the name of the required attribut
637
     * @param string|array $attValue the value, which should be required
638
     *
639
     * @return array
0 ignored issues
show
Documentation introduced by
Should the return type not be array|null?

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...
640
     */
641
    protected function appendMixedCondition($attName, $attValue)
642
    {
643
        // convert arrays with only one value
644
        if (is_array($attValue) && count($attValue) == 1) {
645
            $attValue = $attValue[0];
646
        }
647
648
        if (!is_array($attValue)) {
649
            $this->fetchOptions['where'][]  = $attName . ' = ?';
650
            $this->fetchOptions['params'][] = $attValue;
651
        } else {
652
            $this->fetchOptions['where'][]  = $attName . ' IN (' . implode(', ', array_fill(0, count($attValue), '?')) . ')';
653
            $this->fetchOptions['params'] += $attValue;
654
        }
655
    }
656
}
657