Passed
Branch master (b37043)
by Timo
11:18
created

SolrService   F

Complexity

Total Complexity 86

Size/Duplication

Total Lines 882
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 4

Importance

Changes 0
Metric Value
wmc 86
c 0
b 0
f 0
lcom 4
cbo 4
dl 0
loc 882
rs 1.263

37 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A __toString() 0 4 1
A getMilliseconds() 0 4 1
A search() 0 18 2
A ping() 0 5 1
A getPingRoundTripRuntime() 0 16 2
A performPingRequest() 0 4 1
B extractByQuery() 0 31 2
B requestServlet() 0 38 6
A _constructUrl() 0 14 2
A getScheme() 0 4 1
A setScheme() 0 20 4
A getFieldsMetaData() 0 4 1
A getLukeMetaData() 0 17 2
A hasSearched() 0 4 1
A getResponse() 0 4 1
A setDebug() 0 4 1
A getPluginsInformation() 0 12 2
A getSchemaName() 0 9 2
A getSystemInformation() 0 12 2
A getSolrconfigName() 0 16 3
A getSolrServerVersion() 0 8 1
A deleteByType() 0 8 2
A delete() 0 17 1
B _sendRawPost() 0 33 5
B getSynonyms() 0 23 5
A addSynonym() 0 10 3
A deleteSynonym() 0 8 2
A getStopWords() 0 13 2
A addStopWords() 0 15 3
A deleteStopWord() 0 8 2
A reloadCore() 0 7 1
B _initUrls() 0 35 1
B getManagedLanguage() 0 20 7
A getSchema() 0 5 1
B _sendRawGet() 0 31 5
B _sendRawDelete() 0 40 6

How to fix   Complexity   

Complex Class

Complex classes like SolrService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SolrService, and based on these observations, apply Extract Interface, too.

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
use ApacheSolrForTypo3\Solr\PingFailedException;
29
30
/**
31
 * Solr Service Access
32
 *
33
 * @author Ingo Renner <[email protected]>
34
 * @package TYPO3
35
 * @subpackage solr
36
 */
