Passed
Push — refactor/backendModule-ValueOb... ( 0f77d0...7f5344 )
by Tomas Norre
07:41
created

StartRequestForm::isPhpForkAvailable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 3
cp 0
crap 2
rs 10
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
32
final class StartRequestForm implements RequestForm
33
{
34
    /** @var StandaloneView */
35
    private $view;
36
37
    public function __construct(StandaloneView $view)
38
    {
39
        $this->view = $view;
40
    }
41
42
    public function render($id, string $elementName, array $menuItems): string
43
    {
44
        return $this->showCrawlerInformationAction($id);
45
    }
46
47
    /*******************************
48
     *
49
     * Generate URLs for crawling:
50
     *
51
     ******************************/
52
53
    /**
54
     * Show a list of URLs to be crawled for each page
55
     */
56
    private function showCrawlerInformationAction(int $pageId): string
57
    {
58
        $this->view->setTemplate('ShowCrawlerInformation');
59
        if (empty($pageId)) {
60
            $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...
61
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noPageSelected'));
62
        } else {
63
            $crawlerParameter = GeneralUtility::_GP('_crawl');
64
            $downloadParameter = GeneralUtility::_GP('_download');
65
66
            $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...
67
            $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...
68
            $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...
69
            $this->makeCrawlerProcessableChecks();
70
71
            switch ((string) GeneralUtility::_GP('tstamp')) {
72
                case 'midnight':
73
                    $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...
74
                    break;
75
                case '04:00':
76
                    $this->scheduledTime = mktime(0, 0, 0) + 4 * 3600;
77
                    break;
78
                case 'now':
79
                default:
80
                    $this->scheduledTime = time();
81
                    break;
82
            }
83
84
            $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...
85
            $this->incomingConfigurationSelection = is_array($this->incomingConfigurationSelection) ? $this->incomingConfigurationSelection : [];
86
87
            $this->crawlerController = GeneralUtility::makeInstance(CrawlerController::class);
0 ignored issues
show
Bug Best Practice introduced by
The property crawlerController does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
88
            $this->crawlerController->setAccessMode('gui');
89
            $this->crawlerController->setID = GeneralUtility::md5int(microtime());
90
91
            $code = '';
92
            $noConfigurationSelected = empty($this->incomingConfigurationSelection)
93
                || (count($this->incomingConfigurationSelection) === 1 && empty($this->incomingConfigurationSelection[0]));
94
            if ($noConfigurationSelected) {
95
                MessageUtility::addWarningMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.noConfigSelected'));
96
            } else {
97
                if ($this->submitCrawlUrls) {
98
                    $reason = new Reason();
99
                    $reason->setReason(Reason::REASON_GUI_SUBMIT);
100
                    $reason->setDetailText('The user ' . $GLOBALS['BE_USER']->user['username'] . ' added pages to the crawler queue manually');
101
102
                    $signalPayload = ['reason' => $reason];
103
                    SignalSlotUtility::emitSignal(
104
                        self::class,
105
                        SignalSlotUtility::SIGNAL_INVOKE_QUEUE_CHANGE,
106
                        $signalPayload
107
                    );
108
                }
109
110
                $code = $this->crawlerController->getPageTreeAndUrls(
111
                    $this->id,
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on AOE\Crawler\Backend\RequestForm\StartRequestForm. Did you maybe forget to declare it?
Loading history...
112
                    $this->pObj->MOD_SETTINGS['depth'],
0 ignored issues
show
Bug Best Practice introduced by
The property pObj does not exist on AOE\Crawler\Backend\RequestForm\StartRequestForm. Did you maybe forget to declare it?
Loading history...
113
                    $this->scheduledTime,
114
                    $this->reqMinute,
0 ignored issues
show
Bug Best Practice introduced by
The property reqMinute does not exist on AOE\Crawler\Backend\RequestForm\StartRequestForm. Did you maybe forget to declare it?
Loading history...
115
                    $this->submitCrawlUrls,
116
                    $this->downloadCrawlUrls,
117
                    [], // Do not filter any processing instructions
118
                    $this->incomingConfigurationSelection
119
                );
120
            }
121
122
            $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...
123
            $this->duplicateTrack = $this->crawlerController->duplicateTrack;
124
125
            $this->view->assign('noConfigurationSelected', $noConfigurationSelected);
126
            $this->view->assign('submitCrawlUrls', $this->submitCrawlUrls);
127
            $this->view->assign('amountOfUrls', count(array_keys($this->duplicateTrack)));
128
            $this->view->assign('selectors', $this->generateConfigurationSelectors());
129
            $this->view->assign('code', $code);
130
            $this->view->assign('displayActions', 0);
131
132
            // Download Urls to crawl:
133
            if ($this->downloadCrawlUrls) {
134
                // Creating output header:
135
                header('Content-Type: application/octet-stream');
136
                header('Content-Disposition: attachment; filename=CrawlerUrls.txt');
137
138
                // Printing the content of the CSV lines:
139
                echo implode(chr(13) . chr(10), $this->downloadUrls);
140
                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...
141
            }
142
        }
143
        return $this->view->render();
144
    }
145
146
    private function getLanguageService(): LanguageService
147
    {
148
        return $GLOBALS['LANG'];
149
    }
150
151
    /**
152
     * Verify that the crawler is executable.
153
     */
154
    private function makeCrawlerProcessableChecks(): void
155
    {
156
        if (! $this->isPhpForkAvailable()) {
157
            $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...
158
            MessageUtility::addErrorMessage($this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:message.noPhpForkAvailable'));
159
        }
160
161
        $exitCode = 0;
162
        $out = [];
163
        CommandUtility::exec(
164
            PhpBinaryUtility::getPhpBinary() . ' -v',
165
            $out,
166
            $exitCode
167
        );
168
        if ($exitCode > 0) {
169
            $this->isErrorDetected = true;
170
            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...
171
        }
172
    }
173
174
    /**
175
     * Indicate that the required PHP method "popen" is
176
     * available in the system.
177
     */
178
    private function isPhpForkAvailable(): bool
179
    {
180
        return function_exists('popen');
181
    }
182
183
    /**
184
     * Generates the configuration selectors for compiling URLs:
185
     */
186
    private function generateConfigurationSelectors(): array
187
    {
188
        $selectors = [];
189
        $selectors['depth'] = $this->selectorBox(
190
            [
191
                0 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'),
192
                1 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_1'),
193
                2 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_2'),
194
                3 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_3'),
195
                4 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_4'),
196
                99 => $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_infi'),
197
            ],
198
            'SET[depth]',
199
            $this->pObj->MOD_SETTINGS['depth'],
0 ignored issues
show
Bug Best Practice introduced by
The property pObj does not exist on AOE\Crawler\Backend\RequestForm\StartRequestForm. Did you maybe forget to declare it?
Loading history...
200
            false
201
        );
202
203
        // Configurations
204
        $availableConfigurations = $this->crawlerController->getConfigurationsForBranch((int) $this->id, (int) $this->pObj->MOD_SETTINGS['depth'] ?: 0);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on AOE\Crawler\Backend\RequestForm\StartRequestForm. Did you maybe forget to declare it?
Loading history...
205
        $selectors['configurations'] = $this->selectorBox(
206
            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

206
            /** @scrutinizer ignore-type */ empty($availableConfigurations) ? [] : array_combine($availableConfigurations, $availableConfigurations),
Loading history...
207
            'configurationSelection',
208
            $this->incomingConfigurationSelection,
209
            true
210
        );
211
212
        // Scheduled time:
213
        $selectors['scheduled'] = $this->selectorBox(
214
            [
215
                'now' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.now'),
216
                'midnight' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.midnight'),
217
                '04:00' => $this->getLanguageService()->sL('LLL:EXT:crawler/Resources/Private/Language/locallang.xlf:labels.time.4am'),
218
            ],
219
            'tstamp',
220
            GeneralUtility::_POST('tstamp'),
221
            false
222
        );
223
224
        return $selectors;
225
    }
226
227
    /**
228
     * Create selector box
229
     *
230
     * @param array $optArray Options key(value) => label pairs
231
     * @param string $name Selector box name
232
     * @param string|array $value Selector box value (array for multiple...)
233
     * @param boolean $multiple If set, will draw multiple box.
234
     *
235
     * @return string HTML select element
236
     */
237
    private function selectorBox($optArray, $name, $value, bool $multiple): string
238
    {
239
        if (! is_string($value) || ! is_array($value)) {
0 ignored issues
show
introduced by
The condition is_array($value) is always false.
Loading history...
240
            $value = '';
241
        }
242
243
        $options = [];
244
        foreach ($optArray as $key => $val) {
245
            $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...
246
            $options[] = '
247
                <option value="' . $key . '" ' . ($selected ? ' selected="selected"' : '') . '>' . htmlspecialchars($val) . '</option>';
248
        }
249
250
        return '<select class="form-control" name="' . htmlspecialchars($name . ($multiple ? '[]' : '')) . '"' . ($multiple ? ' multiple' : '') . '>' . implode('', $options) . '</select>';
251
    }
252
}
253