Passed
Push — master ( 20d9ae...2c204f )
by Timo
01:45
created

SolrService::deleteSynonym()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 4
cts 5
cp 0.8
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
crap 2.032
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 ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
29
use ApacheSolrForTypo3\Solr\System\Solr\Parser\SchemaParser;
30
use ApacheSolrForTypo3\Solr\System\Solr\Parser\StopWordParser;
31
use ApacheSolrForTypo3\Solr\System\Solr\Parser\SynonymParser;
32
use ApacheSolrForTypo3\Solr\System\Solr\Schema\Schema;
33
use TYPO3\CMS\Core\Utility\GeneralUtility;
34
use Apache_Solr_HttpTransportException;
35
36
/**
37
 * Solr Service Access
38
 *
39
 * @author Ingo Renner <[email protected]>
40
 */
41
class SolrService extends \Apache_Solr_Service
42
{
43
    const LUKE_SERVLET = 'admin/luke';
44
    const SYSTEM_SERVLET = 'admin/system';
45
    const PLUGINS_SERVLET = 'admin/plugins';
46
    const CORES_SERVLET = 'admin/cores';
47
    const SCHEMA_SERVLET = 'schema';
48
    const SYNONYMS_SERVLET = 'schema/analysis/synonyms/';
49
    const STOPWORDS_SERVLET = 'schema/analysis/stopwords/';
50
51
    const SCHEME_HTTP = 'http';
52
    const SCHEME_HTTPS = 'https';
53
54
    /**
55
     * Server connection scheme. http or https.
56
     *
57
     * @var string
58
     */
59
    protected $_scheme = self::SCHEME_HTTP;
60
61
    /**
62
     * Constructed servlet URL for Luke
63
     *
64
     * @var string
65
     */
66
    protected $_lukeUrl;
67
68
    /**
69
     * Constructed servlet URL for plugin information
70
     *
71
     * @var string
72
     */
73
    protected $_pluginsUrl;
74
75
    /**
76
     * @var string
77
     */
78
    protected $_coresUrl;
79
80
    /**
81
     * @var string
82
     */
83
    protected $_extractUrl;
84
85
    /**
86
     * @var string
87
     */
88
    protected $_synonymsUrl;
89
90
    /**
91
     * @var string
92
     */
93
    protected $_stopWordsUrl;
94
95
    /**
96
     * @var string
97
     */
98
    protected $_schemaUrl;
99
100
    /**
101
     * @var bool
102
     */
103
    protected $debug = false;
104
105
    /**
106
     * @var \Apache_Solr_Response
107
     */
108
    protected $responseCache = null;
109
110
    /**
111
     * @var bool
112
     */
113
    protected $hasSearched = false;
114
115
    /**
116
     * @var array
117
     */
118
    protected $lukeData = [];
119
120
    protected $systemData = null;
121
    protected $pluginsData = null;
122
123
    protected $solrconfigName = null;
124
125
    /**
126
     * @var TypoScriptConfiguration
127
     */
128
    protected $configuration;
129
130
    /**
131
     * @var array
132
     */
133
    protected static $pingCache = [];
134
135
    /**
136
     * @var SynonymParser
137
     */
138
    protected $synonymParser = null;
139
140
    /**
141
     * @var StopWordParser
142
     */
143
    protected $stopWordParser = null;
144
145
    /**
146
     * @var SchemaParser
147
     */
148
    protected $schemaParser = null;
149
150
    /**
151
     * @var Schema
152
     */
153
    protected $schema;
154
155
    /**
156
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager
157
     */
158
    protected $logger = null;
159
160
    /**
161
     * Constructor
162
     *
163
     * @param string $host Solr host
164
     * @param string $port Solr port
165
     * @param string $path Solr path
166
     * @param string $scheme Scheme, defaults to http, can be https
167
     * @param TypoScriptConfiguration $typoScriptConfiguration
168
     * @param SynonymParser $synonymParser
169
     * @param StopWordParser $stopWordParser
170
     * @param SchemaParser $schemaParser
171
     */
172 102
    public function __construct(
173
        $host = '',
174
        $port = '8983',
175
        $path = '/solr/',
176
        $scheme = 'http',
177
        TypoScriptConfiguration $typoScriptConfiguration = null,
178
        SynonymParser $synonymParser = null,
179
        StopWordParser $stopWordParser = null,
180
        SchemaParser $schemaParser = null
181
    ) {
182 102
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__);
183
184 102
        $this->setScheme($scheme);
185 101
        $this->configuration = is_null($typoScriptConfiguration) ? Util::getSolrConfiguration() : $typoScriptConfiguration;
186 101
        $this->synonymParser = is_null($synonymParser) ? GeneralUtility::makeInstance(SynonymParser::class) : $synonymParser;
187 101
        $this->stopWordParser = is_null($stopWordParser) ? GeneralUtility::makeInstance(StopWordParser::class) : $stopWordParser;
188 101
        $this->schemaParser = is_null($schemaParser) ? GeneralUtility::makeInstance(SchemaParser::class) : $schemaParser;
189
190 101
        $this->initializeTimeoutFromConfiguration();
191
192 101
        parent::__construct($host, $port, $path);
193 101
    }