37
class SolrService extends \Apache_Solr_Service
38
{
39
40
    const LUKE_SERVLET = 'admin/luke';
41
    const SYSTEM_SERVLET = 'admin/system';
42
    const PLUGINS_SERVLET = 'admin/plugins';
43
    const CORES_SERVLET = 'admin/cores';
44
    const SCHEMA_SERVLET = 'schema';
45
    const SYNONYMS_SERVLET = 'schema/analysis/synonyms/';
46
    const STOPWORDS_SERVLET = 'schema/analysis/stopwords/';
47
48
    const SCHEME_HTTP = 'http';
49
    const SCHEME_HTTPS = 'https';
50
51
    /**
52
     * Server connection scheme. http or https.
53
     *
54
     * @var string
55
     */
56
    protected $_scheme = self::SCHEME_HTTP;
57
58
    /**
59
     * Constructed servlet URL for Luke
60
     *
61
     * @var string
62
     */
63
    protected $_lukeUrl;
64
65
    /**
66
     * Constructed servlet URL for plugin information
67
     *
68
     * @var string
69
     */
70
    protected $_pluginsUrl;
71
72
    protected $_coresUrl;
73
74
    protected $_extractUrl;
75
76
    protected $_synonymsUrl;
77
78
    protected $_stopWordsUrl;
79
80
    protected $_schemaUrl;
81
82
    protected $debug;
83
84
    /**
85
     * @var \Apache_Solr_Response
86
     */
87
    protected $responseCache = null;
88
    protected $hasSearched = false;
89
90
    protected $lukeData = array();
91
    protected $systemData = null;
92
    protected $pluginsData = null;
93
94
    protected $schemaName = null;
95
    protected $solrconfigName = null;
96
97
    /**
98
     * @var array
99
     */
100
    protected $configuration;
101
102
103
    /**
104
     * Constructor
105
     *
106
     * @param string $host Solr host
107
     * @param string $port Solr port
108
     * @param string $path Solr path
109
     * @param string $scheme Scheme, defaults to http, can be https
110
     */
111
    public function __construct(
112
        $host = '',
113
        $port = '8080',
114
        $path = '/solr/',
115
        $scheme = 'http'
116
    ) {
117
        $this->setScheme($scheme);
118
        $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...
119
120
        parent::__construct($host, $port, $path);
121
    }
122
123
    /**
124
     * Creates a string representation of the Solr connection. Specifically
125
     * will return the Solr URL.
126
     *
127
     * @return string The Solr URL.
128
     */
129
    public function __toString()
130
    {
131
        return $this->_scheme . '://' . $this->_host . ':' . $this->_port . $this->_path;
132
    }
133
134
    /**
135
     * Returns the current time in milliseconds.
136
     *
137
     * @return integer
138
     */
139
    protected function getMilliseconds()
140
    {
141
        return GeneralUtility::milliseconds();
142
    }
143
144
    /**
145
     * Performs a search.
146
     *
147
     * @param string $query query string / search term
148
     * @param integer $offset result offset for pagination
149
     * @param integer $limit number of results to retrieve
150
     * @param array $params additional HTTP GET parameters
151
     * @param string $method The HTTP method (Apache_Solr_Service::METHOD_GET or Apache_Solr_Service::METHOD::POST)
152
     * @return \Apache_Solr_Response Solr response
153
     * @throws \RuntimeException if Solr returns a HTTP status code other than 200
154
     */
155
    public function search($query, $offset = 0, $limit = 10, $params = array(), $method = self::METHOD_GET)
156
    {
157
        $response = parent::search($query, $offset, $limit, $params, $method);
158
        $this->hasSearched = true;
159
160
        $this->responseCache = $response;
161
162
        if ($response->getHttpStatus() != 200) {
163
            throw new \RuntimeException(
164
                'Invalid query. Solr returned an error: '
165
                . $response->getHttpStatus() . ' '
166
                . $response->getHttpStatusMessage(),
167
                1293109870
168
            );
169
        }
170
171
        return $response;
172
    }
173
174
    /**
175
     * Call the /admin/ping servlet, can be used to quickly tell if a connection to the
176
     * server is available.
177
     *
178
     * Simply overrides the SolrPhpClient implementation, changing ping from a
179
     * HEAD to a GET request, see http://forge.typo3.org/issues/44167
180
     *
181
     * Also does not report the time, see https://forge.typo3.org/issues/64551
182
     *
183
     * @param float|integer $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
184
     * @return bool TRUE if Solr can be reached, FALSE if not
185
     */
186
    public function ping($timeout = 2)
187
    {
188
        $httpResponse = $this->performPingRequest($timeout);
189
        return ($httpResponse->getStatusCode() === 200);
190
    }
191
192
    /**
193
     * Call the /admin/ping servlet, can be used to get the runtime of a ping request.
194
     *
195
     * @param float|integer $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
196
     * @return int runtime in milliseconds
197
     * @throws \ApacheSolrForTypo3\Solr\PingFailedException
198
     */
199
    public function getPingRoundTripRuntime($timeout = 2)
200
    {
201
        $start = $this->getMilliseconds();
202
        $httpResponse = $this->performPingRequest($timeout);
203
        $end = $this->getMilliseconds();
204
205
        if ($httpResponse->getStatusCode() !== 200) {
206
            $message = 'Solr ping failed with unexpected response code: ' . $httpResponse->getStatusCode();
207
            /** @var $exception \ApacheSolrForTypo3\Solr\PingFailedException */
208
            $exception = GeneralUtility::makeInstance('ApacheSolrForTypo3\Solr\PingFailedException', $message);
209
            $exception->setHttpResponse($httpResponse);
210
            throw $exception;
211
        }
212
213
        return $end - $start;
214
    }
215
216
    /**
217
     * Performs a ping request and returns the result.
218
     *
219
     * @param int $timeout
220
     * @return \Apache_Solr_HttpTransport_Response
221
     */
222
    protected function performPingRequest($timeout = 2)
223
    {
224
        return $this->getHttpTransport()->performGetRequest($this->_pingUrl, $timeout);
225
    }
226
227
    /**
228
     * Performs a content and meta data extraction request.
229
     *
230
     * @param ExtractingQuery $query An extraction query
231
     * @return array An array containing the extracted content [0] and meta data [1]
232
     */
233
    public function extractByQuery(ExtractingQuery $query)
234
    {
235
        $headers = array(
236
            'Content-Type' => 'multipart/form-data; boundary=' . $query->getMultiPartPostDataBoundary()
237
        );
238
239
        try {
240
            $response = $this->requestServlet(
241
                self::EXTRACT_SERVLET,
242
                $query->getQueryParameters(),
243
                'POST',
244
                $headers,
245
                $query->getRawPostFileData()
246
            );
247
        } catch (\Exception $e) {
248
            GeneralUtility::devLog('Extracting text and meta data through Solr Cell over HTTP POST',
249
                'solr', 3, array(
250
                    'query' => (array)$query,
251
                    'parameters' => $query->getQueryParameters(),
252
                    'file' => $query->getFile(),
253
                    'headers' => $headers,
254
                    'query url' => self::EXTRACT_SERVLET,
255
                    'exception' => $e->getMessage()
256
                ));
257
        }
258
259
        return array(
260
            $response->extracted,
261
            (array)$response->extracted_metadata
262
        );
263
    }
264
265
    /**
266
     * Make a request to a servlet (a path) that's not a standard path.
267
     *
268
     * @param string $servlet Path to be added to the base Solr path.
269
     * @param array $parameters Optional, additional request parameters when constructing the URL.
270
     * @param string $method HTTP method to use, defaults to GET.
271
     * @param array $requestHeaders Key value pairs of header names and values. Should include 'Content-Type' for POST and PUT.
272
     * @param string $rawPost Must be an empty string unless method is POST or PUT.
273
     * @param float|boolean $timeout Read timeout in seconds, defaults to FALSE.
274
     * @return \Apache_Solr_Response Response object
275
     * @throws \Apache_Solr_HttpTransportException if returned HTTP status is other than 200
276
     */
277
    public function requestServlet(
278
        $servlet,
279
        $parameters = array(),
280
        $method = 'GET',
281
        $requestHeaders = array(),
282
        $rawPost = '',
283
        $timeout = false
284
    ) {
285
        $httpTransport = $this->getHttpTransport();
286
        $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...
287
288
        if ($method == 'GET' || $method == 'HEAD') {
289
            // Make sure we are not sending a request body.
290
            $rawPost = '';
291
        }
292
293
        // Add default parameters
294
        $parameters['wt'] = self::SOLR_WRITER;
295
        $parameters['json.nl'] = $this->_namedListTreatment;
296
        $url = $this->_constructUrl($servlet, $parameters);
297
298
        if ($method == self::METHOD_GET) {
299
            $httpResponse = $httpTransport->performGetRequest($url, $timeout);
300
        } elseif ($method == self::METHOD_POST) {
301
            // FIXME should respect all headers, not only Content-Type
302
            $httpResponse = $httpTransport->performPostRequest($url, $rawPost,
303
                $requestHeaders['Content-Type'], $timeout);
304
        }
305
306
        $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...
307
            $this->_createDocuments, $this->_collapseSingleValueArrays);
308
309
        if ($solrResponse->getHttpStatus() != 200) {
310
            throw new \Apache_Solr_HttpTransportException($solrResponse);
311
        }
312
313
        return $solrResponse;
314
    }
