Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — master (#430)
by Alexander
03:51
created

Solr::search()   B

Complexity

Conditions 6
Paths 24

Size

Total Lines 64
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 44
c 0
b 0
f 0
dl 0
loc 64
rs 8.5937
cc 6
nc 24
nop 0

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
namespace Kitodo\Dlf\Common;
4
5
/**
6
 * (c) Kitodo. Key to digital objects e.V. <[email protected]>
7
 *
8
 * This file is part of the Kitodo and TYPO3 projects.
9
 *
10
 * @license GNU General Public License version 3 or later.
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 */
14
15
use TYPO3\CMS\Core\Database\ConnectionPool;
16
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
17
use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19
/**
20
 * Solr class for the 'dlf' extension
21
 *
22
 * @author Sebastian Meyer <[email protected]>
23
 * @author Henrik Lochmann <[email protected]>
24
 * @package TYPO3
25
 * @subpackage dlf
26
 * @access public
27
 * @property-write integer $cPid This holds the PID for the configuration
28
 * @property integer $limit This holds the max results
29
 * @property-read integer $numberOfHits This holds the number of hits for last search
30
 * @property-write array $params This holds the additional query parameters
31
 * @property-read boolean $ready Is the search instantiated successfully?
32
 * @property-read \Solarium\Client $service This holds the Solr service object
33
 */
34
class Solr
35
{
36
    /**
37
     * This holds the core name
38
     *
39
     * @var string
40
     * @access protected
41
     */
42
    protected $core = '';
43
44
    /**
45
     * This holds the PID for the configuration
46
     *
47
     * @var integer
48
     * @access protected
49
     */
50
    protected $cPid = 0;
51
52
    /**
53
     * The extension key
54
     *
55
     * @var string
56
     * @access public
57
     */
58
    public static $extKey = 'dlf';
59
60
    /**
61
     * This holds the max results
62
     *
63
     * @var integer
64
     * @access protected
65
     */
66
    protected $limit = 50000;
67
68
    /**
69
     * This holds the number of hits for last search
70
     *
71
     * @var integer
72
     * @access protected
73
     */
74
    protected $numberOfHits = 0;
75
76
    /**
77
     * This holds the additional query parameters
78
     *
79
     * @var array
80
     * @access protected
81
     */
82
    protected $params = [];
83
84
    /**
85
     * Is the search instantiated successfully?
86
     *
87
     * @var boolean
88
     * @access protected
89
     */
90
    protected $ready = FALSE;
91
92
    /**
93
     * This holds the singleton search objects with their core as array key
94
     *
95
     * @var array (\Kitodo\Dlf\Common\Solr)
96
     * @access protected
97
     */
98
    protected static $registry = [];
99
100
    /**
101
     * This holds the Solr service object
102
     *
103
     * @var \Solarium\Client
104
     * @access protected
105
     */
106
    protected $service;
107
108
    /**
109
     * Escape all special characters in a query string
110
     *
111
     * @access public
112
     *
113
     * @param string $query: The query string
114
     *
115
     * @return string The escaped query string
116
     */
117
    public static function escapeQuery($query)
118
    {
119
        $helper = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\Solarium\Core\Query\Helper::class);
120
        // Escape query phrase or term.
121
        if (preg_match('/^".*"$/', $query)) {
122
            return $helper->escapePhrase(trim($query, '"'));
123
        } else {
124
            return $helper->escapeTerm($query);
125
        }
126
    }
127
128
    /**
129
     * Escape all special characters in a query string while retaining valid field queries
130
     *
131
     * @access public
132
     *
133
     * @param string $query: The query string
134
     * @param integer $pid: The PID for the field configuration
135
     *
136
     * @return string The escaped query string
137
     */
138
    public static function escapeQueryKeepField($query, $pid)
139
    {
140
        // Is there a field query?
141
        if (preg_match('/^[[:alnum:]]+_[tu][su]i:\(?.*\)?$/', $query)) {
142
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
143
                ->getQueryBuilderForTable('tx_dlf_metadata');
144
145
            // Get all indexed fields.
146
            $fields = [];
147
            $result = $queryBuilder
148
                ->select(
149
                    'tx_dlf_metadata.index_name AS index_name',
150
                    'tx_dlf_metadata.index_tokenized AS index_tokenized',
151
                    'tx_dlf_metadata.index_stored AS index_stored'
152
                )
153
                ->from('tx_dlf_metadata')
154
                ->where(
155
                    $queryBuilder->expr()->eq('tx_dlf_metadata.index_indexed', 1),
156
                    $queryBuilder->expr()->eq('tx_dlf_metadata.pid', intval($pid)),
157
                    $queryBuilder->expr()->orX(
158
                        $queryBuilder->expr()->in('tx_dlf_metadata.sys_language_uid', [-1, 0]),
159
                        $queryBuilder->expr()->eq('tx_dlf_metadata.l18n_parent', 0)
160
                    ),
161
                    Helper::whereExpression('tx_dlf_metadata')
162
                )
163
                ->execute();
164
165
            while ($resArray = $result->fetch()) {
166
                $fields[] = $resArray['index_name'] . '_' . ($resArray['index_tokenized'] ? 't' : 'u') . ($resArray['index_stored'] ? 's' : 'u') . 'i';
167
            }
168
169
            // Check if queried field is valid.
170
            $splitQuery = explode(':', $query, 2);
171
            if (in_array($splitQuery[0], $fields)) {
172
                $query = $splitQuery[0] . ':(' . self::escapeQuery(trim($splitQuery[1], '()')) . ')';
173
            } else {
174
                $query = self::escapeQuery($query);
175
            }
176
        } elseif (
177
            !empty($query)
178
            && $query !== '*'
179
        ) {
180
            // Don't escape plain asterisk search.
181
            $query = self::escapeQuery($query);
182
        }
183
        return $query;
184
    }
185
186
    /**
187
     * This is a singleton class, thus instances must be created by this method
188
     *
189
     * @access public
190
     *
191
     * @param mixed $core: Name or UID of the core to load
192
     *
193
     * @return \Kitodo\Dlf\Common\Solr Instance of this class
194
     */
195
    public static function getInstance($core)
196
    {
197
        // Get core name if UID is given.
198
        if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($core)) {
199
            $core = Helper::getIndexNameFromUid($core, 'tx_dlf_solrcores');
200
        }
201
        // Check if core is set.
202
        if (empty($core)) {
203
            Helper::devLog('Invalid core name "' . $core . '" for Apache Solr', DEVLOG_SEVERITY_ERROR);
204
            return;
205
        }
206
        // Check if there is an instance in the registry already.
207
        if (
208
            is_object(self::$registry[$core])
209
            && self::$registry[$core] instanceof self
210
        ) {
211
            // Return singleton instance if available.
212
            return self::$registry[$core];
213
        }
214
        // Create new instance...
215
        $instance = new self($core);
216
        // ...and save it to registry.
217
        if ($instance->ready) {
218
            self::$registry[$core] = $instance;
219
            // Return new instance.
220
            return $instance;
221
        } else {
222
            Helper::devLog('Could not connect to Apache Solr server', DEVLOG_SEVERITY_ERROR);
223
            return;
224
        }
225
    }