194
195
    /**
196
     * Initializes the timeout from TypoScript when configuration is present.
197
     *
198
     * @return void
199
     */
200 101
    protected function initializeTimeoutFromConfiguration()
201
    {
202 101
        $timeout = $this->configuration->getSolrTimeout();
203 101
        if ($timeout > 0) {
204 1
            $this->getHttpTransport()->setDefaultTimeout($timeout);
205
        }
206 101
    }
207
208
    /**
209
     * Creates a string representation of the Solr connection. Specifically
210
     * will return the Solr URL.
211
     *
212
     * @return string The Solr URL.
213
     */
214 60
    public function __toString()
215
    {
216 60
        return $this->_scheme . '://' . $this->_host . ':' . $this->_port . $this->_path;
217
    }
218
219
    /**
220
     * Returns the current time in milliseconds.
221
     *
222
     * @return double
223
     */
224 3
    protected function getMilliseconds()
225
    {
226 3
        return GeneralUtility::milliseconds();
227
    }
228
229
    /**
230
     * Performs a search.
231
     *
232
     * @param string $query query string / search term
233
     * @param int $offset result offset for pagination
234
     * @param int $limit number of results to retrieve
235
     * @param array $params additional HTTP GET parameters
236
     * @param string $method The HTTP method (Apache_Solr_Service::METHOD_GET or Apache_Solr_Service::METHOD::POST)
237
     * @return \Apache_Solr_Response Solr response
238
     * @throws \RuntimeException if Solr returns a HTTP status code other than 200
239
     */
240 30
    public function search($query, $offset = 0, $limit = 10, $params = [], $method = self::METHOD_GET)
241
    {
242 30
        $response = parent::search($query, $offset, $limit, $params, $method);
243 30
        $this->hasSearched = true;
244
245 30
        $this->responseCache = $response;
246
247 30
        if ($response->getHttpStatus() != 200) {
248 1
            throw new \RuntimeException(
249
                'Invalid query. Solr returned an error: '
250 1
                . $response->getHttpStatus() . ' '
251 1
                . $response->getHttpStatusMessage(),
252 1
                1293109870
253
            );
254
        }
255
256 29
        return $response;
257
    }
258
259
    /**
260
     * Call the /admin/ping servlet, can be used to quickly tell if a connection to the
261
     * server is available.
262
     *
263
     * Simply overrides the SolrPhpClient implementation, changing ping from a
264
     * HEAD to a GET request, see http://forge.typo3.org/issues/44167
265
     *
266
     * Also does not report the time, see https://forge.typo3.org/issues/64551
267
     *
268
     * @param int $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
269
     * @param boolean $useCache indicates if the ping result should be cached in the instance or not
270
     * @return bool TRUE if Solr can be reached, FALSE if not
271
     */
272 55
    public function ping($timeout = 2, $useCache = true)
273
    {
274 55
        $httpResponse = $this->performPingRequest($timeout, $useCache);
275 55
        return ($httpResponse->getStatusCode() === 200);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $httpResponse->getStatusCode() === 200; (boolean) is incompatible with the return type of the parent method Apache_Solr_Service::ping of type double|false.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
276
    }
277
278
    /**
279
     * Call the /admin/ping servlet, can be used to get the runtime of a ping request.
280
     *
281
     * @param int $timeout maximum time to wait for ping in seconds, -1 for unlimited (default is 2)
282
     * @param boolean $useCache indicates if the ping result should be cached in the instance or not
283
     * @return double runtime in milliseconds
284
     * @throws \ApacheSolrForTypo3\Solr\PingFailedException
285
     */
286 3
    public function getPingRoundTripRuntime($timeout = 2, $useCache = true)