315
316
    /**
317
     * Return a valid http URL given this server's scheme, host, port, and path
318
     * and a provided servlet name.
319
     *
320
     * @param string $servlet Servlet name
321
     * @param array $params Additional URL parameters to attach to the end of the URL
322
     * @return string Servlet URL
323
     */
324
    protected function _constructUrl($servlet, $params = array())
325
    {
326
        $url = parent::_constructUrl($servlet, $params);
327
328
        if (!GeneralUtility::isFirstPartOfStr($url, $this->_scheme)) {
329
            $parsedUrl = parse_url($url);
330
331
            // unfortunately can't use str_replace as it replace all
332
            // occurrences of $needle and can't be limited to replace only once
333
            $url = $this->_scheme . substr($url, strlen($parsedUrl['scheme']));
334
        }
335
336
        return $url;
337
    }
338
339
    /**
340
     * Returns the set scheme
341
     *
342
     * @return string
343
     */
344
    public function getScheme()
345
    {
346
        return $this->_scheme;
347
    }
348
349
    /**
350
     * Set the scheme used. If empty will fallback to constants
351
     *
352
     * @param string $scheme Either http or https
353
     * @throws \UnexpectedValueException
354
     */
355
    public function setScheme($scheme)
356
    {
357
        // Use the provided scheme or use the default
358
        if (empty($scheme)) {
359
            throw new \UnexpectedValueException('Scheme parameter is empty',
360
                1380756390);
361
        } else {
362
            if (in_array($scheme,
363
                array(self::SCHEME_HTTP, self::SCHEME_HTTPS))) {
364
                $this->_scheme = $scheme;
365
            } else {
366
                throw new \UnexpectedValueException('Unsupported scheme parameter, scheme must be http or https',
367
                    1380756442);
368
            }
369
        }
370
371
        if ($this->_urlsInited) {
372
            $this->_initUrls();
373
        }
374
    }
