Test Failed
Push — 6-0 ( cfb4d5...b26d37 )
by
unknown
04:59
created

ProcessCleanUpHook::removeProcessFromProcesslist()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 1
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
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 TYPO3\CMS\Core\Utility\GeneralUtility;
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Core\Utility\GeneralUtility was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
29
30
/**
31
 * Class ProcessCleanUpHook
32
 * @package AOE\Crawler\Hooks
33
 */
34
class ProcessCleanUpHook
35
{
36
    /**
37
     * @var \tx_crawler_lib
38
     */
39
    private $crawlerLib;
40
41
    /**
42
     * @var array
43
     */
44
    private $extensionSettings;
45
46
    /**
47
     * Main function of process CleanUp Hook.
48
     *
49
     * @param \tx_crawler_lib $crawlerLib Crawler Lib class
50
     *
51
     * @return void
52
     */
53
    public function crawler_init(\tx_crawler_lib $crawlerLib)
54
    {
55
        $this->crawlerLib = $crawlerLib;
56
        $this->extensionSettings = $this->crawlerLib->extensionSettings;
57
58
        // Clean Up
59
        $this->removeActiveOrphanProcesses();
60
        $this->removeActiveProcessesOlderThanOneHour();
61
    }
62
63
    /**
64
     * Remove active processes older than one hour
65
     *
66
     * @return void
67
     */
68
    private function removeActiveProcessesOlderThanOneHour()
69
    {
70
        $results = $this->getDatabaseConnection()->exec_SELECTgetRows(
71
            'process_id, system_process_id',
72
            'tx_crawler_process',
73
            'ttl <= ' . intval(time() - $this->extensionSettings['processMaxRunTime'] - 3600) . ' AND active = 1'
74
        );
75
76
        if (!is_array($results)) {
77
            return;
78
        }
79
        foreach ($results as $result) {
80
            $systemProcessId = (int)$result['system_process_id'];
81
            $processId = $result['process_id'];
82
            if ($systemProcessId > 1) {
83
                if ($this->doProcessStillExists($systemProcessId)) {
84
                    $this->killProcess($systemProcessId);
85
                }
86
                $this->removeProcessFromProcesslist($processId);
87
            }
88
        }
89
    }
90
91
    /**
92
     * Removes active orphan processes from process list
93
     *
94
     * @return void
95
     */
96
    private function removeActiveOrphanProcesses()
97
    {
98
        $results = $this->getDatabaseConnection()->exec_SELECTgetRows(
99
            'process_id, system_process_id',
100
            'tx_crawler_process',
101
            'ttl <= ' . intval(time() - $this->extensionSettings['processMaxRunTime']) . ' AND active = 1'
102
        );
103
104
        if (!is_array($results)) {
105
            return;
106
        }
107
        foreach ($results as $result) {
108
            $processExists = false;
109
            $systemProcessId = (int)$result['system_process_id'];
110
            $processId = $result['process_id'];
111
            if ($systemProcessId > 1) {
112
                $dispatcherProcesses = $this->findDispatcherProcesses();
113
                if (!is_array($dispatcherProcesses) || empty($dispatcherProcesses)) {
114
                    $this->removeProcessFromProcesslist($processId);
115
                    return;
116
                }
117
                foreach ($dispatcherProcesses as $process) {
118
                    $responseArray = $this->createResponseArray($process);
119
                    if ($systemProcessId === (int)$responseArray[1]) {
120
                        $processExists = true;
121
                    };
122
                }
123
                if (!$processExists) {
124
                    $this->removeProcessFromProcesslist($processId);
125
                }
126
            }
127
        }
128
    }
129
130
    /**
131
     * Remove a process from processlist
132
     *
133
     * @param string $processId Unique process Id.
134
     *
135
     * @return void
136
     */
137
    private function removeProcessFromProcesslist($processId)
138
    {
139
        $this->getDatabaseConnection()->exec_DELETEquery(
140
            'tx_crawler_process',
141
            'process_id = ' . $this->getDatabaseConnection()->fullQuoteStr($processId, 'tx_crawler_process')
142
        );
143
144
        $this->getDatabaseConnection()->exec_UPDATEquery(
145
            'tx_crawler_queue',
146
            'process_id = ' . $this->getDatabaseConnection()->fullQuoteStr($processId, 'tx_crawler_queue'),
147
            ['process_id' => '']
148
        );
149
    }
150
151
    /**
152
     * Create response array
153
     * Convert string to array with space character as delimiter,
154
     * removes all empty records to have a cleaner array
155
     *
156
     * @param string $string String to create array from
157
     *
158
     * @return array
159
     *
160
     */
161
    private function createResponseArray($string)
162
    {
163
        $responseArray = GeneralUtility::trimExplode(' ', $string, true);
164
        $responseArray = array_values($responseArray);
165
        return $responseArray;
166
    }
167
168
    /**
169
     * Check if the process still exists
170
     *
171
     * @param int $pid Process id to be checked.
172
     *
173
     * @return bool
174
     */
175
    private function doProcessStillExists($pid)
176
    {
177
        $doProcessStillExists = false;
178
        if (!$this->isOsWindows()) {
179
            // Not windows
180
            if (file_exists('/proc/' . $pid)) {
181
                $doProcessStillExists = true;
182
            }
183
        } else {
184
            // Windows
185
            exec('tasklist | find "' . $pid . '"', $returnArray, $returnValue);
186
            if (count($returnArray) > 0 && preg_match('/php/i', $returnValue[0])) {
187
                $doProcessStillExists = true;
188
            }
189
        }
190
        return $doProcessStillExists;
191
    }
192
193
    /**
194
     * Kills a process
195
     *
196
     * @param int $pid Process id to kill
197
     *
198
     * @return void
199
     */
200
    private function killProcess($pid)
201
    {
202
        if (!$this->isOsWindows()) {
203
            // Not windows
204
            posix_kill($pid, 9);
205
        } else {
206
            // Windows
207
            exec('taskkill /PID ' . $pid);
208
        }
209
    }
210
211
    /**
212
     * Find dispatcher processes
213
     *
214
     * @return array
215
     */
216
    private function findDispatcherProcesses()
217
    {
218
        $returnArray = [];
219
        if (!$this->isOsWindows()) {
220
            // Not windows
221
            exec('ps aux | grep \'cli_dispatcher\'', $returnArray, $returnValue);
222
        } else {
223
            // Windows
224
            exec('tasklist | find \'cli_dispatcher\'', $returnArray, $returnValue);
225
        }
226
        return $returnArray;
227
    }
228
229
    /**
230
     * Check if OS is Windows
231
     *
232
     * @return bool
233
     */
234
    private function isOsWindows()
235
    {
236
        if (TYPO3_OS === 'WIN') {
0 ignored issues
show
Bug introduced by
The constant AOE\Crawler\Hooks\TYPO3_OS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
237
            return true;
238
        }
239
        return false;
240
    }
241
242
    /**
243
     * @return \TYPO3\CMS\Core\Database\DatabaseConnection
0 ignored issues
show
Bug introduced by
The type TYPO3\CMS\Core\Database\DatabaseConnection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
244
     */
245
    private function getDatabaseConnection()
246
    {
247
        return $GLOBALS['TYPO3_DB'];
248
    }
249
}
250