287
    {
288 3
        $start = $this->getMilliseconds();
289 3
        $httpResponse = $this->performPingRequest($timeout, $useCache);
290 3
        $end = $this->getMilliseconds();
291
292 3
        if ($httpResponse->getStatusCode() !== 200) {
293 1
            $message = 'Solr ping failed with unexpected response code: ' . $httpResponse->getStatusCode();
294
            /** @var $exception \ApacheSolrForTypo3\Solr\PingFailedException */
295 1
            $exception = GeneralUtility::makeInstance(PingFailedException::class, $message);
296 1
            $exception->setHttpResponse($httpResponse);
297 1
            throw $exception;
298
        }
299
300 2
        return $end - $start;
301
    }
302
303
    /**
304
     * Performs a ping request and returns the result.
305
     *
306
     * @param int $timeout
307
     * @param boolean $useCache indicates if the ping result should be cached in the instance or not
308
     * @return \Apache_Solr_HttpTransport_Response
309
     */
310 58
    protected function performPingRequest($timeout = 2, $useCache = true)
311
    {
312 58
        $cacheKey = (string)($this);
313 58
        if ($useCache && isset(static::$pingCache[$cacheKey])) {
314 46
            return static::$pingCache[$cacheKey];
315
        }
316
317 58
        $pingResult = $this->getHttpTransport()->performGetRequest($this->_pingUrl, $timeout);
318
319 58
        if ($useCache) {
320 57
            static::$pingCache[$cacheKey] = $pingResult;
321
        }
322
323 58
        return $pingResult;
324
    }
325
326
    /**
327
     * Performs a content and meta data extraction request.
328
     *
329
     * @param ExtractingQuery $query An extraction query
330
     * @return array An array containing the extracted content [0] and meta data [1]
331
     */
332 1
    public function extractByQuery(ExtractingQuery $query)
333
    {
334
        $headers = [
335 1
            'Content-Type' => 'multipart/form-data; boundary=' . $query->getMultiPartPostDataBoundary()
336
        ];
337
338
        try {
339 1
            $response = $this->requestServlet(
340 1
                self::EXTRACT_SERVLET,
341 1
                $query->getQueryParameters(),
342 1
                'POST',
343
                $headers,
344 1
                $query->getRawPostFileData()
345
            );
346
        } catch (\Exception $e) {
347
            $this->logger->log(
348
                SolrLogManager::ERROR,
349
                'Extracting text and meta data through Solr Cell over HTTP POST',
350
                [
351
                    'query' => (array)$query,
352
                    'parameters' => $query->getQueryParameters(),
353
                    'file' => $query->getFile(),
354
                    'headers' => $headers,
355
                    'query url' => self::EXTRACT_SERVLET,
356
                    'exception' => $e->getMessage()
357
                ]
358
            );
359
        }
360
361
        return [
362 1
            $response->extracted,
363 1
            (array)$response->extracted_metadata
364
        ];
365
    }
366
367
    /**
368
     * Make a request to a servlet (a path) that's not a standard path.
369
     *
370
     * @param string $servlet Path to be added to the base Solr path.
371
     * @param array $parameters Optional, additional request parameters when constructing the URL.
372
     * @param string $method HTTP method to use, defaults to GET.
373
     * @param array $requestHeaders Key value pairs of header names and values. Should include 'Content-Type' for POST and PUT.
374
     * @param string $rawPost Must be an empty string unless method is POST or PUT.
375
     * @param float|bool $timeout Read timeout in seconds, defaults to FALSE.
376
     * @return \Apache_Solr_Response Response object
377
     * @throws \Apache_Solr_HttpTransportException if returned HTTP status is other than 200
378
     */
379 1
    public function requestServlet($servlet, $parameters = [], $method = 'GET', $requestHeaders = [], $rawPost = '', $timeout = false)
380
    {
381
        // Add default parameters
382 1
        $parameters['wt'] = self::SOLR_WRITER;
383 1
        $parameters['json.nl'] = $this->_namedListTreatment;
384 1
        $url = $this->_constructUrl($servlet, $parameters);
385
386 1
        $httpResponse = $this->getResponseFromTransport($url, $method, $requestHeaders, $rawPost, $timeout);
387 1
        $solrResponse = new \Apache_Solr_Response($httpResponse, $this->_createDocuments, $this->_collapseSingleValueArrays);
388 1
        if ($solrResponse->getHttpStatus() != 200) {
389
            throw new \Apache_Solr_HttpTransportException($solrResponse);
390
        }
391
392 1
        return $solrResponse;
393
    }