375
376
    /**
377
     * get field meta data for the index
378
     *
379
     * @param integer $numberOfTerms Number of top terms to fetch for each field
380
     * @return array
381
     */
382
    public function getFieldsMetaData($numberOfTerms = 0)
383
    {
384
        return $this->getLukeMetaData($numberOfTerms)->fields;
385
    }
386
387
    /**
388
     * Retrieves meta data about the index from the luke request handler
389
     *
390
     * @param integer $numberOfTerms Number of top terms to fetch for each field
391
     * @return \Apache_Solr_Response Index meta data
392
     */
393
    public function getLukeMetaData($numberOfTerms = 0)
394
    {
395
        if (!isset($this->lukeData[$numberOfTerms])) {
396
            $lukeUrl = $this->_constructUrl(
397
                self::LUKE_SERVLET,
398
                array(
399
                    'numTerms' => $numberOfTerms,
400
                    'wt' => self::SOLR_WRITER,
401
                    'fl' => '*'
402
                )
403
            );
404
405
            $this->lukeData[$numberOfTerms] = $this->_sendRawGet($lukeUrl);
406
        }
407
408
        return $this->lukeData[$numberOfTerms];
409
    }
410
411
    /**
412
     * Central method for making a get operation against this Solr Server
413
     *
414
     * @param string $url
415
     * @param float|boolean $timeout Read timeout in seconds
416
     * @return \Apache_Solr_Response
417
     */
418
    protected function _sendRawGet($url, $timeout = false)
419
    {
420
        $logSeverity = 0; // info
421
422
        try {
423
            $response = parent::_sendRawGet($url, $timeout);
424
        } 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...
425
            $logSeverity = 3; // fatal error
426
            $response = $e->getResponse();
427
        }
428
429
        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...
430
            $logData = array(
431
                'query url' => $url,
432
                'response' => (array)$response
433
            );
434
435
            if (!empty($e)) {
436
                $logData['exception'] = $e->__toString();
437
            } else {
438
                // trigger data parsing
439
                $response->response;
440
                $logData['response data'] = print_r($response, true);
441
            }
442
443
            GeneralUtility::devLog('Querying Solr using GET', 'solr',
444
                $logSeverity, $logData);
445
        }
446
447
        return $response;
448
    }
449
450
    /**
451
     * Returns whether a search has been executed or not.
452
     *
453
     * @return bool TRUE if a search has been executed, FALSE otherwise
454
     */
455
    public function hasSearched()
456
    {
457
        return $this->hasSearched;
458
    }
459
460
    /**
461
     * Gets the most recent response (if any)
462
     *
463
     * @return \Apache_Solr_Response Most recent response, or NULL if a search has not been executed yet.
464
     */
465
    public function getResponse()
466
    {
467
        return $this->responseCache;
468
    }
469
470
    /**
471
     * Enable/Disable debug mode
472
     *
473
     * @param boolean $debug TRUE to enable debug mode, FALSE to turn off, off by default
474
     */
475
    public function setDebug($debug)
476
    {
477
        $this->debug = (boolean)$debug;
478
    }
479
480
    /**
481
     * Gets information about the plugins installed in Solr
482
     *
483
     * @return array A nested array of plugin data.
484
     */
485
    public function getPluginsInformation()
486
    {
487
        if (empty($this->pluginsData)) {
488
            $pluginsInformation = $this->_sendRawGet($this->_pluginsUrl);
489
490
            // access a random property to trigger response parsing
491
            $pluginsInformation->responseHeader;
492
            $this->pluginsData = $pluginsInformation;
493
        }
494
495
        return $this->pluginsData;
496
    }
497
498
    /**
499
     * Gets the name of the schema.xml file installed and in use on the Solr
500
     * server.
501
     *
502
     * @return string Name of the active schema.xml
503
     */
504
    public function getSchemaName()