226
227
    /**
228
     * Returns the connection information for Solr
229
     *
230
     * @access public
231
     *
232
     * @return array The connection parameters for a specific Solr core
233
     */
234
    public static function getSolrConnectionInfo()
235
    {
236
        $solrInfo = [];
237
        // Extract extension configuration.
238
        $conf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf'][self::$extKey]);
239
        // Derive Solr scheme
240
        $solrInfo['scheme'] = empty($conf['solrHttps']) ? 'http' : 'https';
241
        // Derive Solr host name.
242
        $solrInfo['host'] = ($conf['solrHost'] ? $conf['solrHost'] : '127.0.0.1');
243
        // Set username and password.
244
        $solrInfo['username'] = $conf['solrUser'];
245
        $solrInfo['password'] = $conf['solrPass'];
246
        // Set port if not set.
247
        $solrInfo['port'] = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($conf['solrPort'], 1, 65535, 8983);
248
        // Append core name to path.
249
        $solrInfo['path'] = trim($conf['solrPath'], '/');
250
        // Timeout
251
        $solrInfo['timeout'] = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange($conf['solrTimeout'], 1, intval(ini_get('max_execution_time')), 10);
252
        return $solrInfo;
253
    }
254
255
    /**
256
     * Returns the request URL for a specific Solr core
257
     *
258
     * @access public
259
     *
260
     * @param string $core: Name of the core to load
261
     *
262
     * @return string The request URL for a specific Solr core
263
     */
264
    public static function getSolrUrl($core = '')
265
    {
266
        // Get Solr connection information.
267
        $solrInfo = self::getSolrConnectionInfo();
268
        if (
269
            $solrInfo['username']
270
            && $solrInfo['password']
271
        ) {
272
            $host = $solrInfo['username'] . ':' . $solrInfo['password'] . '@' . $solrInfo['host'];
273
        } else {
274
            $host = $solrInfo['host'];
275
        }
276
        // Return entire request URL.
277
        return $solrInfo['scheme'] . '://' . $host . ':' . $solrInfo['port'] . '/' . $solrInfo['path'] . '/' . $core;
278
    }
279
280
    /**
281
     * Get next unused Solr core number
282
     *
283
     * @access public
284
     *
285
     * @param integer $start: Number to start with
286
     *
287
     * @return integer First unused core number found
288
     */