394
395
    /**
396
     * Decides which transport method to used, depending on the request method and retrieves the response.
397
     *
398
     * @param string $url
399
     * @param string $method
400
     * @param array $requestHeaders
401
     * @param string $rawPost
402
     * @param float|bool $timeout
403
     * @return \Apache_Solr_HttpTransport_Response
404
     */
405 1
    protected function getResponseFromTransport($url, $method, $requestHeaders, $rawPost, $timeout)
406
    {
407 1
        $httpTransport = $this->getHttpTransport();
408
409 1
        if ($method == self::METHOD_GET) {
410
            return $httpTransport->performGetRequest($url, $timeout);
411
        }
412 1
        if ($method == self::METHOD_POST) {
413
            // FIXME should respect all headers, not only Content-Type
414 1
            return $httpTransport->performPostRequest($url, $rawPost, $requestHeaders['Content-Type'], $timeout);
415
        }
416
417
        throw new \InvalidArgumentException('$method should be GET or POST');
418
    }
419
420
    /**
421
     * Return a valid http URL given this server's scheme, host, port, and path
422
     * and a provided servlet name.
423
     *
424
     * @param string $servlet Servlet name
425
     * @param array $params Additional URL parameters to attach to the end of the URL
426
     * @return string Servlet URL
427
     */
428 101
    protected function _constructUrl($servlet, $params = [])
429
    {
430 101
        $url = parent::_constructUrl($servlet, $params);
431
432 101
        if (!GeneralUtility::isFirstPartOfStr($url, $this->_scheme)) {
433 2
            $parsedUrl = parse_url($url);
434
435
            // unfortunately can't use str_replace as it replace all
436
            // occurrences of $needle and can't be limited to replace only once
437 2
            $url = $this->_scheme . substr($url, strlen($parsedUrl['scheme']));
438
        }
439
440 101
        return $url;
441
    }
442
443
    /**
444
     * Returns the set scheme
445
     *
446
     * @return string
447
     */
448 3
    public function getScheme()
449
    {
450 3
        return $this->_scheme;
451
    }
452
453
    /**
454
     * Set the scheme used. If empty will fallback to constants
455
     *
456
     * @param string $scheme Either http or https
457
     * @throws \UnexpectedValueException
458
     */
459 102
    public function setScheme($scheme)
460
    {
461
        // Use the provided scheme or use the default
462 102
        if (empty($scheme)) {
463 2
            throw new \UnexpectedValueException('Scheme parameter is empty', 1380756390);
464
        }
465
466 101
        $isHttpOrHttps = in_array($scheme, [self::SCHEME_HTTP, self::SCHEME_HTTPS]);
467 101
        if (!$isHttpOrHttps) {
468 1
            throw new \UnexpectedValueException('Unsupported scheme parameter, scheme must be http or https', 1380756442);
469
        }
470
471
        // we have a valid scheme
472 101
        $this->_scheme = $scheme;
473
474 101
        if ($this->_urlsInited) {
475 1
            $this->_initUrls();
476
        }
477 101
    }
478
479
    /**
480
     * get field meta data for the index
481
     *
482
     * @param int $numberOfTerms Number of top terms to fetch for each field
483
     * @return array
484
     */
485
    public function getFieldsMetaData($numberOfTerms = 0)
486
    {
487
        return $this->getLukeMetaData($numberOfTerms)->fields;
488
    }
489
490
    /**
491
     * Retrieves meta data about the index from the luke request handler
492
     *
493
     * @param int $numberOfTerms Number of top terms to fetch for each field
494
     * @return \Apache_Solr_Response Index meta data
495
     */
496
    public function getLukeMetaData($numberOfTerms = 0)
497
    {
498
        if (!isset($this->lukeData[$numberOfTerms])) {
499
            $lukeUrl = $this->_constructUrl(
500
                self::LUKE_SERVLET,
501
                [
502
                    'numTerms' => $numberOfTerms,
503
                    'wt' => self::SOLR_WRITER,
504
                    'fl' => '*'
505
                ]
506
            );
507
508
            $this->lukeData[$numberOfTerms] = $this->_sendRawGet($lukeUrl);
509
        }
510
511
        return $this->lukeData[$numberOfTerms];
512
    }
