ProcessCleanUpHook   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 186
Duplicated Lines 0 %

Test Coverage

Coverage 41.18%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 63
c 3
b 0
f 0
dl 0
loc 186
ccs 28
cts 68
cp 0.4118
rs 10
wmc 27

9 Methods

Rating   Name   Duplication   Size   Complexity  
A killProcess() 0 8 2
A doProcessStillExists() 0 16 5
A findDispatcherProcesses() 0 11 2
B removeActiveOrphanProcesses() 0 25 9
A removeActiveProcessesOlderThanOneHour() 0 15 5
A createResponseArray() 0 4 1
A crawler_init() 0 8 1
A __construct() 0 5 1
A removeProcessFromProcesslist() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AOE\Crawler\Hooks;
6
7
/*
8
 * (c) 2020 AOE GmbH <[email protected]>
9
 *
10
 * This file is part of the TYPO3 Crawler Extension.
11
 *
12
 * It is free software; you can redistribute it and/or modify it under
13
 * the terms of the GNU General Public License, either version 2
14
 * of the License, or any later version.
15
 *
16
 * For the full copyright and license information, please read the
17
 * LICENSE.txt file that was distributed with this source code.
18
 *
19
 * The TYPO3 project - inspiring people to share!
20
 */
21
22
use AOE\Crawler\Controller\CrawlerController;
23
use AOE\Crawler\Domain\Repository\ProcessRepository;
24
use AOE\Crawler\Domain\Repository\QueueRepository;
25
use TYPO3\CMS\Core\Core\Environment;
26
use TYPO3\CMS\Core\Utility\GeneralUtility;
27
use TYPO3\CMS\Extbase\Object\ObjectManager;
28
29
/**
30
 * @internal since v9.2.5
31
 */