289
    public static function solrGetCoreNumber($start = 0)
290
    {
291
        $start = max(intval($start), 0);
292
        // Check if core already exists.
293
        if (self::getInstance('dlfCore' . $start) === NULL) {
294
            return $start;
295
        } else {
296
            return self::solrGetCoreNumber($start + 1);
297
        }
298
    }
299
300
    /**
301
     * Processes a search request.
302
     *
303
     * @access public
304
     *
305
     * @return \Kitodo\Dlf\Common\DocumentList The result list
306
     */
307
    public function search()
308
    {
309
        $toplevel = [];
310
        // Take over query parameters.
311
        $params = $this->params;
312
        $params['filterquery'] = isset($params['filterquery']) ? $params['filterquery'] : [];
313
        // Set some query parameters.
314
        $params['start'] = 0;
315
        $params['rows'] = 0;
316
        // Perform search to determine the total number of hits without fetching them.
317
        $selectQuery = $this->service->createSelect($params);
318
        $results = $this->service->select($selectQuery);
319
        $this->numberOfHits = $results->getNumFound();
320
        // Restore query parameters
321
        $params = $this->params;
322
        $params['filterquery'] = isset($params['filterquery']) ? $params['filterquery'] : [];
323
        // Restrict the fields to the required ones.
324
        $params['fields'] = 'uid,id';
325
        // Extend filter query to get all documents with the same uids.
326
        foreach ($params['filterquery'] as $key => $value) {
327
            if (isset($value['query'])) {
328
                $params['filterquery'][$key]['query'] = '{!join from=uid to=uid}' . $value['query'];
329
            }
330
        }
331
        // Set filter query to just get toplevel documents.
332
        $params['filterquery'][] = ['query' => 'toplevel:true'];
333
        // Set join query to get all documents with the same uids.
334
        $params['query'] = '{!join from=uid to=uid}' . $params['query'];
335
        // Perform search to determine the total number of toplevel hits and fetch the required rows.
336
        $selectQuery = $this->service->createSelect($params);
337
        $results = $this->service->select($selectQuery);
338
        $numberOfToplevelHits = $results->getNumFound();
339
        // Process results.
340
        foreach ($results as $doc) {
341
            $toplevel[$doc->id] = [
342
                'u' => $doc->uid,
343
                'h' => '',
344
                's' => '',
345
                'p' => []
346
            ];
347
        }
348
        // Save list of documents.
349
        $list = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(DocumentList::class);
350
        $list->reset();
351
        $list->add(array_values($toplevel));
352
        // Set metadata for search.
353
        $list->metadata = [
354
            'label' => '',
355
            'description' => '',
356
            'options' => [
357
                'source' => 'search',
358
                'engine' => 'solr',
359
                'select' => $this->params['query'],
360
                'userid' => 0,
361
                'params' => $this->params,
362
                'core' => $this->core,
363
                'pid' => $this->cPid,
364
                'order' => 'score',
365
                'order.asc' => TRUE,
366
                'numberOfHits' => $this->numberOfHits,
367
                'numberOfToplevelHits' => $numberOfToplevelHits
368
            ]
369
        ];
370
        return $list;
371
    }
372
373
    /**
374
     * Processes a search request and returns the raw Apache Solr Documents.
375
     *
376
     * @access public
377
     *
378
     * @param string $query: The search query
379
     * @param array $parameters: Additional search parameters
380
     *
381
     * @return array The Apache Solr Documents that were fetched
382
     */
383
    public function search_raw($query = '', $parameters = [])
384
    {
385
        // Set additional query parameters.
386
        $parameters['start'] = 0;
387
        $parameters['rows'] = $this->limit;
388
        // Set query.
389
        $parameters['query'] = $query;
390
391
        // calculate cache identifier
392
        $cacheIdentifier = hash('md5', print_r(array_merge($this->params, $parameters), 1));
393
        $cache = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('tx_dlf_solr');
394
395
        $resultSet = [];
396
        if (($entry = $cache->get($cacheIdentifier)) === FALSE) {
397
            $selectQuery = $this->service->createSelect(array_merge($this->params, $parameters));
398
            $result = $this->service->select($selectQuery);
399
            foreach ($result as $doc) {
400
                $resultSet[] = $doc;
401
            }
402
            // Save value in cache
403
            $cache->set($cacheIdentifier, $resultSet);
404
        } else {
405
            // return cache hit
406
            $resultSet = $entry;
407
        }
408
        return $resultSet;
409
    }
410
411
    /**
412
     * This returns $this->limit via __get()
413
     *
414
     * @access protected
415
     *
416
     * @return integer The max number of results
417
     */
418
    protected function _getLimit()
419
    {
420
        return $this->limit;
421
    }