513
514
    /**
515
     * Central method for making a get operation against this Solr Server
516
     *
517
     * @param string $url
518
     * @param float|bool $timeout Read timeout in seconds
519
     * @return \Apache_Solr_Response
520
     */
521 44
    protected function _sendRawGet($url, $timeout = false)
522
    {
523 44
        $logSeverity = SolrLogManager::INFO;
524 44
        $exception = null;
525
526
        try {
527 44
            $response = parent::_sendRawGet($url, $timeout);
0 ignored issues
show
Bug introduced by
It seems like $timeout defined by parameter $timeout on line 521 can also be of type boolean; however, Apache_Solr_Service::_sendRawGet() does only seem to accept false|double, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
528 4
        } catch (\Apache_Solr_HttpTransportException $exception) {
529 4
            $logSeverity = SolrLogManager::ERROR;
530 4
            $response = $exception->getResponse();
531
        }
532
533 44
        if ($this->configuration->getLoggingQueryRawGet() || $response->getHttpStatus() != 200) {
534 4
            $this->writeLog($logSeverity, 'Querying Solr using GET', $url, $response, $exception);
535
        }
536
537 44
        return $response;
538
    }
539
540
    /**
541
     * Returns whether a search has been executed or not.
542
     *
543
     * @return bool TRUE if a search has been executed, FALSE otherwise
544
     */
545
    public function hasSearched()
546
    {
547
        return $this->hasSearched;
548
    }
549
550
    /**
551
     * Gets the most recent response (if any)
552
     *
553
     * @return \Apache_Solr_Response Most recent response, or NULL if a search has not been executed yet.
554
     */
555 1
    public function getResponse()
556
    {
557 1
        return $this->responseCache;
558
    }
559
560
    /**
561
     * Enable/Disable debug mode
562
     *
563
     * @param bool $debug TRUE to enable debug mode, FALSE to turn off, off by default
564
     */
565
    public function setDebug($debug)
566
    {
567
        $this->debug = (boolean)$debug;
568
    }
569
570
    /**
571
     * Gets information about the plugins installed in Solr
572
     *
573
     * @return array A nested array of plugin data.
574
     */
575 3
    public function getPluginsInformation()
576
    {
577 3
        if (empty($this->pluginsData)) {
578 3
            $pluginsInformation = $this->_sendRawGet($this->_pluginsUrl);
579
580
            // access a random property to trigger response parsing
581 3
            $pluginsInformation->responseHeader;
582 2
            $this->pluginsData = $pluginsInformation;
583
        }
584
585 2
        return $this->pluginsData;
586
    }
587
588
    /**
589
     * Gets information about the Solr server
590
     *
591
     * @return array A nested array of system data.
592
     */
593 5
    public function getSystemInformation()
594
    {
595 5
        if (empty($this->systemData)) {
596 5
            $systemInformation = $this->system();
597
598
            // access a random property to trigger response parsing
599 5
            $systemInformation->responseHeader;
600 4
            $this->systemData = $systemInformation;
601
        }
602
603 4
        return $this->systemData;
604
    }
605
606
    /**
607
     * Gets the name of the solrconfig.xml file installed and in use on the Solr
608
     * server.
609
     *
610
     * @return string Name of the active solrconfig.xml
611
     */
612 3
    public function getSolrconfigName()
613
    {
614 3
        if (is_null($this->solrconfigName)) {
615 3
            $solrconfigXmlUrl = $this->_scheme . '://'
616 3
                . $this->_host . ':' . $this->_port
617 3
                . $this->_path . 'admin/file/?file=solrconfig.xml';
618 3
            $response = $this->_sendRawGet($solrconfigXmlUrl);
619
620 3
            $solrconfigXml = simplexml_load_string($response->getRawResponse());
621 3
            if ($solrconfigXml === false) {
622 1
                throw new \InvalidArgumentException('No valid xml response from schema file: ' . $solrconfigXmlUrl);
623
            }
624 2
            $this->solrconfigName = (string)$solrconfigXml->attributes()->name;
625
        }
626
627 2
        return $this->solrconfigName;
628
    }
629
630
    /**
631
     * Gets the Solr server's version number.
632
     *
633
     * @return string Solr version number
634
     */
635 4
    public function getSolrServerVersion()
636
    {
637 4
        $systemInformation = $this->getSystemInformation();
638
639
        // don't know why $systemInformation->lucene->solr-spec-version won't work
640 3
        $luceneInformation = (array)$systemInformation->lucene;
641 3
        return $luceneInformation['solr-spec-version'];
642
    }
