Passed
Push — release-11.5.x ( 39fc07...8ccd81 )
by Markus
34:52 queued 29:33
created

AbstractStrategy   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 163
Duplicated Lines 0 %

Test Coverage

Coverage 92.59%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 17
eloc 49
c 3
b 0
f 0
dl 0
loc 163
ccs 50
cts 54
cp 0.9259
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A deleteRecordInAllSolrConnections() 0 30 4
A __construct() 0 6 1
A removeGarbageOf() 0 4 1
A deleteInSolrAndRemoveFromIndexQueue() 0 4 1
A deleteIndexDocuments() 0 20 5
A callPostProcessGarbageCollectorHook() 0 15 4
A deleteInSolrAndUpdateIndexQueue() 0 4 1
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace ApacheSolrForTypo3\Solr\Domain\Index\Queue\GarbageRemover;
17
18
use ApacheSolrForTypo3\Solr\ConnectionManager;
19
use ApacheSolrForTypo3\Solr\GarbageCollectorPostProcessor;
20
use ApacheSolrForTypo3\Solr\IndexQueue\Queue;
21
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
22
use ApacheSolrForTypo3\Solr\System\Solr\SolrConnection;
23
use InvalidArgumentException;
24
use TYPO3\CMS\Core\Utility\GeneralUtility;
25
26
/**
27
 * An implementation ob a garbage remover strategy is responsible to remove all garbage from the index queue and
28
 * the solr server for a certain table and uid combination.
29
 */
