Passed
Pull Request — master (#647)
by Tomas Norre
21:43 queued 18:06
created

StartRequestForm::selectorBox()   B

Complexity

Conditions 10
Paths 44

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 8
c 1
b 0
f 0
nc 44
nop 4
dl 0
loc 14
ccs 0
cts 12
cp 0
crap 110
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace AOE\Crawler\Backend\RequestForm;
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\Model\Reason;
24
use AOE\Crawler\Utility\MessageUtility;
25
use AOE\Crawler\Utility\PhpBinaryUtility;
26
use AOE\Crawler\Utility\SignalSlotUtility;
27
use TYPO3\CMS\Core\Localization\LanguageService;
28
use TYPO3\CMS\Core\Utility\CommandUtility;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
use TYPO3\CMS\Fluid\View\StandaloneView;
31
use TYPO3\CMS\Info\Controller\InfoModuleController;
32
33
final class StartRequestForm extends AbstractRequestForm implements RequestFormInterface
34
{
35
    /**
36
     * @var StandaloneView
37
     */
38
    private $view;
39
40
    /**
41
     * @var InfoModuleController
42
     */
43
    private $infoModuleController;
44
45
    /**
46
     * @var int
47
     */
48
    private $reqMinute = 1000;
49
50
    public function __construct(StandaloneView $view, InfoModuleController $infoModuleController)
51
    {
52
        $this->view = $view;
53
        $this->infoModuleController = $infoModuleController;
54
    }
55
56
    public function render($id, string $elementName, array $menuItems): string
57
    {
58
        return $this->showCrawlerInformationAction($id);
59
    }
60
61
    /*******************************
62
     *
63
     * Generate URLs for crawling:
64
     *
65
     ******************************/
66
67
    /**
68
     * Show a list of URLs to be crawled for each page
69
     */
70
    private function showCrawlerInformationAction(int $pageId): string
71
    {
72
        $this->view->setTemplate('ShowCrawlerInformation');
73
        if (empty($pageId)) {
74
            $this->isErrorDetected = true;
0 ignored issues
show
Bug Best Practice introduced by
The property isErrorDetected does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
75
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noPageSelected'));
76
        } else {
77
            $crawlerParameter = GeneralUtility::_GP('_crawl');
78
            $downloadParameter = GeneralUtility::_GP('_download');
79
80
            $this->duplicateTrack = [];
0 ignored issues
show
Bug Best Practice introduced by
The property duplicateTrack does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
81
            $this->submitCrawlUrls = isset($crawlerParameter);
0 ignored issues
show
Bug Best Practice introduced by
The property submitCrawlUrls does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
82
            $this->downloadCrawlUrls = isset($downloadParameter);
0 ignored issues
show
Bug Best Practice introduced by
The property downloadCrawlUrls does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
83
            $this->makeCrawlerProcessableChecks();
84
85
            switch ((string) GeneralUtility::_GP('tstamp')) {
86
                case 'midnight':
87
                    $this->scheduledTime = mktime(0, 0, 0);
0 ignored issues
show
Bug Best Practice introduced by
The property scheduledTime does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
88
                    break;
89
                case '04:00':
90
                    $this->scheduledTime = mktime(0, 0, 0) + 4 * 3600;
91
                    break;
92
                case 'now':
93
                default:
94
                    $this->scheduledTime = time();
95
                    break;
96
            }
97
98
            $this->incomingConfigurationSelection = GeneralUtility::_GP('configurationSelection');
0 ignored issues
show
Bug Best Practice introduced by
The property incomingConfigurationSelection does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
99
            $this->incomingConfigurationSelection = is_array($this->incomingConfigurationSelection) ? $this->incomingConfigurationSelection : [];
100
101
            $this->crawlerController = GeneralUtility::makeInstance(CrawlerController::class);
102
            $this->crawlerController->setAccessMode('gui');
103
            $this->crawlerController->setID = GeneralUtility::md5int(microtime());
104
105
            $code = '';
106
            $noConfigurationSelected = empty($this->incomingConfigurationSelection)
107
                || (count($this->incomingConfigurationSelection) === 1 && empty($this->incomingConfigurationSelection[0]));
108
            if ($noConfigurationSelected) {
109
                MessageUtility::addWarningMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noConfigSelected'));
110
            } else {
111
                if ($this->submitCrawlUrls) {
112
                    $reason = new Reason();
113
                    $reason->setReason(Reason::REASON_GUI_SUBMIT);
114
                    $reason->setDetailText('The user ' . $GLOBALS['BE_USER']->user['username'] . ' added pages to the crawler queue manually');
115
116
                    $signalPayload = ['reason' => $reason];
117
                    SignalSlotUtility::emitSignal(
118
                        self::class,
119
                        SignalSlotUtility::SIGNAL_INVOKE_QUEUE_CHANGE,
120
                        $signalPayload
121
                    );
122
                }
123
124
                $code = $this->crawlerController->getPageTreeAndUrls(
125
                    $pageId,
126
                    $this->infoModuleController->MOD_SETTINGS['depth'],
127
                    $this->scheduledTime,
128
                    $this->reqMinute,
129
                    $this->submitCrawlUrls,
130
                    $this->downloadCrawlUrls,
131
                    [], // Do not filter any processing instructions
132
                    $this->incomingConfigurationSelection
133
                );
134
            }
135
136
            $this->downloadUrls = $this->crawlerController->downloadUrls;
0 ignored issues
show
Bug Best Practice introduced by
The property downloadUrls does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
137
            $this->duplicateTrack = $this->crawlerController->duplicateTrack;
138
139
            $this->view->assign('noConfigurationSelected', $noConfigurationSelected);
140
            $this->view->assign('submitCrawlUrls', $this->submitCrawlUrls);
141
            $this->view->assign('amountOfUrls', count(array_keys($this->duplicateTrack)));
142
            $this->view->assign('selectors', $this->generateConfigurationSelectors());
143
            $this->view->assign('code', $code);
144
            $this->view->assign('displayActions', 0);
145
146
            // Download Urls to crawl:
147
            if ($this->downloadCrawlUrls) {
148
                // Creating output header:
149
                header('Content-Type: application/octet-stream');
150
                header('Content-Disposition: attachment; filename=CrawlerUrls.txt');
151
152
                // Printing the content of the CSV lines:
153
                echo implode(chr(13) . chr(10), $this->downloadUrls);
154
                exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
155
            }
156
        }
157
        return $this->view->render();
158
    }
159
160
    private function getLanguageService(): LanguageService
161
    {
162
        return $GLOBALS['LANG'];
163
    }
164
165
    /**
166
     * Verify that the crawler is executable.
167
     * TODO: popen() is part of PHP Core and check can be removed. The PHP Binary check should be moved to PhPBinaryUtility::class
168
     */
169
    private function makeCrawlerProcessableChecks(): void
170
    {
171
        if (! $this->isPhpForkAvailable()) {
172
            $this->isErrorDetected = true;
0 ignored issues
show
Bug Best Practice introduced by
The property isErrorDetected does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
173
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.noPhpForkAvailable'));
174
        }
175
176
        $exitCode = 0;
177
        $out = [];
178
        CommandUtility::exec(
179
            PhpBinaryUtility::getPhpBinary() . ' -v',
180
            $out,
181
            $exitCode
182
        );
183
        if ($exitCode > 0) {
184
            $this->isErrorDetected = true;
185
            MessageUtility::addErrorMessage(sprintf($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.phpBinaryNotFound'), htmlspecialchars($this->extensionSettings['phpPath'])));
0 ignored issues
show
Bug Best Practice introduced by
The property extensionSettings does not exist on AOE\Crawler\Backend\RequestForm\StartRequestForm. Did you maybe forget to declare it?
Loading history...
186
        }
187
    }
188
189
    /**
190
     * Indicate that the required PHP method "popen" is
191
     * available in the system.
192
     */
193
    private function isPhpForkAvailable(): bool
194
    {
195
        return function_exists('popen');
196
    }
197
198
    /**
199
     * Generates the configuration selectors for compiling URLs:
200
     */
201
    private function generateConfigurationSelectors(): array
202
    {
203
        $selectors = [];
204
        $selectors['depth'] = $this->selectorBox(
205
            [
206
                0 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
207
                1 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
208
                2 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
209
                3 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
210
                4 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
211
                99 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi'),
212
            ],
213
            'SET[depth]',
214
            $this->infoModuleController->MOD_SETTINGS['depth'],
215
            false
216
        );
217
218
        // Configurations
219
        $availableConfigurations = $this->crawlerController->getConfigurationsForBranch((int) $this->pageId, (int) $this->infoModuleController->MOD_SETTINGS['depth'] ?: 0);
0 ignored issues
show
Bug Best Practice introduced by
The property pageId does not exist on AOE\Crawler\Backend\RequestForm\StartRequestForm. Did you maybe forget to declare it?
Loading history...
220
        $selectors['configurations'] = $this->selectorBox(
221
            empty($availableConfigurations) ? [] : array_combine($availableConfigurations, $availableConfigurations),
0 ignored issues
show
Bug introduced by
It seems like empty($availableConfigur...vailableConfigurations) can also be of type false; however, parameter $optArray of AOE\Crawler\Backend\Requ...uestForm::selectorBox() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

221
            /** @scrutinizer ignore-type */ empty($availableConfigurations) ? [] : array_combine($availableConfigurations, $availableConfigurations),
Loading history...
222
            'configurationSelection',
223
            $this->incomingConfigurationSelection,
224
            true
225
        );
226
227
        // Scheduled time:
228
        $selectors['scheduled'] = $this->selectorBox(
229
            [
230
                'now' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.now'),
231
                'midnight' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.midnight'),
232
                '04:00' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.4am'),
233
            ],
234
            'tstamp',
235
            GeneralUtility::_POST('tstamp'),
236
            false
237
        );
238
239
        return $selectors;
240
    }
241
242
    /**
243
     * Create selector box
244
     *
245
     * @param array $optArray Options key(value) => label pairs
246
     * @param string $name Selector box name
247
     * @param string|array $value Selector box value (array for multiple...)
248
     * @param boolean $multiple If set, will draw multiple box.
249
     *
250
     * @return string HTML select element
251
     */
252
    private function selectorBox($optArray, $name, $value, bool $multiple): string
253
    {
254
        if (! is_string($value) || ! is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
255
            $value = '';
256
        }
257
258
        $options = [];
259
        foreach ($optArray as $key => $val) {
260
            $selected = (! $multiple && ! strcmp($value, (string) $key)) || ($multiple && in_array($key, (array) $value, true));
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: $selected = (! $multiple..., (array)$value, true)), Probably Intended Meaning: $selected = ! $multiple ..., (array)$value, true))
Loading history...
261
            $options[] = '
262
                <option value="' . $key . '" ' . ($selected ? ' selected="selected"' : '') . '>' . htmlspecialchars($val, ENT_QUOTES | ENT_HTML5) . '</option>';
263
        }
264
265
        return '<select class="form-control" name="' . htmlspecialchars($name . ($multiple ? '[]' : ''), ENT_QUOTES | ENT_HTML5) . '"' . ($multiple ? ' multiple' : '') . '>' . implode('', $options) . '</select>';
266
    }
267
}
268