422
423
    /**
424
     * This returns $this->numberOfHits via __get()
425
     *
426
     * @access protected
427
     *
428
     * @return integer Total number of hits for last search
429
     */
430
    protected function _getNumberOfHits()
431
    {
432
        return $this->numberOfHits;
433
    }
434
435
    /**
436
     * This returns $this->ready via __get()
437
     *
438
     * @access protected
439
     *
440
     * @return boolean Is the search instantiated successfully?
441
     */
442
    protected function _getReady()
443
    {
444
        return $this->ready;
445
    }
446
447
    /**
448
     * This returns $this->service via __get()
449
     *
450
     * @access protected
451
     *
452
     * @return \Solarium\Client Apache Solr service object
453
     */
454
    protected function _getService()
455
    {
456
        return $this->service;
457
    }
458
459
    /**
460
     * This sets $this->cPid via __set()
461
     *
462
     * @access protected
463
     *
464
     * @param integer $value: The new PID for the metadata definitions
465
     *
466
     * @return void
467
     */
468
    protected function _setCPid($value)
469
    {
470
        $this->cPid = max(intval($value), 0);
471
    }
472
473
    /**
474
     * This sets $this->limit via __set()
475
     *
476
     * @access protected
477
     *
478
     * @param integer $value: The max number of results
479
     *
480
     * @return void
481
     */
482
    protected function _setLimit($value)
483
    {
484
        $this->limit = max(intval($value), 0);
485
    }
486
487
    /**
488
     * This sets $this->params via __set()
489
     *
490
     * @access protected
491
     *
492
     * @param array $value: The query parameters
493
     *
494
     * @return void
495
     */
496
    protected function _setParams(array $value)
497
    {
498
        $this->params = $value;
499
    }
500
501
    /**
502
     * This magic method is called each time an invisible property is referenced from the object
503
     *
504
     * @access public
505
     *
506
     * @param string $var: Name of variable to get
507
     *
508
     * @return mixed Value of $this->$var
509
     */
510
    public function __get($var)
511
    {
512
        $method = '_get' . ucfirst($var);
513
        if (
514
            !property_exists($this, $var)
515
            || !method_exists($this, $method)
516
        ) {
517
            Helper::devLog('There is no getter function for property "' . $var . '"', DEVLOG_SEVERITY_WARNING);
518
            return;
519
        } else {
520
            return $this->$method();
521
        }
522
    }
523
524
    /**
525
     * This magic method is called each time an invisible property is checked for isset() or empty()
526
     *
527
     * @access public
528
     *
529
     * @param string $var: Name of variable to check
530
     *
531
     * @return boolean TRUE if variable is set and not empty, FALSE otherwise
532
     */
533
    public function __isset($var) {
534
        return !empty($this->__get($var));
535
    }
536
537
    /**
538
     * This magic method is called each time an invisible property is referenced from the object
539
     *
540
     * @access public
541
     *
542
     * @param string $var: Name of variable to set
543
     * @param mixed $value: New value of variable
544
     *
545
     * @return void
546
     */
547
    public function __set($var, $value)
548
    {
549
        $method = '_set' . ucfirst($var);
550
        if (
551
            !property_exists($this, $var)
552
            || !method_exists($this, $method)
553
        ) {
554
            Helper::devLog('There is no setter function for property "' . $var . '"', DEVLOG_SEVERITY_WARNING);
555
        } else {
556
            $this->$method($value);
557
        }
558
    }
559
560
    /**
561
     * This is a singleton class, thus the constructor should be private/protected
562
     *
563
     * @access protected
564
     *
565
     * @param string $core: The name of the core to use
566
     *
567
     * @return void
568
     */
569
    protected function __construct($core)
570
    {
571
        $solrInfo = self::getSolrConnectionInfo();
572
        $config = [
573
            'endpoint' => [
574
                'dlf' => [
575
                    'scheme' => $solrInfo['scheme'],
576
                    'host' => $solrInfo['host'],
577
                    'port' => $solrInfo['port'],
578
                    'path' => '/' . $solrInfo['path'] . '/',
579
                    'core' => $core,
580
                    'username' => $solrInfo['username'],
581
                    'password' => $solrInfo['password'],
582
                    'timeout' => $solrInfo['timeout']
583
                ]
584
            ]
585
        ];
586
        // Instantiate Solarium\Client class.
587
        $this->service = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\Solarium\Client::class, $config);
588
        // Check if connection is established.
589
        $ping = $this->service->createPing();
590
        try {
591
            $this->service->ping($ping);
592
            // Set core name.
593
            $this->core = $core;
594
            // Instantiation successful!
595
            $this->ready = TRUE;
596
        } catch (\Exception $e) {
597
            // Nothing to do here.
598
        }
599
    }
600
}
601