505
    {
506
        if (is_null($this->schemaName)) {
507
            $systemInformation = $this->getSystemInformation();
508
            $this->schemaName = $systemInformation->core->schema;
509
        }
510
511
        return $this->schemaName;
512
    }
513
514
    /**
515
     * Gets information about the Solr server
516
     *
517
     * @return array A nested array of system data.
518
     */
519
    public function getSystemInformation()
520
    {
521
        if (empty($this->systemData)) {
522
            $systemInformation = $this->system();
523
524
            // access a random property to trigger response parsing
525
            $systemInformation->responseHeader;
526
            $this->systemData = $systemInformation;
527
        }
528
529
        return $this->systemData;
530
    }
531
532
    /**
533
     * Gets the name of the solrconfig.xml file installed and in use on the Solr
534
     * server.
535
     *
536
     * @return string Name of the active solrconfig.xml
537
     */
538
    public function getSolrconfigName()
539
    {
540
        if (is_null($this->solrconfigName)) {
541
            $solrconfigXmlUrl = $this->_scheme . '://'
542
                . $this->_host . ':' . $this->_port
543
                . $this->_path . 'admin/file/?file=solrconfig.xml';
544
545
            $solrconfigXml = simplexml_load_file($solrconfigXmlUrl);
546
            if ($solrconfigXml === false) {
547
                throw new \InvalidArgumentException('No valid xml response from schema file: '.$solrconfigXmlUrl);
548
            }
549
            $this->solrconfigName = (string)$solrconfigXml->attributes()->name;
550
        }
551
552
        return $this->solrconfigName;
553
    }
554
555
    /**
556
     * Gets the Solr server's version number.
557
     *
558
     * @return string Solr version number
559
     */
560
    public function getSolrServerVersion()
561
    {
562
        $systemInformation = $this->getSystemInformation();
563
564
        // don't know why $systemInformation->lucene->solr-spec-version won't work
565
        $luceneInformation = (array)$systemInformation->lucene;
566
        return $luceneInformation['solr-spec-version'];
567
    }
568
569
    /**
570
     * Deletes all index documents of a certain type and does a commit
571
     * afterwards.
572
     *
573
     * @param string $type The type of documents to delete, usually a table name.
574
     * @param boolean $commit Will commit immediately after deleting the documents if set, defaults to TRUE
575
     */
576
    public function deleteByType($type, $commit = true)
577
    {
578
        $this->deleteByQuery('type:' . trim($type));
579
580
        if ($commit) {
581
            $this->commit(false, false, false);
582
        }
583
    }
584
585
    /**
586
     * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be
587
     * a complete and well formed "delete" xml document
588
     *
589
     * @param string $rawPost Expected to be utf-8 encoded xml document
590
     * @param float|integer $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
591
     * @return \Apache_Solr_Response
592
     */
593
    public function delete($rawPost, $timeout = 3600)
594
    {
595
        $response = $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout);
596
597
        GeneralUtility::devLog(
598
            'Delete Query sent.',
599
            'solr',
600
            1,
601
            array(
602
                'query' => $rawPost,
603
                'query url' => $this->_updateUrl,
604
                'response' => (array)$response
605
            )
606
        );
607
608
        return $response;
609
    }
610
611
    /**
612
     * Central method for making a post operation against this Solr Server
613
     *
614
     * @param string $url
615
     * @param string $rawPost
616
     * @param float|boolean $timeout Read timeout in seconds
617
     * @param string $contentType
618
     * @return \Apache_Solr_Response
619
     */
620
    protected function _sendRawPost(
621
        $url,
622
        $rawPost,
623
        $timeout = false,
624
        $contentType = 'text/xml; charset=UTF-8'
625
    ) {
626
        $logSeverity = 0; // info
627
628
        try {
629
            $response = parent::_sendRawPost($url, $rawPost, $timeout,
630
                $contentType);
631
        } 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...
632
            $logSeverity = 3; // fatal error
633
            $response = $e->getResponse();
634
        }
635
636
        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...
637
            $logData = array(
638
                'query url' => $url,
639
                'content' => $rawPost,
640
                'response' => (array)$response
641
            );
642
643
            if (!empty($e)) {
644
                $logData['exception'] = $e->__toString();
645
            }
646
647
            GeneralUtility::devLog('Querying Solr using POST', 'solr',
648
                $logSeverity, $logData);
649
        }
650
651
        return $response;
652
    }
