Completed
Push — master ( 324c2f...3e3e29 )
by Timo
47s
created

SolrService   F

Complexity

Total Complexity 91

Size/Duplication

Total Lines 931
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 5

Test Coverage

Coverage 51.85%

Importance

Changes 0
Metric Value
wmc 91
lcom 4
cbo 5
dl 0
loc 931
ccs 153
cts 295
cp 0.5185
rs 1.263
c 0
b 0
f 0

37 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 2
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 15 4
B extractByQuery() 0 31 2
C requestServlet() 0 41 7
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
B _sendRawGet() 0 31 5
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
B _sendRawDelete() 0 40 6
A getStopWords() 0 13 2
A addStopWords() 0 15 3
A deleteStopWord() 0 8 2
A reloadCore() 0 7 1
B _initUrls() 0 36 1
B getManagedLanguage() 0 20 7
A getSchema() 0 5 1

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