643
644
    /**
645
     * Deletes all index documents of a certain type and does a commit
646
     * afterwards.
647
     *
648
     * @param string $type The type of documents to delete, usually a table name.
649
     * @param bool $commit Will commit immediately after deleting the documents if set, defaults to TRUE
650
     */
651
    public function deleteByType($type, $commit = true)
652
    {
653
        $this->deleteByQuery('type:' . trim($type));
654
655
        if ($commit) {
656
            $this->commit(false, false, false);
657
        }
658
    }
659
660
    /**
661
     * Raw Delete Method. Takes a raw post body and sends it to the update service. Body should be
662
     * a complete and well formed "delete" xml document
663
     *
664
     * @param string $rawPost Expected to be utf-8 encoded xml document
665
     * @param float|int $timeout Maximum expected duration of the delete operation on the server (otherwise, will throw a communication exception)
666
     * @return \Apache_Solr_Response
667
     */
668 11
    public function delete($rawPost, $timeout = 3600)
669
    {
670 11
        $response = $this->_sendRawPost($this->_updateUrl, $rawPost, $timeout);
671
672 11
        $this->logger->log(
673 11
            SolrLogManager::NOTICE,
674 11
            'Delete Query sent.',
675
            [
676 11
                'query' => $rawPost,
677 11
                'query url' => $this->_updateUrl,
678 11
                'response' => (array)$response
679
            ]
680
        );
681
682 11
        return $response;
683
    }
684
685
    /**
686
     * Central method for making a post operation against this Solr Server
687
     *
688
     * @param string $url
689
     * @param string $rawPost
690
     * @param float|bool $timeout Read timeout in seconds
691
     * @param string $contentType
692
     * @return \Apache_Solr_Response
693
     */
694 69
    protected function _sendRawPost($url, $rawPost, $timeout = false, $contentType = 'text/xml; charset=UTF-8')
695
    {
696 69
        $logSeverity = SolrLogManager::INFO;
697 69
        $exception = null;
698
699
        try {
700 69
            $response = parent::_sendRawPost($url, $rawPost, $timeout, $contentType);
0 ignored issues
show
Bug introduced by
It seems like $timeout defined by parameter $timeout on line 694 can also be of type boolean; however, Apache_Solr_Service::_sendRawPost() does only seem to accept false|double, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
701
        } catch (\Apache_Solr_HttpTransportException $exception) {
702
            $logSeverity = SolrLogManager::ERROR;
703
            $response = $exception->getResponse();
704
        }
705
706 69
        if ($this->configuration->getLoggingQueryRawPost() || $response->getHttpStatus() != 200) {
707
            $this->writeLog($logSeverity, 'Querying Solr using POST', $url, $response, $exception, $rawPost);
708
        }
709
710 69
        return $response;
711
    }
712
713
    /**
714
     * Get currently configured synonyms
715
     *
716
     * @param string $baseWord If given a base word, retrieves the synonyms for that word only
717
     * @return array
718
     */
719 2
    public function getSynonyms($baseWord = '')
720
    {
721 2
        $this->initializeSynonymsUrl();
722 2
        $synonymsUrl = $this->_synonymsUrl;
723 2
        if (!empty($baseWord)) {
724 2
            $synonymsUrl .= '/' . $baseWord;
725
        }
726
727 2
        $response = $this->_sendRawGet($synonymsUrl);
728 2
        return $this->synonymParser->parseJson($baseWord, $response->getRawResponse());
729
    }
730
731
    /**
732
     * Add list of synonyms for base word to managed synonyms map
733
     *
734
     * @param string $baseWord
735
     * @param array $synonyms
736
     *
737
     * @return \Apache_Solr_Response
738
     *
739
     * @throws \Apache_Solr_InvalidArgumentException If $baseWord or $synonyms are empty
740
     */
741 2
    public function addSynonym($baseWord, array $synonyms)
742
    {
743 2
        $this->initializeSynonymsUrl();
744 2
        $json = $this->synonymParser->toJson($baseWord, $synonyms);
745 2
        return $this->_sendRawPost($this->_synonymsUrl, $json,
746 2
            $this->getHttpTransport()->getDefaultTimeout(), 'application/json');
747
    }
748
749
    /**
750
     * Remove a synonym from the synonyms map
751
     *
752
     * @param string $baseWord
753
     * @return \Apache_Solr_Response
754
     * @throws \Apache_Solr_InvalidArgumentException
755
     */
