Completed
Branch master (33af51)
by Timo
04:12
created

SolrService::getSolrconfigName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3.009

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 16
ccs 9
cts 10
cp 0.9
rs 9.4285
cc 3
eloc 10
nc 3
nop 0
crap 3.009
1
<?php
2
namespace ApacheSolrForTypo3\Solr;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2009-2015 Ingo Renner <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 2 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use TYPO3\CMS\Core\Utility\GeneralUtility;
28
29
/**
30
 * Solr Service Access
31
 *
32
 * @author Ingo Renner <[email protected]>
33
 */
34
class SolrService extends \Apache_Solr_Service
35
{
36
    const LUKE_SERVLET = 'admin/luke';
37
    const SYSTEM_SERVLET = 'admin/system';
38
    const PLUGINS_SERVLET = 'admin/plugins';
39
    const CORES_SERVLET = 'admin/cores';
40
    const SCHEMA_SERVLET = 'schema';
41
    const SYNONYMS_SERVLET = 'schema/analysis/synonyms/';
42
    const STOPWORDS_SERVLET = 'schema/analysis/stopwords/';
43
44
    const SCHEME_HTTP = 'http';
45
    const SCHEME_HTTPS = 'https';
46
47
    /**
48
     * Server connection scheme. http or https.
49
     *
50
     * @var string
51
     */
52
    protected $_scheme = self::SCHEME_HTTP;
53
54
    /**
55
     * Constructed servlet URL for Luke
56
     *
57
     * @var string
58
     */
59
    protected $_lukeUrl;
60
61
    /**
62
     * Constructed servlet URL for plugin information
63
     *
64
     * @var string
65
     */
66
    protected $_pluginsUrl;
67
68
    protected $_coresUrl;
69
70
    protected $_extractUrl;
71
72
    protected $_synonymsUrl;
73
74
    protected $_stopWordsUrl;
75
76
    protected $_schemaUrl;
77
78
    protected $debug;
79
80
    /**
81
     * @var \Apache_Solr_Response
82
     */
83
    protected $responseCache = null;
84
    protected $hasSearched = false;
85
86
    protected $lukeData = array();
87
    protected $systemData = null;
88
    protected $pluginsData = null;
89
90
    protected $schemaName = null;
91
    protected $solrconfigName = null;
92
93
    /**
94
     * @var array
95
     */
96
    protected $configuration;
97
98
    /**
99
     * Constructor
100
     *
101
     * @param string $host Solr host
102
     * @param string $port Solr port
103
     * @param string $path Solr path
104
     * @param string $scheme Scheme, defaults to http, can be https
105
     */
106 54
    public function __construct(
107
        $host = '',
108
        $port = '8983',
109
        $path = '/solr/',
110
        $scheme = 'http'
111
    ) {
112 54
        $this->setScheme($scheme);
113 53
        $this->configuration = Util::getSolrConfiguration();
0 ignored issues
show
Documentation Bug introduced by
It seems like \ApacheSolrForTypo3\Solr...:getSolrConfiguration() of type object<ApacheSolrForTypo...ypoScriptConfiguration> is incompatible with the declared type array of property $configuration.

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...
114
115 53
        parent::__construct($host, $port, $path);
116 53
    }
117
118
    /**
119
     * Creates a string representation of the Solr connection. Specifically
120
     * will return the Solr URL.
121
     *
122
     * @return string The Solr URL.
123
     */
124 4
    public function __toString()
125
    {
126 4
        return $this->_scheme . '://' . $this->_host . ':' . $this->_port . $this->_path;
127
    }
128
129
    /**
130
     * Returns the current time in milliseconds.
131
     *
132
     * @return int
133
     */
134
    protected function getMilliseconds()
135
    {
136
        return GeneralUtility::milliseconds();
137
    }
138
139
    /**
140
     * Performs a search.
141
     *
142
     * @param string $query query string / search term
143
     * @param int $offset result offset for pagination
144
     * @param int $limit number of results to retrieve
145
     * @param array $params additional HTTP GET parameters
146
     * @param string $method The HTTP method (Apache_Solr_Service::METHOD_GET or Apache_Solr_Service::METHOD::POST)
147
     * @return \Apache_Solr_Response Solr response
148
     * @throws \RuntimeException if Solr returns a HTTP status code other than 200
149
     */
150 25
    public function search($query, $offset = 0, $limit = 10, $params = array(), $method = self::METHOD_GET)
151
    {
152 25
        $response = parent::search($query, $offset, $limit, $params, $method);
153 25
        $this->hasSearched = true;
154
155 25
        $this->responseCache = $response;
156
157 25
        if ($response->getHttpStatus() != 200) {
158
            throw new \RuntimeException(
159
                'Invalid query. Solr returned an error: '
160
                . $response->getHttpStatus() . ' '
161
                . $response->getHttpStatusMessage(),
162
                1293109870
163
            );
164
        }
165
166 25
        return $response;
167
    }
168
169
    /**
170
     * Call the /admin/ping servlet, can be used to quickly tell if a connection to the
171
     * server is available.
172
     *
173
     * Simply overrides the SolrPhpClient implementation, changing ping from a
174
     * HEAD to a GET request, see http://forge.typo3.org/issues/44167
175
     *
176
     * Also does not report the time, see https://forge.typo3.org/issues/64551
177
     *
178
     * @param float|int $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
179
     * @return bool TRUE if Solr can be reached, FALSE if not
180
     */
181 35
    public function ping($timeout = 2)
182
    {
183 35
        $httpResponse = $this->performPingRequest($timeout);
184 35
        return ($httpResponse->getStatusCode() === 200);
185
    }
186
187
    /**
188
     * Call the /admin/ping servlet, can be used to get the runtime of a ping request.
189
     *
190
     * @param float|int $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
191
     * @return int runtime in milliseconds
192
     * @throws \ApacheSolrForTypo3\Solr\PingFailedException
193
     */
194
    public function getPingRoundTripRuntime($timeout = 2)
195
    {
196
        $start = $this->getMilliseconds();
197
        $httpResponse = $this->performPingRequest($timeout);
198
        $end = $this->getMilliseconds();
199
200
        if ($httpResponse->getStatusCode() !== 200) {
201
            $message = 'Solr ping failed with unexpected response code: ' . $httpResponse->getStatusCode();
202
            /** @var $exception \ApacheSolrForTypo3\Solr\PingFailedException */
203
            $exception = GeneralUtility::makeInstance('ApacheSolrForTypo3\Solr\PingFailedException', $message);
204
            $exception->setHttpResponse($httpResponse);
205
            throw $exception;
206
        }
207
208
        return $end - $start;
209
    }
210
211
    /**
212
     * Performs a ping request and returns the result.
213
     *
214
     * @param int $timeout
215
     * @return \Apache_Solr_HttpTransport_Response
216
     */
217 35
    protected function performPingRequest($timeout = 2)
218
    {
219 35
        return $this->getHttpTransport()->performGetRequest($this->_pingUrl, $timeout);
220
    }
221
222
    /**
223
     * Performs a content and meta data extraction request.
224
     *
225
     * @param ExtractingQuery $query An extraction query
226
     * @return array An array containing the extracted content [0] and meta data [1]
227
     */
228 1
    public function extractByQuery(ExtractingQuery $query)
229
    {
230
        $headers = array(
231 1
            'Content-Type' => 'multipart/form-data; boundary=' . $query->getMultiPartPostDataBoundary()
232
        );
233
234
        try {
235 1
            $response = $this->requestServlet(
236 1
                self::EXTRACT_SERVLET,
237 1
                $query->getQueryParameters(),
238 1
                'POST',
239
                $headers,
240 1
                $query->getRawPostFileData()
241
            );
242
        } catch (\Exception $e) {
243
            GeneralUtility::devLog('Extracting text and meta data through Solr Cell over HTTP POST',
244
                'solr', 3, array(
245
                    'query' => (array)$query,
246
                    'parameters' => $query->getQueryParameters(),
247
                    'file' => $query->getFile(),
248
                    'headers' => $headers,
249
                    'query url' => self::EXTRACT_SERVLET,
250
                    'exception' => $e->getMessage()
251
                ));
252
        }
253
254
        return array(
255 1
            $response->extracted,
256 1
            (array)$response->extracted_metadata
257
        );
258
    }
259
260
    /**
261
     * Make a request to a servlet (a path) that's not a standard path.
262
     *
263
     * @param string $servlet Path to be added to the base Solr path.
264
     * @param array $parameters Optional, additional request parameters when constructing the URL.
265
     * @param string $method HTTP method to use, defaults to GET.
266
     * @param array $requestHeaders Key value pairs of header names and values. Should include 'Content-Type' for POST and PUT.
267
     * @param string $rawPost Must be an empty string unless method is POST or PUT.
268
     * @param float|bool $timeout Read timeout in seconds, defaults to FALSE.
269
     * @return \Apache_Solr_Response Response object
270
     * @throws \Apache_Solr_HttpTransportException if returned HTTP status is other than 200
271
     */
272 1
    public function requestServlet(
273
        $servlet,
274
        $parameters = array(),
275
        $method = 'GET',
276
        $requestHeaders = array(),
277
        $rawPost = '',
278
        $timeout = false
279
    ) {
280 1
        $httpTransport = $this->getHttpTransport();
281 1
        $solrResponse = null;
0 ignored issues
show
Unused Code introduced by
$solrResponse 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...
282
283 1
        if ($method == 'GET' || $method == 'HEAD') {
284
            // Make sure we are not sending a request body.
285
            $rawPost = '';
286
        }
287
288
        // Add default parameters
289 1
        $parameters['wt'] = self::SOLR_WRITER;
290 1
        $parameters['json.nl'] = $this->_namedListTreatment;
291 1
        $url = $this->_constructUrl($servlet, $parameters);
292
293 1
        if ($method == self::METHOD_GET) {
294
            $httpResponse = $httpTransport->performGetRequest($url, $timeout);
295 1
        } elseif ($method == self::METHOD_POST) {
296
            // FIXME should respect all headers, not only Content-Type
297 1
            $httpResponse = $httpTransport->performPostRequest($url, $rawPost,
298 1
                $requestHeaders['Content-Type'], $timeout);
299
        }
300
301 1
        $solrResponse = new \Apache_Solr_Response($httpResponse,
0 ignored issues
show
Bug introduced by
The variable $httpResponse does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
302 1
            $this->_createDocuments, $this->_collapseSingleValueArrays);
303
304 1
        if ($solrResponse->getHttpStatus() != 200) {
305
            throw new \Apache_Solr_HttpTransportException($solrResponse);
306
        }
307
308 1
        return $solrResponse;
309
    }
310
311
    /**
312
     * Return a valid http URL given this server's scheme, host, port, and path
313
     * and a provided servlet name.
314
     *
315
     * @param string $servlet Servlet name
316
     * @param array $params Additional URL parameters to attach to the end of the URL
317
     * @return string Servlet URL
318
     */
319 53
    protected function _constructUrl($servlet, $params = array())
320
    {
321 53
        $url = parent::_constructUrl($servlet, $params);
322
323 53
        if (!GeneralUtility::isFirstPartOfStr($url, $this->_scheme)) {
324 1
            $parsedUrl = parse_url($url);
325
326
            // unfortunately can't use str_replace as it replace all
327
            // occurrences of $needle and can't be limited to replace only once
328 1
            $url = $this->_scheme . substr($url, strlen($parsedUrl['scheme']));
329
        }
330
331 53
        return $url;
332
    }
333
334
    /**
335
     * Returns the set scheme
336
     *
337
     * @return string
338
     */
339
    public function getScheme()
340
    {
341
        return $this->_scheme;
342
    }
343
344
    /**
345
     * Set the scheme used. If empty will fallback to constants
346
     *
347
     * @param string $scheme Either http or https
348
     * @throws \UnexpectedValueException
349
     */
350 54
    public function setScheme($scheme)
351
    {
352
        // Use the provided scheme or use the default
353 54
        if (empty($scheme)) {
354 1
            throw new \UnexpectedValueException('Scheme parameter is empty',
355 1
                1380756390);
356
        } else {
357 53
            if (in_array($scheme,
358 53
                array(self::SCHEME_HTTP, self::SCHEME_HTTPS))) {
359 53
                $this->_scheme = $scheme;
360
            } else {
361
                throw new \UnexpectedValueException('Unsupported scheme parameter, scheme must be http or https',
362
                    1380756442);
363
            }
364
        }
365
366 53
        if ($this->_urlsInited) {
367
            $this->_initUrls();
368
        }
369 53
    }
370
371
    /**
372
     * get field meta data for the index
373
     *
374
     * @param int $numberOfTerms Number of top terms to fetch for each field
375
     * @return array
376
     */
377
    public function getFieldsMetaData($numberOfTerms = 0)
378
    {
379
        return $this->getLukeMetaData($numberOfTerms)->fields;
380
    }
381
382
    /**
383
     * Retrieves meta data about the index from the luke request handler
384
     *
385
     * @param int $numberOfTerms Number of top terms to fetch for each field
386
     * @return \Apache_Solr_Response Index meta data
387
     */
388
    public function getLukeMetaData($numberOfTerms = 0)
389
    {
390
        if (!isset($this->lukeData[$numberOfTerms])) {
391
            $lukeUrl = $this->_constructUrl(
392
                self::LUKE_SERVLET,
393
                array(
394
                    'numTerms' => $numberOfTerms,
395
                    'wt' => self::SOLR_WRITER,
396
                    'fl' => '*'
397
                )
398
            );
399
400
            $this->lukeData[$numberOfTerms] = $this->_sendRawGet($lukeUrl);
401
        }
402
403
        return $this->lukeData[$numberOfTerms];
404
    }
405
406
    /**
407
     * Central method for making a get operation against this Solr Server
408
     *
409
     * @param string $url
410
     * @param float|bool $timeout Read timeout in seconds
411
     * @return \Apache_Solr_Response
412
     */
413 53
    protected function _sendRawGet($url, $timeout = false)
414
    {
415 53
        $logSeverity = 0; // info
416
417
        try {
418 53
            $response = parent::_sendRawGet($url, $timeout);
419 1
        } catch (\Apache_Solr_HttpTransportException $e) {
0 ignored issues
show
Bug introduced by
The class Apache_Solr_HttpTransportException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
420 1
            $logSeverity = 3; // fatal error
421 1
            $response = $e->getResponse();
422
        }
423
424 53
        if ($this->configuration->getLoggingQueryRawGet() || $response->getHttpStatus() != 200) {
0 ignored issues
show
Bug introduced by
The method getLoggingQueryRawGet cannot be called on $this->configuration (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
425
            $logData = array(
426 1
                'query url' => $url,
427 1
                'response' => (array)$response
428
            );
429
430 1
            if (!empty($e)) {
431 1
                $logData['exception'] = $e->__toString();
432
            } else {
433
                // trigger data parsing
434
                $response->response;
435
                $logData['response data'] = print_r($response, true);
436
            }
437
438 1
            GeneralUtility::devLog('Querying Solr using GET', 'solr',
439
                $logSeverity, $logData);
440
        }
441
442 53
        return $response;
443
    }
444
445
    /**
446
     * Returns whether a search has been executed or not.
447
     *
448
     * @return bool TRUE if a search has been executed, FALSE otherwise
449
     */
450
    public function hasSearched()
451
    {
452
        return $this->hasSearched;
453
    }
454
455
    /**
456
     * Gets the most recent response (if any)
457
     *
458
     * @return \Apache_Solr_Response Most recent response, or NULL if a search has not been executed yet.
459
     */
460
    public function getResponse()
461
    {
462
        return $this->responseCache;
463
    }
464
465
    /**
466
     * Enable/Disable debug mode
467
     *
468
     * @param bool $debug TRUE to enable debug mode, FALSE to turn off, off by default
469
     */
470
    public function setDebug($debug)
471
    {
472
        $this->debug = (boolean)$debug;
473
    }
474
475
    /**
476
     * Gets information about the plugins installed in Solr
477
     *
478
     * @return array A nested array of plugin data.
479
     */
480
    public function getPluginsInformation()
481
    {
482
        if (empty($this->pluginsData)) {
483
            $pluginsInformation = $this->_sendRawGet($this->_pluginsUrl);
484
485
            // access a random property to trigger response parsing
486
            $pluginsInformation->responseHeader;
487
            $this->pluginsData = $pluginsInformation;
488
        }
489
490
        return $this->pluginsData;
491
    }
492
493
    /**
494
     * Gets the name of the schema.xml file installed and in use on the Solr
495
     * server.
496
     *
497
     * @return string Name of the active schema.xml
498
     */
499 1
    public function getSchemaName()
500
    {
501 1
        if (is_null($this->schemaName)) {
502 1
            $systemInformation = $this->getSystemInformation();
503 1
            $this->schemaName = $systemInformation->core->schema;
504
        }
505
506 1
        return $this->schemaName;
507
    }
508
509
    /**
510
     * Gets information about the Solr server
511
     *
512
     * @return array A nested array of system data.
513
     */
514 2
    public function getSystemInformation()
515
    {
516 2
        if (empty($this->systemData)) {
517 2
            $systemInformation = $this->system();
518
519
            // access a random property to trigger response parsing
520 2
            $systemInformation->responseHeader;
521 2
            $this->systemData = $systemInformation;
522
        }
523
524 2
        return $this->systemData;
525
    }
526
527
    /**
528
     * Gets the name of the solrconfig.xml file installed and in use on the Solr
529
     * server.
530
     *
531
     * @return string Name of the active solrconfig.xml
532
     */
533 1
    public function getSolrconfigName()
534
    {
535 1
        if (is_null($this->solrconfigName)) {
536 1
            $solrconfigXmlUrl = $this->_scheme . '://'
537 1
                . $this->_host . ':' . $this->_port
538 1
                . $this->_path . 'admin/file/?file=solrconfig.xml';
539
540 1
            $solrconfigXml = simplexml_load_file($solrconfigXmlUrl);
541 1
            if ($solrconfigXml === false) {
542
                throw new \InvalidArgumentException('No valid xml response from schema file: ' . $solrconfigXmlUrl);
543
            }
544 1
            $this->solrconfigName = (string)$solrconfigXml->attributes()->name;
545
        }
546
547 1
        return $this->solrconfigName;
548
    }
549
550
    /**
551
     * Gets the Solr server's version number.
552
     *
553
     * @return string Solr version number
554
     */
555 1
    public function getSolrServerVersion()
556
    {
557 1
        $systemInformation = $this->getSystemInformation();
558
559
        // don't know why $systemInformation->lucene->solr-spec-version won't work
560 1
        $luceneInformation = (array)$systemInformation->lucene;
561 1
        return $luceneInformation['solr-spec-version'];
562
    }
563
564
    /**
565
     * Deletes all index documents of a certain type and does a commit
566
     * afterwards.
567
     *
568
     * @param string $type The type of documents to delete, usually a table name.
569
     * @param bool $commit Will commit immediately after deleting the documents if set, defaults to TRUE
570
     */
571
    public function deleteByType($type, $commit = true)
572
    {
573
        $this->deleteByQuery('type:' . trim($type));
574
575
        if ($commit) {
576
            $this->commit(false, false, false);
577
        }
578
    }
579
580
    /**
581
     * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be
582
     * a complete and well formed "delete" xml document
583
     *
584
     * @param string $rawPost Expected to be utf-8 encoded xml document
585
     * @param float|int $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
586
     * @return \Apache_Solr_Response
587
     */
588 5
    public function delete($rawPost, $timeout = 3600)
589
    {
590 5
        $response = $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout);
591
592 5
        GeneralUtility::devLog(
593 5
            'Delete Query sent.',
594 5
            'solr',
595 5
            1,
596
            array(
597 5
                'query' => $rawPost,
598 5
                'query url' => $this->_updateUrl,
599 5
                'response' => (array)$response
600
            )
601
        );
602
603 5
        return $response;
604
    }
605
606
    /**
607
     * Central method for making a post operation against this Solr Server
608
     *
609
     * @param string $url
610
     * @param string $rawPost
611
     * @param float|bool $timeout Read timeout in seconds
612
     * @param string $contentType
613
     * @return \Apache_Solr_Response
614
     */
615 46
    protected function _sendRawPost(
616
        $url,
617
        $rawPost,
618
        $timeout = false,
619
        $contentType = 'text/xml; charset=UTF-8'
620
    ) {
621 46
        $logSeverity = 0; // info
622
623
        try {
624 46
            $response = parent::_sendRawPost($url, $rawPost, $timeout,
625
                $contentType);
626
        } catch (\Apache_Solr_HttpTransportException $e) {
0 ignored issues
show
Bug introduced by
The class Apache_Solr_HttpTransportException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
627
            $logSeverity = 3; // fatal error
628
            $response = $e->getResponse();
629
        }
630
631 46
        if ($this->configuration->getLoggingQueryRawPost() || $response->getHttpStatus() != 200) {
0 ignored issues
show
Bug introduced by
The method getLoggingQueryRawPost cannot be called on $this->configuration (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
632
            $logData = array(
633
                'query url' => $url,
634
                'content' => $rawPost,
635
                'response' => (array)$response
636
            );
637
638
            if (!empty($e)) {
639
                $logData['exception'] = $e->__toString();
640
            }
641
642
            GeneralUtility::devLog('Querying Solr using POST', 'solr',
643
                $logSeverity, $logData);
644
        }
645
646 46
        return $response;
647
    }
648
649
    /**
650
     * Get currently configured synonyms
651
     *
652
     * @param string $baseWord If given a base word, retrieves the synonyms for that word only
653
     * @return array
654
     */
655
    public function getSynonyms($baseWord = '')
656
    {
657
        $synonymsUrl = $this->_synonymsUrl;
658
        if (!empty($baseWord)) {
659
            $synonymsUrl .= '/' . $baseWord;
660
        }
661
662
        $response = $this->_sendRawGet($synonymsUrl);
663
        $decodedResponse = json_decode($response->getRawResponse());
664
665
        $synonyms = array();
666
        if (!empty($baseWord)) {
667
            if (is_array($decodedResponse->{$baseWord})) {
668
                $synonyms = $decodedResponse->{$baseWord};
669
            }
670
        } else {
671
            if (isset($decodedResponse->synonymMappings->managedMap)) {
672
                $synonyms = (array)$decodedResponse->synonymMappings->managedMap;
673
            }
674
        }
675
676
        return $synonyms;
677
    }
678
679
    /**
680
     * Add list of synonyms for base word to managed synonyms map
681
     *
682
     * @param $baseWord
683
     * @param array $synonyms
684
     *
685
     * @return \Apache_Solr_Response
686
     *
687
     * @throws \Apache_Solr_InvalidArgumentException If $baseWord or $synonyms are empty
688
     */
689
    public function addSynonym($baseWord, array $synonyms)
690
    {
691
        if (empty($baseWord) || empty($synonyms)) {
692
            throw new \Apache_Solr_InvalidArgumentException('Must provide base word and synonyms.');
693
        }
694
695
        $rawPut = json_encode(array($baseWord => $synonyms));
696
        return $this->_sendRawPost($this->_synonymsUrl, $rawPut,
697
            $this->getHttpTransport()->getDefaultTimeout(), 'application/json');
698
    }
699
700
    /**
701
     * Remove a synonym from the synonyms map
702
     *
703
     * @param $baseWord
704
     * @return \Apache_Solr_Response
705
     * @throws \Apache_Solr_InvalidArgumentException
706
     */
707
    public function deleteSynonym($baseWord)
708
    {
709
        if (empty($baseWord)) {
710
            throw new \Apache_Solr_InvalidArgumentException('Must provide base word.');
711
        }
712
713
        return $this->_sendRawDelete($this->_synonymsUrl . '/' . $baseWord);
714
    }
715
716
    /**
717
     * Central method for making a HTTP DELETE operation against the Solr server
718
     *
719
     * @param $url
720
     * @param bool|float $timeout Read timeout in seconds
721
     * @return \Apache_Solr_Response
722
     */
723
    protected function _sendRawDelete($url, $timeout = false)
724
    {
725
        $logSeverity = 0; // info
726
727
        try {
728
            $httpTransport = $this->getHttpTransport();
729
730
            $httpResponse = $httpTransport->performDeleteRequest($url,
731
                $timeout);
732
            $solrResponse = new \Apache_Solr_Response($httpResponse,
733
                $this->_createDocuments, $this->_collapseSingleValueArrays);
734
735
            if ($solrResponse->getHttpStatus() != 200) {
736
                throw new \Apache_Solr_HttpTransportException($solrResponse);
737
            }
738
        } catch (\Apache_Solr_HttpTransportException $e) {
0 ignored issues
show
Bug introduced by
The class Apache_Solr_HttpTransportException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
739
            $logSeverity = 3; // fatal error
740
            $solrResponse = $e->getResponse();
741
        }
742
743
        if ($this->configuration->getLoggingQueryRawDelete() || $solrResponse->getHttpStatus() != 200) {
0 ignored issues
show
Bug introduced by
The method getLoggingQueryRawDelete cannot be called on $this->configuration (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
744
            $logData = array(
745
                'query url' => $url,
746
                'response' => (array)$solrResponse
747
            );
748
749
            if (!empty($e)) {
750
                $logData['exception'] = $e->__toString();
751
            } else {
752
                // trigger data parsing
753
                $solrResponse->response;
754
                $logData['response data'] = print_r($solrResponse, true);
755
            }
756
757
            GeneralUtility::devLog('Querying Solr using DELETE', 'solr',
758
                $logSeverity, $logData);
759
        }
760
761
        return $solrResponse;
762
    }
763
764
    /**
765
     * Get currently configured stop words
766
     *
767
     * @return array
768
     */
769
    public function getStopWords()
770
    {
771
        $stopWords = array();
772
773
        $response = $this->_sendRawGet($this->_stopWordsUrl);
774
        $decodedResponse = json_decode($response->getRawResponse());
775
776
        if (isset($decodedResponse->wordSet->managedList)) {
777
            $stopWords = (array)$decodedResponse->wordSet->managedList;
778
        }
779
780
        return $stopWords;
781
    }
782
783
    /**
784
     * Adds stop words to the managed stop word list
785
     *
786
     * @param array|string $stopWords string for a single word, array for multiple words
787
     * @return \Apache_Solr_Response
788
     * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty
789
     */
790
    public function addStopWords($stopWords)
791
    {
792
        if (empty($stopWords)) {
793
            throw new \Apache_Solr_InvalidArgumentException('Must provide stop word.');
794
        }
795
796
        if (is_string($stopWords)) {
797
            $stopWords = array($stopWords);
798
        }
799
800
        $stopWords = array_values($stopWords);
801
        $rawPut = json_encode($stopWords);
802
        return $this->_sendRawPost($this->_stopWordsUrl, $rawPut,
803
            $this->getHttpTransport()->getDefaultTimeout(), 'application/json');
804
    }
805
806
    /**
807
     * Deletes a words from the managed stop word list
808
     *
809
     * @param string $stopWord stop word to delete
810
     * @return \Apache_Solr_Response
811
     * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty
812
     */
813
    public function deleteStopWord($stopWord)
814
    {
815
        if (empty($stopWord)) {
816
            throw new \Apache_Solr_InvalidArgumentException('Must provide stop word.');
817
        }
818
819
        return $this->_sendRawDelete($this->_stopWordsUrl . '/' . $stopWord);
820
    }
821
822
    /**
823
     * Reloads the current core
824
     *
825
     * @return \Apache_Solr_Response
826
     */
827
    public function reloadCore()
828
    {
829
        $coreName = array_pop(explode('/', trim($this->_path, '/')));
0 ignored issues
show
Bug introduced by
explode('/', trim($this->_path, '/')) cannot be passed to array_pop() as the parameter $array expects a reference.
Loading history...
830
        $coreAdminReloadUrl = $this->_coresUrl . '?action=reload&core=' . $coreName;
831
832
        return $this->_sendRawGet($coreAdminReloadUrl);
833
    }
834
835
    /**
836
     * initializes various URLs, including the Luke URL
837
     *
838
     * @return void
839
     */
840 53
    protected function _initUrls()
841
    {
842 53
        parent::_initUrls();
843
844 53
        $this->_lukeUrl = $this->_constructUrl(
845 53
            self::LUKE_SERVLET,
846
            array(
847 53
                'numTerms' => '0',
848 53
                'wt' => self::SOLR_WRITER
849
            )
850
        );
851
852 53
        $this->_pluginsUrl = $this->_constructUrl(
853 53
            self::PLUGINS_SERVLET,
854 53
            array('wt' => self::SOLR_WRITER)
855
        );
856
857 53
        $pathElements = explode('/', trim($this->_path, '/'));
858 53
        $this->_coresUrl =
859 53
            $this->_scheme . '://' .
860 53
            $this->_host . ':' .
861 53
            $this->_port . '/' .
862 53
            $pathElements[0] . '/' .
863 53
            self::CORES_SERVLET;
864
865 53
        $this->_schemaUrl = $this->_constructUrl(self::SCHEMA_SERVLET);
866
867 53
        $managedLanguage = $this->getManagedLanguage();
868 53
        $this->_synonymsUrl = $this->_constructUrl(
869 53
                self::SYNONYMS_SERVLET
870 53
            ) . $managedLanguage;
871 53
        $this->_stopWordsUrl = $this->_constructUrl(
872 53
                self::STOPWORDS_SERVLET
873 53
            ) . $managedLanguage;
874 53
    }
875
876
    /**
877
     * Get the language map name for the text field.
878
     * This is necessary to select the correct synonym map.
879
     *
880
     * @return string
881
     */
882 53
    protected function getManagedLanguage()
883
    {
884 53
        $language = 'english';
885
886 53
        $schema = $this->getSchema();
887
888 53
        if (is_object($schema) && isset($schema->fieldTypes)) {
889 52
            foreach ($schema->fieldTypes as $fieldType) {
890 52
                if ($fieldType->name === 'text') {
891 52
                    foreach ($fieldType->queryAnalyzer->filters as $filter) {
892 52
                        if ($filter->class === 'solr.ManagedSynonymFilterFactory') {
893 52
                            $language = $filter->managed;
894
                        }
895
                    }
896
                }
897
            }
898
        }
899
900 53
        return $language;
901
    }
902
903
    /**
904
     * Get the configured schema for the current core
905
     *
906
     * @return \stdClass
907
     */
908 53
    protected function getSchema()
909
    {
910 53
        $response = $this->_sendRawGet($this->_schemaUrl);
911 53
        return json_decode($response->getRawResponse())->schema;
912
    }
913
}
914