Completed
Branch test-coverage (951e01)
by Tomas Norre
15:40 queued 13:43
created

removeActiveProcessesOlderThanOneHour()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 14
nc 5
nop 0
dl 0
loc 22
rs 8.6737
c 0
b 0
f 0
ccs 0
cts 15
cp 0
crap 30
1
<?php
2
namespace AOE\Crawler\Hooks;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2016 AOE GmbH <[email protected]>
8
 *
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 3 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use AOE\Crawler\Controller\CrawlerController;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
31
/**
32
 * Class ProcessCleanUpHook
33
 * @package AOE\Crawler\Hooks
34
 */
35
class ProcessCleanUpHook
36
{
37
    /**
38
     * @var CrawlerController
39
     */
40
    private $crawlerController;
41
42
    /**
43
     * @var array
44
     */
45
    private $extensionSettings;
46
47
    /**
48
     * Main function of process CleanUp Hook.
49
     *
50
     * @param CrawlerController $crawlerController Crawler Lib class
51
     *
52
     * @return void
53
     */
54
    public function crawler_init(CrawlerController $crawlerController)
55
    {
56
        $this->crawlerController = $crawlerController;
57
        $this->extensionSettings = $this->crawlerController->extensionSettings;
58
59
        // Clean Up
60
        $this->removeActiveOrphanProcesses();
61
        $this->removeActiveProcessesOlderThanOneHour();
62
    }
63
64
    /**
65
     * Remove active processes older than one hour
66
     *
67
     * @return void
68
     */
69
    private function removeActiveProcessesOlderThanOneHour()
70
    {
71
        $results = $this->getDatabaseConnection()->exec_SELECTgetRows(
72
            'process_id, system_process_id',
73
            'tx_crawler_process',
74
            'ttl <= ' . intval(time() - $this->extensionSettings['processMaxRunTime'] - 3600) . ' AND active = 1'
75
        );
76
77
        if (!is_array($results)) {
78
            return;
79
        }
80
        foreach ($results as $result) {
81
            $systemProcessId = (int)$result['system_process_id'];
82
            $processId = $result['process_id'];
83
            if ($systemProcessId > 1) {
84
                if ($this->doProcessStillExists($systemProcessId)) {
85
                    $this->killProcess($systemProcessId);
86
                }
87
                $this->removeProcessFromProcesslist($processId);
88
            }
89
        }
90
    }
91
92
    /**
93
     * Removes active orphan processes from process list
94
     *
95
     * @return void
96
     */
97
    private function removeActiveOrphanProcesses()
98
    {
99
        $results = $this->getDatabaseConnection()->exec_SELECTgetRows(
100
            'process_id, system_process_id',
101
            'tx_crawler_process',
102
            'ttl <= ' . intval(time() - $this->extensionSettings['processMaxRunTime']) . ' AND active = 1'
103
        );
104
105
        if (!is_array($results)) {
106
            return;
107
        }
108
        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
    }
130
131
    /**
132
     * Remove a process from processlist
133
     *
134
     * @param string $processId Unique process Id.
135
     *
136
     * @return void
137
     */
138 3
    private function removeProcessFromProcesslist($processId)
139
    {
140 3
        $this->getDatabaseConnection()->exec_DELETEquery(
141 3
            'tx_crawler_process',
142 3
            'process_id = ' . $this->getDatabaseConnection()->fullQuoteStr($processId, 'tx_crawler_process')
143
        );
144
145 3
        $this->getDatabaseConnection()->exec_UPDATEquery(
146 3
            'tx_crawler_queue',
147 3
            'process_id = ' . $this->getDatabaseConnection()->fullQuoteStr($processId, 'tx_crawler_queue'),
148 3
            ['process_id' => '']
149
        );
150 3
    }
151
152
    /**
153
     * Create response array
154
     * Convert string to array with space character as delimiter,
155
     * removes all empty records to have a cleaner array
156
     *
157
     * @param string $string String to create array from
158
     *
159
     * @return array
160
     *
161
     */
162 2
    private function createResponseArray($string)
163
    {
164 2
        $responseArray = GeneralUtility::trimExplode(' ', $string, true);
165 2
        $responseArray = array_values($responseArray);
166 2
        return $responseArray;
167
    }
168
169
    /**
170
     * Check if the process still exists
171
     *
172
     * @param int $pid Process id to be checked.
173
     *
174
     * @return bool
175
     */
176
    private function doProcessStillExists($pid)
177
    {
178
        $doProcessStillExists = false;
179
        if (!$this->isOsWindows()) {
180
            // Not windows
181
            if (file_exists('/proc/' . $pid)) {
182
                $doProcessStillExists = true;
183
            }
184
        } else {
185
            // Windows
186
            exec('tasklist | find "' . $pid . '"', $returnArray, $returnValue);
187
            if (count($returnArray) > 0 && preg_match('/php/i', $returnValue[0])) {
188
                $doProcessStillExists = true;
189
            }
190
        }
191
        return $doProcessStillExists;
192
    }
193
194
    /**
195
     * Kills a process
196
     *
197
     * @param int $pid Process id to kill
198
     *
199
     * @return void
200
     */
201
    private function killProcess($pid)
202
    {
203
        if (!$this->isOsWindows()) {
204
            // Not windows
205
            posix_kill($pid, 9);
206
        } else {
207
            // Windows
208
            exec('taskkill /PID ' . $pid);
209
        }
210
    }
211
212
    /**
213
     * Find dispatcher processes
214
     *
215
     * @return array
216
     */
217
    private function findDispatcherProcesses()
218
    {
219
        $returnArray = [];
220
        if (!$this->isOsWindows()) {
221
            // Not windows
222
            exec('ps aux | grep \'cli_dispatcher\'', $returnArray, $returnValue);
223
        } else {
224
            // Windows
225
            exec('tasklist | find \'cli_dispatcher\'', $returnArray, $returnValue);
226
        }
227
        return $returnArray;
228
    }
229
230
    /**
231
     * Check if OS is Windows
232
     *
233
     * @return bool
234
     */
235
    private function isOsWindows()
236
    {
237
        if (TYPO3_OS === 'WIN') {
238
            return true;
239
        }
240
        return false;
241
    }
242
243
    /**
244
     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
245
     */
246 3
    private function getDatabaseConnection()
247
    {
248 3
        return $GLOBALS['TYPO3_DB'];
249
    }
250
}
251