756 2
    public function deleteSynonym($baseWord)
757
    {
758 2
        $this->initializeSynonymsUrl();
759 2
        if (empty($baseWord)) {
760
            throw new \Apache_Solr_InvalidArgumentException('Must provide base word.');
761
        }
762
763 2
        return $this->_sendRawDelete($this->_synonymsUrl . '/' . urlencode($baseWord));
764
    }
765
766
    /**
767
     * Central method for making a HTTP DELETE operation against the Solr server
768
     *
769
     * @param string $url
770
     * @param bool|float $timeout Read timeout in seconds
771
     * @return \Apache_Solr_Response
772
     */
773 4
    protected function _sendRawDelete($url, $timeout = false)
774
    {
775 4
        $logSeverity = SolrLogManager::INFO;
776 4
        $exception = null;
777
778
        try {
779 4
            $httpTransport = $this->getHttpTransport();
780 4
            $httpResponse = $httpTransport->performDeleteRequest($url, $timeout);
781 4
            $solrResponse = new \Apache_Solr_Response($httpResponse, $this->_createDocuments, $this->_collapseSingleValueArrays);
782
783 4
            if ($solrResponse->getHttpStatus() != 200) {
784 4
                throw new \Apache_Solr_HttpTransportException($solrResponse);
785
            }
786 4
        } catch (\Apache_Solr_HttpTransportException $exception) {
787 4
            $logSeverity = SolrLogManager::ERROR;
788 4
            $solrResponse = $exception->getResponse();
789
        }
790
791 4
        if ($this->configuration->getLoggingQueryRawDelete() || $solrResponse->getHttpStatus() != 200) {
792 4
            $this->writeLog($logSeverity, 'Querying Solr using DELETE', $url, $solrResponse, $exception);
793
        }
794
795 4
        return $solrResponse;
796
    }
797
798
    /**
799
     * Build the log data and writes the message to the log
800
     *
801
     * @param integer $logSeverity
802
     * @param string $message
803
     * @param string $url
804
     * @param \Apache_Solr_Response $solrResponse
805
     * @param \Exception $exception
806
     * @param string $contentSend
807
     */
808 6
    protected function writeLog($logSeverity, $message, $url, $solrResponse, $exception = null, $contentSend = '')
809
    {
810 6
        $logData = $this->buildLogDataFromResponse($solrResponse, $exception, $url, $contentSend);
811 6
        $this->logger->log($logSeverity, $message, $logData);
812 6
    }
813
814
    /**
815
     * Parses the solr information to build data for the logger.
816
     *
817
     * @param \Apache_Solr_Response $solrResponse
818
     * @param \Exception $e
819
     * @param string $url
820
     * @param string $contentSend
821
     * @return array
822
     */
823 6
    protected function buildLogDataFromResponse(\Apache_Solr_Response $solrResponse, \Exception $e = null, $url = '', $contentSend = '')
824
    {
825 6
        $logData = ['query url' => $url, 'response' => (array)$solrResponse];
826
827 6
        if ($contentSend !== '') {
828
            $logData['content'] = $contentSend;
829
        }
830
831 6
        if (!empty($e)) {
832 6
            $logData['exception'] = $e->__toString();
833 6
            return $logData;
834
        } else {
835
            // trigger data parsing
836
            $solrResponse->response;
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
837
            $logData['response data'] = print_r($solrResponse, true);
838
            return $logData;
839
        }
840
    }
841
842
    /**
843
     * Get currently configured stop words
844
     *
845
     * @return array
846
     */
847 3
    public function getStopWords()
848
    {
849 3
        $this->initializeStopWordsUrl();
850 3
        $response = $this->_sendRawGet($this->_stopWordsUrl);
851 3
        return $this->stopWordParser->parseJson($response->getRawResponse());
852
    }
853
854
    /**
855
     * Adds stop words to the managed stop word list
856
     *
857
     * @param array|string $stopWords string for a single word, array for multiple words
858
     * @return \Apache_Solr_Response
859
     * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty
860
     */
861 2
    public function addStopWords($stopWords)
862
    {
863 2
        $this->initializeStopWordsUrl();
864 2
        $json = $this->stopWordParser->toJson($stopWords);
865 2
        return $this->_sendRawPost($this->_stopWordsUrl, $json,
866 2
            $this->getHttpTransport()->getDefaultTimeout(), 'application/json');
867
    }