653
654
    /**
655
     * Get currently configured synonyms
656
     *
657
     * @param string $baseWord If given a base word, retrieves the synonyms for that word only
658
     * @return array
659
     */
660
    public function getSynonyms($baseWord = '')
661
    {
662
        $synonymsUrl = $this->_synonymsUrl;
663
        if (!empty($baseWord)) {
664
            $synonymsUrl .= '/' . $baseWord;
665
        }
666
667
        $response = $this->_sendRawGet($synonymsUrl);
668
        $decodedResponse = json_decode($response->getRawResponse());
669
670
        $synonyms = array();
671
        if (!empty($baseWord)) {
672
            if (is_array($decodedResponse->{$baseWord})) {
673
                $synonyms = $decodedResponse->{$baseWord};
674
            }
675
        } else {
676
            if (isset($decodedResponse->synonymMappings->managedMap)) {
677
                $synonyms = (array)$decodedResponse->synonymMappings->managedMap;
678
            }
679
        }
680
681
        return $synonyms;
682
    }
683
684
    /**
685
     * Add list of synonyms for base word to managed synonyms map
686
     *
687
     * @param $baseWord
688
     * @param array $synonyms
689
     *
690
     * @return \Apache_Solr_Response
691
     *
692
     * @throws \Apache_Solr_InvalidArgumentException If $baseWord or $synonyms are empty
693
     */
694
    public function addSynonym($baseWord, array $synonyms)
695
    {
696
        if (empty($baseWord) || empty($synonyms)) {
697
            throw new \Apache_Solr_InvalidArgumentException('Must provide base word and synonyms.');
698
        }
699
700
        $rawPut = json_encode(array($baseWord => $synonyms));
701
        return $this->_sendRawPost($this->_synonymsUrl, $rawPut,
702
            $this->getHttpTransport()->getDefaultTimeout(), 'application/json');
703
    }
704
705
    /**
706
     * Remove a synonym from the synonyms map
707
     *
708
     * @param $baseWord
709
     * @return \Apache_Solr_Response
710
     * @throws \Apache_Solr_InvalidArgumentException
711
     */
712
    public function deleteSynonym($baseWord)
713
    {
714
        if (empty($baseWord)) {
715
            throw new \Apache_Solr_InvalidArgumentException('Must provide base word.');
716
        }
717
718
        return $this->_sendRawDelete($this->_synonymsUrl . '/' . $baseWord);
719
    }
720
721
    /**
722
     * Central method for making a HTTP DELETE operation against the Solr server
723
     *
724
     * @param $url
725
     * @param bool|float $timeout Read timeout in seconds
726
     * @return \Apache_Solr_Response
727
     */
728
    protected function _sendRawDelete($url, $timeout = false)
729
    {
730
        $logSeverity = 0; // info
731
732
        try {
733
            $httpTransport = $this->getHttpTransport();
734
735
            $httpResponse = $httpTransport->performDeleteRequest($url,
736
                $timeout);
737
            $solrResponse = new \Apache_Solr_Response($httpResponse,
738
                $this->_createDocuments, $this->_collapseSingleValueArrays);
739
740
            if ($solrResponse->getHttpStatus() != 200) {
741
                throw new \Apache_Solr_HttpTransportException($solrResponse);
742
            }
743
        } 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...
744
            $logSeverity = 3; // fatal error
745
            $solrResponse = $e->getResponse();
746
        }
747
748
        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...
749
            $logData = array(
750
                'query url' => $url,
751
                'response' => (array)$solrResponse
752
            );
753
754
            if (!empty($e)) {
755
                $logData['exception'] = $e->__toString();
756
            } else {
757
                // trigger data parsing
758
                $solrResponse->response;
759
                $logData['response data'] = print_r($solrResponse, true);
760
            }
761
762
            GeneralUtility::devLog('Querying Solr using DELETE', 'solr',
763
                $logSeverity, $logData);
764
        }
765
766
        return $solrResponse;
767
    }
768
769
    /**
770
     * Get currently configured stop words
771
     *
772
     * @return array
773
     */
774
    public function getStopWords()
775
    {
776
        $stopWords = array();
777
778
        $response = $this->_sendRawGet($this->_stopWordsUrl);
779
        $decodedResponse = json_decode($response->getRawResponse());
780
781
        if (isset($decodedResponse->wordSet->managedList)) {
782
            $stopWords = (array)$decodedResponse->wordSet->managedList;
783
        }
784
785
        return $stopWords;
786
    }