30
abstract class AbstractStrategy
31
{
32
    /**
33
     * @var Queue
34
     */
35
    protected Queue $queue;
36
37
    /**
38
     * @var ConnectionManager
39
     */
40
    protected ConnectionManager $connectionManager;
41
42
    /**
43
     * AbstractStrategy constructor.
44
     * @param Queue|null $queue
45
     * @param ConnectionManager|null $connectionManager
46
     */
47 25
    public function __construct(
48
        Queue $queue = null,
49
        ConnectionManager $connectionManager = null
50
    ) {
51 25
        $this->queue = $queue ?? GeneralUtility::makeInstance(Queue::class);
52 25
        $this->connectionManager = $connectionManager ?? GeneralUtility::makeInstance(ConnectionManager::class);
53
    }
54
55
    /**
56
     * Call's the removal of the strategy and afterwards the garbage-collector post-processing hook.
57
     *
58
     * @param string $table
59
     * @param int $uid
60
     */
61 25
    public function removeGarbageOf(string $table, int $uid)
62
    {
63 25
        $this->removeGarbageOfByStrategy($table, $uid);
64 25
        $this->callPostProcessGarbageCollectorHook($table, $uid);
65
    }
66
67
    /**
68
     * An implementation of the GarbageCollection strategy is responsible to remove the garbage from
69
     * the indexqueue and from the solr server.
70
     *
71
     * @param string $table
72
     * @param int $uid
73
     */
74
    abstract protected function removeGarbageOfByStrategy(string $table, int $uid);
75
76
    /**
77
     * Deletes a document from solr and from the index queue.
78
     *
79
     * @param string $table
80
     * @param int $uid
81
     */
82 14
    protected function deleteInSolrAndRemoveFromIndexQueue(string $table, int $uid)
83
    {
84 14
        $this->deleteIndexDocuments($table, $uid);
85 14
        $this->queue->deleteItem($table, $uid);
86
    }
87
88
    /**
89
     * Deletes a document from solr and updates the item in the index queue (e.g. on page content updates).
90
     *
91
     * @param string $table
92
     * @param int $uid
93
     */
94 11
    protected function deleteInSolrAndUpdateIndexQueue(string $table, int $uid)
95
    {
96 11
        $this->deleteIndexDocuments($table, $uid);
97 11
        $this->queue->updateItem($table, $uid);
98
    }
99
100
    /**
101
     * Deletes index documents for a given record identification.
102
     *
103
     * @param string $table The record's table name.
104
     * @param int $uid The record's uid.
105
     */
106 25
    protected function deleteIndexDocuments(string $table, int $uid, int $language = 0)
107
    {
108
        // record can be indexed for multiple sites
109 25
        $indexQueueItems = $this->queue->getItems($table, $uid);
110 25
        foreach ($indexQueueItems as $indexQueueItem) {
111
            try {
112 21
                $site = $indexQueueItem->getSite();
113 1
            } catch (InvalidArgumentException $e) {
114 1
                $this->queue->deleteItem($indexQueueItem->getType(), $indexQueueItem->getIndexQueueUid());
115 1
                continue;
116
            }
117
118 20
            $enableCommitsSetting = $site->getSolrConfiguration()->getEnableCommits();
119 20
            $siteHash = $site->getSiteHash();
120
            // a site can have multiple connections (cores / languages)
121 20
            $solrConnections = $this->connectionManager->getConnectionsBySite($site);
122 20
            if ($language > 0 && isset($solrConnections[$language])) {
123
                $solrConnections = [$language => $solrConnections[$language]];
124
            }
125 20
            $this->deleteRecordInAllSolrConnections($table, $uid, $solrConnections, $siteHash, $enableCommitsSetting);
126
        }
127
    }
128
129
    /**
130
     * Deletes the record in all solr connections from that site.
131
     *
132
     * @param string $table
133
     * @param int $uid
134
     * @param SolrConnection[] $solrConnections
135
     * @param string $siteHash
136
     * @param bool $enableCommitsSetting
137
     */
138 26
    protected function deleteRecordInAllSolrConnections(
139
        string $table,
140
        int $uid,
141
        array $solrConnections,
142
        string $siteHash,
143
        bool $enableCommitsSetting
144
    ) {
145 26
        foreach ($solrConnections as $solr) {
146 26
            $query = 'type:' . $table . ' AND uid:' . $uid . ' AND siteHash:' . $siteHash;
147 26
            $response = $solr->getWriteService()->deleteByQuery($query);
148
149 26
            if ($response->getHttpStatus() !== 200) {
150 2
                $logger = GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
151 2
                $logger->log(
152 2
                    SolrLogManager::ERROR,
153 2
                    'Couldn\'t delete index document',
154 2
                    [
155 2
                        'status' => $response->getHttpStatus(),
156 2
                        'msg' => $response->getHttpStatusMessage(),
157 2
                        'core' => $solr->getWriteService()->getCorePath(),
158 2
                        'query' => $query,
159 2
                    ]
160 2
                );
161
162
                // @todo: Ensure index is updated later on, e.g. via a new index queue status
163 2
                continue;
164
            }
165
166 24
            if ($enableCommitsSetting) {
167 22
                $solr->getWriteService()->commit(false, false);
168
            }
169
        }
170
    }
171
172
    /**
173
     * Calls the registered post-processing hooks after the garbageCollection.
174
     *
175
     * @param string $table
176
     * @param int $uid
177
     */
178 25
    protected function callPostProcessGarbageCollectorHook(string $table, int $uid)
179
    {
180 25
        if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessGarbageCollector'] ?? null)) {
181 23
            return;
182
        }
183
184 2
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessGarbageCollector'] as $classReference) {
185 2
            $garbageCollectorPostProcessor = GeneralUtility::makeInstance($classReference);
186
187 2
            if ($garbageCollectorPostProcessor instanceof GarbageCollectorPostProcessor) {
188 2
                $garbageCollectorPostProcessor->postProcessGarbageCollector($table, $uid);
189
            } else {
190
                $message = get_class($garbageCollectorPostProcessor) . ' must implement interface ' .
191
                    GarbageCollectorPostProcessor::class;
192
                throw new \UnexpectedValueException($message, 1345807460);
193
            }
194
        }
195
    }
196
}
197