868
869
    /**
870
     * Deletes a words from the managed stop word list
871
     *
872
     * @param string $stopWord stop word to delete
873
     * @return \Apache_Solr_Response
874
     * @throws \Apache_Solr_InvalidArgumentException If $stopWords is empty
875
     */
876 2
    public function deleteStopWord($stopWord)
877
    {
878 2
        $this->initializeStopWordsUrl();
879 2
        if (empty($stopWord)) {
880
            throw new \Apache_Solr_InvalidArgumentException('Must provide stop word.');
881
        }
882
883 2
        return $this->_sendRawDelete($this->_stopWordsUrl . '/' . urlencode($stopWord));
884
    }
885
886
    /**
887
     * Returns the core name from the configured path.
888
     *
889
     * @return string
890
     */
891 3
    public function getCoreName()
892
    {
893 3
        $paths = explode('/', trim($this->_path, '/'));
894
895 3
        return (string)array_pop($paths);
896
    }
897
898
    /**
899
     * Returns the core name from the configured path without the core name.
900
     *
901
     * @return string
902
     */
903 101
    public function getCoreBasePath()
904
    {
905 101
        $pathWithoutLeadingAndTrailingSlashes = trim(trim($this->_path), "/");
906 101
        $pathWithoutLastSegment = substr($pathWithoutLeadingAndTrailingSlashes, 0, strrpos($pathWithoutLeadingAndTrailingSlashes, "/"));
907 101
        return '/' . $pathWithoutLastSegment . '/';
908
    }
909
910
    /**
911
     * Reloads the current core
912
     *
913
     * @return \Apache_Solr_Response
914
     */
915 1
    public function reloadCore()
916
    {
917 1
        return $this->reloadCoreByName($this->getCoreName());
918
    }
919
920
    /**
921
     * Reloads a core of the connection by a given corename.
922
     *
923
     * @param string $coreName
924
     * @return \Apache_Solr_Response
925
     */
926 1
    public function reloadCoreByName($coreName)
927
    {
928 1
        $coreAdminReloadUrl = $this->_coresUrl . '?action=reload&core=' . $coreName;
929 1
        return $this->_sendRawGet($coreAdminReloadUrl);
930
    }
931
932
    /**
933
     * initializes various URLs, including the Luke URL
934
     *
935
     * @return void
936
     */
937 101
    protected function _initUrls()
938
    {
939 101
        parent::_initUrls();
940
941 101
        $this->_lukeUrl = $this->_constructUrl(
942 101
            self::LUKE_SERVLET,
943
            [
944 101
                'numTerms' => '0',
945 101
                'wt' => self::SOLR_WRITER
946
            ]
947
        );
948
949 101
        $this->_pluginsUrl = $this->_constructUrl(
950 101
            self::PLUGINS_SERVLET,
951 101
            ['wt' => self::SOLR_WRITER]
952
        );
953
954 101
        $this->_coresUrl =
955 101
            $this->_scheme . '://' .
956 101
            $this->_host . ':' .
957 101
            $this->_port .
958 101
            $this->getCoreBasePath() .
959 101
            self::CORES_SERVLET;
960
961 101
        $this->_schemaUrl = $this->_constructUrl(self::SCHEMA_SERVLET);
962 101
    }
963
964
    /**
965
     * @return void
966
     */
967 2
    protected function initializeSynonymsUrl()
968
    {
969 2
        if (trim($this->_synonymsUrl) !== '') {
970 2
            return;
971
        }
972 2
        $this->_synonymsUrl = $this->_constructUrl(self::SYNONYMS_SERVLET) . $this->getSchema()->getLanguage();
973 2
    }
974
975
    /**
976
     * @return void
977
     */
978 3
    protected function initializeStopWordsUrl()
979
    {
980 3
        if (trim($this->_stopWordsUrl) !== '') {
981 2
            return;
982
        }
983
984 3
        $this->_stopWordsUrl = $this->_constructUrl(self::STOPWORDS_SERVLET) . $this->getSchema()->getLanguage();
985 3
    }
986
987
    /**
988
     * Get the configured schema for the current core.
989
     *
990
     * @return Schema
991
     */
992 8
    public function getSchema()
993
    {
994 8
        if ($this->schema !== null) {
995
            return $this->schema;
996
        }
997 8
        $response = $this->_sendRawGet($this->_schemaUrl);
998 8
        $this->schema = $this->schemaParser->parseJson($response->getRawResponse());
999 8
        return $this->schema;
1000
    }
1001
}
1002