Passed
Push — release-11.5.x ( 3026d3...1b5a31 )
by Markus
30:33 queued 11:13
created

AbstractStrategy   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Test Coverage

Coverage 92.86%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 18
eloc 51
c 4
b 0
f 0
dl 0
loc 167
ccs 52
cts 56
cp 0.9286
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A removeGarbageOf() 0 4 1
A deleteInSolrAndRemoveFromIndexQueue() 0 4 1
A deleteInSolrAndUpdateIndexQueue() 0 4 1
A deleteRecordInAllSolrConnections() 0 30 4
A deleteIndexDocuments() 0 24 6
A callPostProcessGarbageCollectorHook() 0 15 4
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
                $site = null;
115
            }
116
117 21
            if ($site === null) {
118 1
                $this->queue->deleteItem($indexQueueItem->getType(), $indexQueueItem->getIndexQueueUid());
119 1
                continue;
120
            }
121
122 20
            $enableCommitsSetting = $site->getSolrConfiguration()->getEnableCommits();
123 20
            $siteHash = $site->getSiteHash();
124
            // a site can have multiple connections (cores / languages)
125 20
            $solrConnections = $this->connectionManager->getConnectionsBySite($site);
126 20
            if ($language > 0 && isset($solrConnections[$language])) {
127
                $solrConnections = [$language => $solrConnections[$language]];
128
            }
129 20
            $this->deleteRecordInAllSolrConnections($table, $uid, $solrConnections, $siteHash, $enableCommitsSetting);
130
        }
131
    }
132
133
    /**
134
     * Deletes the record in all solr connections from that site.
135
     *
136
     * @param string $table
137
     * @param int $uid
138
     * @param SolrConnection[] $solrConnections
139
     * @param string $siteHash
140
     * @param bool $enableCommitsSetting
141
     */
142 26
    protected function deleteRecordInAllSolrConnections(
143
        string $table,
144
        int $uid,
145
        array $solrConnections,
146
        string $siteHash,
147
        bool $enableCommitsSetting
148
    ) {
149 26
        foreach ($solrConnections as $solr) {
150 26
            $query = 'type:' . $table . ' AND uid:' . $uid . ' AND siteHash:' . $siteHash;
151 26
            $response = $solr->getWriteService()->deleteByQuery($query);
152
153 26
            if ($response->getHttpStatus() !== 200) {
154 2
                $logger = GeneralUtility::makeInstance(SolrLogManager::class, /** @scrutinizer ignore-type */ __CLASS__);
155 2
                $logger->log(
156 2
                    SolrLogManager::ERROR,
157 2
                    'Couldn\'t delete index document',
158 2
                    [
159 2
                        'status' => $response->getHttpStatus(),
160 2
                        'msg' => $response->getHttpStatusMessage(),
161 2
                        'core' => $solr->getWriteService()->getCorePath(),
162 2
                        'query' => $query,
163 2
                    ]
164 2
                );
165
166
                // @todo: Ensure index is updated later on, e.g. via a new index queue status
167 2
                continue;
168
            }
169
170 24
            if ($enableCommitsSetting) {
171 22
                $solr->getWriteService()->commit(false, false);
172
            }
173
        }
174
    }
175
176
    /**
177
     * Calls the registered post-processing hooks after the garbageCollection.
178
     *
179
     * @param string $table
180
     * @param int $uid
181
     */
182 25
    protected function callPostProcessGarbageCollectorHook(string $table, int $uid)
183
    {
184 25
        if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessGarbageCollector'] ?? null)) {
185 23
            return;
186
        }
187
188 2
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['postProcessGarbageCollector'] as $classReference) {
189 2
            $garbageCollectorPostProcessor = GeneralUtility::makeInstance($classReference);
190
191 2
            if ($garbageCollectorPostProcessor instanceof GarbageCollectorPostProcessor) {
192 2
                $garbageCollectorPostProcessor->postProcessGarbageCollector($table, $uid);
193
            } else {
194
                $message = get_class($garbageCollectorPostProcessor) . ' must implement interface ' .
195
                    GarbageCollectorPostProcessor::class;
196
                throw new \UnexpectedValueException($message, 1345807460);
197
            }
198
        }
199
    }
200
}
201