32
class ProcessCleanUpHook implements CrawlerHookInterface
33
{
34
    /**
35
     * @var ProcessRepository
36
     */
37
    protected $processRepository;
38
39
    /**
40
     * @var QueueRepository
41
     */
42
    protected $queueRepository;
43
44
    /**
45
     * @var CrawlerController
46
     */
47
    private $crawlerController;
48
49
    /**
50
     * @var array
51
     */
52
    private $extensionSettings;
53
54 7
    public function __construct()
55
    {
56 7
        $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
57 7
        $this->processRepository = $objectManager->get(ProcessRepository::class);
58 7
        $this->queueRepository = $objectManager->get(QueueRepository::class);
59 7
    }
60
61
    /**
62
     * Main function of process CleanUp Hook.
63
     *
64
     * @param CrawlerController $crawlerController Crawler Lib class
65
     */
66 2
    public function crawler_init(CrawlerController $crawlerController): void
67
    {
68 2
        $this->crawlerController = $crawlerController;
69 2
        $this->extensionSettings = $this->crawlerController->extensionSettings;
70
71
        // Clean Up
72 2
        $this->removeActiveOrphanProcesses();
73 2
        $this->removeActiveProcessesOlderThanOneHour();
74 2
    }
75
76
    /**
77
     * Remove active processes older than one hour
78
     */
79 2
    private function removeActiveProcessesOlderThanOneHour(): void
80
    {
81 2
        $results = $this->processRepository->getActiveProcessesOlderThanOneHour();
82
83 2
        if (! is_array($results)) {
0 ignored issues
show
introduced by
The condition is_array($results) is always true.
Loading history...
84
            return;
85
        }
86 2
        foreach ($results as $result) {
87
            $systemProcessId = (int) $result['system_process_id'];
88
            $processId = $result['process_id'];
89
            if ($systemProcessId > 1) {
90
                if ($this->doProcessStillExists($systemProcessId)) {
91
                    $this->killProcess($systemProcessId);
92
                }
93
                $this->removeProcessFromProcesslist($processId);
94
            }
95
        }
96 2
    }
97
98
    /**
99
     * Removes active orphan processes from process list
100
     */
101 2
    private function removeActiveOrphanProcesses(): void
102
    {
103 2
        $results = $this->processRepository->getActiveOrphanProcesses();
104
105 2
        if (! is_array($results)) {
0 ignored issues
show
introduced by
The condition is_array($results) is always true.
Loading history...
106
            return;
107
        }
108 2
        foreach ($results as $result) {
109
            $processExists = false;
110
            $systemProcessId = (int) $result['system_process_id'];
111
            $processId = $result['process_id'];
112
            if ($systemProcessId > 1) {
113
                $dispatcherProcesses = $this->findDispatcherProcesses();
114
                if (! is_array($dispatcherProcesses) || empty($dispatcherProcesses)) {
115
                    $this->removeProcessFromProcesslist($processId);
116
                    return;
117
                }
118
                foreach ($dispatcherProcesses as $process) {
119
                    $responseArray = $this->createResponseArray($process);
120
                    if ($systemProcessId === (int) $responseArray[1]) {
121
                        $processExists = true;
122
                    };
123
                }
124
                if (! $processExists) {
125
                    $this->removeProcessFromProcesslist($processId);
126
                }
127
            }
128
        }
129 2
    }
130
131
    /**
132
     * Remove a process from processlist
133
     *
134
     * @param string $processId Unique process Id.
135
     */
136 3
    private function removeProcessFromProcesslist($processId): void
137
    {
138 3
        $this->processRepository->removeByProcessId($processId);
139 3
        $this->queueRepository->unsetQueueProcessId($processId);
140 3
    }
141
142
    /**
143
     * Create response array
144
     * Convert string to array with space character as delimiter,
145
     * removes all empty records to have a cleaner array
146
     *
147
     * @param string $string String to create array from
148
     *
149
     * @return array
150
     */
151 2
    private function createResponseArray($string)
152
    {
153 2
        $responseArray = GeneralUtility::trimExplode(' ', $string, true);
154 2
        return array_values($responseArray);
155
    }
156
157
    /**
158
     * Check if the process still exists
159
     *
160
     * @param int $pid Process id to be checked.
161
     *
162
     * @return bool
163
     * @codeCoverageIgnore
164
     */
165
    private function doProcessStillExists($pid)
166
    {
167
        $doProcessStillExists = false;
168
        if (! Environment::isWindows()) {
169
            // Not windows
170
            if (file_exists('/proc/' . $pid)) {
171
                $doProcessStillExists = true;
172
            }
173
        } else {
174
            // Windows
175
            exec('tasklist | find "' . $pid . '"', $returnArray, $returnValue);
176
            if (count($returnArray) > 0 && stripos($returnArray[0], 'php') !== false) {
177
                $doProcessStillExists = true;
178
            }
179
        }
180
        return $doProcessStillExists;
181
    }
182
183
    /**
184
     * Kills a process
185
     *
186
     * @param int $pid Process id to kill
187
     *
188
     * @codeCoverageIgnore
189
     */
190
    private function killProcess($pid): void
191
    {
192
        if (! Environment::isWindows()) {
193
            // Not windows
194
            posix_kill($pid, 9);
195
        } else {
196
            // Windows
197
            exec('taskkill /PID ' . $pid);
198
        }
199
    }
200
201
    /**
202
     * Find dispatcher processes
203
     *
204
     * @return array
205
     * @codeCoverageIgnore
206
     */
207
    private function findDispatcherProcesses()
208
    {
209
        $returnArray = [];
210
        if (! Environment::isWindows()) {
211
            // Not windows
212
            exec('ps aux | grep \'typo3 crawler:processQueue\'', $returnArray, $returnValue);
213
        } else {
214
            // Windows
215
            exec('tasklist | find \'typo3 crawler:processQueue\'', $returnArray, $returnValue);
216
        }
217
        return $returnArray;
218
    }
219
}
220