787
788
    /**
789
     * Adds stop words to the managed stop word list
790
     *
791
     * @param array|string $stopWords string for a single word, array for multiple words
792
     * @return \Apache_Solr_Response
793
     * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty
794
     */
795
    public function addStopWords($stopWords)
796
    {
797
        if (empty($stopWords)) {
798
            throw new \Apache_Solr_InvalidArgumentException('Must provide stop word.');
799
        }
800
801
        if (is_string($stopWords)) {
802
            $stopWords = array($stopWords);
803
        }
804
805
        $stopWords = array_values($stopWords);
806
        $rawPut = json_encode($stopWords);
807
        return $this->_sendRawPost($this->_stopWordsUrl, $rawPut,
808
            $this->getHttpTransport()->getDefaultTimeout(), 'application/json');
809
    }
810
811
    /**
812
     * Deletes a words from the managed stop word list
813
     *
814
     * @param string $stopWord stop word to delete
815
     * @return \Apache_Solr_Response
816
     * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty
817
     */
818
    public function deleteStopWord($stopWord)
819
    {
820
        if (empty($stopWord)) {
821
            throw new \Apache_Solr_InvalidArgumentException('Must provide stop word.');
822
        }
823
824
        return $this->_sendRawDelete($this->_stopWordsUrl . '/' . $stopWord);
825
    }
826
827
    /**
828
     * Reloads the current core
829
     *
830
     * @return \Apache_Solr_Response
831
     */
832
    public function reloadCore()
833
    {
834
        $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...
835
        $coreAdminReloadUrl = $this->_coresUrl . '?action=reload&core=' . $coreName;
836
837
        return $this->_sendRawGet($coreAdminReloadUrl);
838
    }
839
840
    /**
841
     * initializes various URLs, including the Luke URL
842
     *
843
     * @return void
844
     */
845
    protected function _initUrls()
846
    {
847
        parent::_initUrls();
848
849
        $this->_lukeUrl = $this->_constructUrl(
850
            self::LUKE_SERVLET,
851
            array(
852
                'numTerms' => '0',
853
                'wt' => self::SOLR_WRITER
854
            )
855
        );
856
857
        $this->_pluginsUrl = $this->_constructUrl(
858
            self::PLUGINS_SERVLET,
859
            array('wt' => self::SOLR_WRITER)
860
        );
861
862
        $pathElements = explode('/', trim($this->_path, '/'));
863
        $this->_coresUrl =
864
            $this->_scheme . '://' .
865
            $this->_host . ':' .
866
            $this->_port . '/' .
867
            $pathElements[0] . '/' .
868
            self::CORES_SERVLET;
869
870
        $this->_schemaUrl = $this->_constructUrl(self::SCHEMA_SERVLET);
871
872
        $managedLanguage = $this->getManagedLanguage();
873
        $this->_synonymsUrl = $this->_constructUrl(
874
                self::SYNONYMS_SERVLET
875
            ) . $managedLanguage;
876
        $this->_stopWordsUrl = $this->_constructUrl(
877
                self::STOPWORDS_SERVLET
878
            ) . $managedLanguage;
879
    }
880
881
    /**
882
     * Get the language map name for the text field.
883
     * This is necessary to select the correct synonym map.
884
     *
885
     * @return string
886
     */
887
    protected function getManagedLanguage()
888
    {
889
        $language = 'english';
890
891
        $schema = $this->getSchema();
892
893
        if (is_object($schema) && isset($schema->fieldTypes)) {
894
            foreach ($schema->fieldTypes as $fieldType) {
895
                if ($fieldType->name === 'text') {
896
                    foreach ($fieldType->queryAnalyzer->filters as $filter) {
897
                        if ($filter->class === 'solr.ManagedSynonymFilterFactory') {
898
                            $language = $filter->managed;
899
                        }
900
                    }
901
                }
902
            }
903
        }
904
905
        return $language;
906
    }
907
908
    /**
909
     * Get the configured schema for the current core
910
     *
911
     * @return \stdClass
912
     */
913
    protected function getSchema()
914
    {
915
        $response = $this->_sendRawGet($this->_schemaUrl);
916
        return json_decode($response->getRawResponse())->schema;
917
    }
918
}
919