TemplateTrait::setDialog()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 * This file is part of the Shieldon package.
4
 *
5
 * (c) Terry L. <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * php version 7.1.0
11
 *
12
 * @category  Web-security
13
 * @package   Shieldon
14
 * @author    Terry Lin <[email protected]>
15
 * @copyright 2019 terrylinooo
16
 * @license   https://github.com/terrylinooo/shieldon/blob/2.x/LICENSE MIT
17
 * @link      https://github.com/terrylinooo/shieldon
18
 * @see       https://shieldon.io
19
 */
20
21
declare(strict_types=1);
22
23
namespace Shieldon\Firewall\Kernel;
24
25
use Psr\Http\Message\ResponseInterface;
26
use Shieldon\Firewall\Kernel\Enum;
27
use Shieldon\Firewall\HttpFactory;
28
use Shieldon\Firewall\Container;
29
use Shieldon\Event\Event;
30
use function Shieldon\Firewall\get_response;
31
use function Shieldon\Firewall\get_request;
32
use function Shieldon\Firewall\get_session_instance;
33
use function Shieldon\Firewall\__;
34
use InvalidArgumentException;
35
use RuntimeException;
36
use function array_keys;
37
use function define;
38
use function defined;
39
use function is_dir;
40
use function ob_end_clean;
41
use function ob_get_contents;
42
use function ob_start;
43
use function file_exists;
44
use function sprintf;
45
46
/*
47
 * The template-related functions.
48
 */
49
trait TemplateTrait
50
{
51
    /**
52
     *   Public methods       | Desctiotion
53
     *  ----------------------|---------------------------------------------
54
     *   respond              | Respond the result.
55
     *   setTemplateDirectory | Set the frontend template directory.
56
     *   getJavascript        | Print a JavaScript snippet in the pages.
57
     *  ----------------------|---------------------------------------------
58
     */
59
60
    /**
61
     * The directory in where the frontend template files are placed.
62
     *
63
     * @var string
64
     */
65
    protected $templateDirectory = '';
66
67
    /**
68
     * Custom dialog UI settings.
69
     *
70
     * @var array
71
     */
72
    protected $dialog = [];
73
74
    /**
75
     * Get current visior's path.
76
     *
77
     * @return string
78
     */
79
    abstract public function getCurrentUrl(): string;
80
81
    /**
82
     * Customize the dialog UI.
83
     *
84
     * @param array $settings The dialog UI settings.
85
     *
86
     * @return void
87
     */
88 73
    public function setDialog(array $settings): void
89
    {
90 73
        $this->dialog = $settings;
91
    }
92
93
    /**
94
     * Respond the result.
95
     *
96
     * @return ResponseInterface
97
     */
98 12
    public function respond(): ResponseInterface
99
    {
100 12
        $response = get_response();
101
102 12
        $httpStatusCodes = [
103 12
            Enum::RESPONSE_TEMPORARILY_DENY => [
104 12
                'type' => 'captcha',
105 12
                'code' => Enum::HTTP_STATUS_FORBIDDEN,
106 12
            ],
107
108 12
            Enum::RESPONSE_LIMIT_SESSION => [
109 12
                'type' => 'session_limitation',
110 12
                'code' => Enum::HTTP_STATUS_TOO_MANY_REQUESTS,
111 12
            ],
112
113 12
            Enum::RESPONSE_DENY => [
114 12
                'type' => 'rejection',
115 12
                'code' => Enum::HTTP_STATUS_BAD_REQUEST,
116 12
            ],
117 12
        ];
118
119
        // Nothing happened. Return.
120 12
        if (empty($httpStatusCodes[$this->result])) {
121 11
            return $response;
122
        }
123
124 3
        $type = $httpStatusCodes[$this->result]['type'];
125 3
        $statusCode = $httpStatusCodes[$this->result]['code'];
126
127 3
        $viewPath = $this->getTemplate($type);
128
129
        // The language of output UI. It is used on views.
130 3
        $langCode = get_session_instance()->get('shieldon_ui_lang') ?? 'en';
131
132 3
        $onlineinfo = [];
133 3
        $onlineinfo['queue'] = $this->sessionStatus['queue'];
134 3
        $onlineinfo['count'] = $this->sessionStatus['count'];
135 3
        $onlineinfo['period'] = $this->sessionLimit['period'];
136
137 3
        $dialoguserinfo = [];
138 3
        $dialoguserinfo['ip'] = $this->ip;
139 3
        $dialoguserinfo['rdns'] = $this->rdns;
140 3
        $dialoguserinfo['user_agent'] = get_request()->getHeaderLine('user-agent');
141
142
        // Captcha form
143 3
        $form = $this->getCurrentUrl();
144 3
        $captchas = $this->captcha;
145
146
        // Check and confirm the UI settings.
147 3
        $ui = $this->confirmUiSettings();
148 3
        $uiInfo = $this->confirmUiInfoSettings($statusCode);
149
150 3
        $css = include $this->getTemplate('css/default');
151
152
        /**
153
         * Hook - dialog_output
154
         */
155 3
        Event::doDispatch('dialog_output');
156
157 3
        $performanceReport = $this->displayPerformanceReport();
158
159 3
        ob_start();
160 3
        include $viewPath;
161 3
        $output = ob_get_contents();
162 3
        ob_end_clean();
163
164
        // Remove unused variable notices generated from PHP intelephense.
165 3
        unset($css, $ui, $form, $captchas, $langCode, $performanceReport, $uiInfo);
166
167 3
        $stream = HttpFactory::createStream();
168 3
        $stream->write($output);
169 3
        $stream->rewind();
170
171 3
        return $response
172 3
            ->withHeader('X-Protected-By', 'shieldon.io')
173 3
            ->withBody($stream)
174 3
            ->withStatus($statusCode);
0 ignored issues
show
Bug introduced by
The method withStatus() does not exist on Psr\Http\Message\MessageInterface. It seems like you code against a sub-type of Psr\Http\Message\MessageInterface such as Psr\Http\Message\ResponseInterface or Shieldon\Psr7\Response. ( Ignorable by Annotation )

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

174
            ->/** @scrutinizer ignore-call */ withStatus($statusCode);
Loading history...
175
    }
176
177
    /**
178
     * Print a JavaScript snippet in your webpages.
179
     *
180
     * This snippet generate cookie on client's browser,then we check the
181
     * cookie to identify the client is a rebot or not.
182
     *
183
     * @return string
184
     */
185 1
    public function getJavascript(): string
186
    {
187 1
        $tmpCookieName = $this->properties['cookie_name'];
188 1
        $tmpCookieDomain = $this->properties['cookie_domain'];
189
190 1
        if (empty($tmpCookieDomain) && get_request()->getHeaderLine('host')) {
191 1
            $tmpCookieDomain = get_request()->getHeaderLine('host');
192
        }
193
194 1
        $tmpCookieValue = $this->properties['cookie_value'];
195
196 1
        $jsString = '
197
            <script>
198
                var d = new Date();
199
                d.setTime(d.getTime()+(60*60*24*30));
200 1
                document.cookie = "' . $tmpCookieName . '=' . $tmpCookieValue . ';domain=.' . $tmpCookieDomain .
201
                ';expires="+d.toUTCString();
202 1
            </script>
203
        ';
204 1
205
        return $jsString;
206
    }
207
208
    /**
209
     * Set the frontend template directory.
210
     *
211
     * @param string $directory The directory in where the template files are placed.
212
     *
213
     * @return void
214 4
     */
215
    public function setTemplateDirectory(string $directory): void
216 4
    {
217 1
        if (!is_dir($directory)) {
218 1
            throw new InvalidArgumentException(
219 1
                'The template directory does not exist.'
220
            );
221 3
        }
222
        $this->templateDirectory = $directory;
223
    }
224
225
    /**
226
     * Get a template PHP file.
227
     *
228
     * @param string $type The template type.
229
     *
230
     * @return string
231 5
     */
232
    protected function getTemplate(string $type): string
233 5
    {
234
        $directory = Enum::KERNEL_DIR . '/../../templates/frontend';
235 5
236 2
        if (!empty($this->templateDirectory)) {
237
            $directory = $this->templateDirectory;
238
        }
239 5
240
        $path = $directory . '/' . $type . '.php';
241 5
242 2
        if (!file_exists($path)) {
243 2
            throw new RuntimeException(
244 2
                sprintf(
245 2
                    'The templeate file is missing. (%s)',
246 2
                    $path
247 2
                )
248
            );
249
        }
250 4
251
        return $path;
252
    }
253
254
    /**
255
     * Count the performance statistics.
256
     *
257
     * @return array
258 1
     */
259
    protected function getPerformanceStats(): array
260 1
    {
261 1
        $statStart = Container::get('shieldon_start');
262
        $statEnd = Container::get('shieldon_end');
263 1
264 1
        $startTimeArr = explode(' ', $statStart['time']);
265
        $endTimeArr = explode(' ', $statStart['time']);
266 1
267 1
        $timeDifference = ($endTimeArr[1] - $startTimeArr[1]) + ($endTimeArr[0] - $startTimeArr[0]);
268
        $memoryDifference = round(($statEnd['memory'] - $statStart['memory']) / 1024, 2); // KB
269 1
270 1
        $data = [
271 1
            'time' => $timeDifference,
272 1
            'memory' => $memoryDifference,
273
        ];
274 1
275
        return $data;
276
    }
277
278
    /**
279
     * Display the HTML of the performance report.
280
     *
281
     * @return string
282 3
     */
283
    protected function displayPerformanceReport(): string
284 3
    {
285 2
        if (!Container::get('shieldon_start')) {
286
            return '';
287
        }
288 1
289
        $html = '';
290 1
291
        $performance = $this->getPerformanceStats();
292 1
293 1
        if ($performance['time'] < 0.001) {
294
            $performance['time'] = 'fewer than 0.001';
295
        }
296 1
297 1
        if (isset($performance['time'])) {
298 1
            $html .= '<div class="performance-report">';
299 1
            $html .= 'Memory consumed: <strong>' . $performance['memory'] . '</strong> KB / ';
300 1
            $html .= 'Execution:  <strong>' . $performance['time'] . ' </strong> seconds.';
301
            $html .= '</div>';
302
        }
303 1
304
        return $html;
305
    }
306
307
308
    /**
309
     * Confirm the UI settings.
310
     *
311
     * @return array
312 3
     */
313
    private function confirmUiSettings(): array
314 3
    {
315 3
        if (!defined('SHIELDON_VIEW')) {
316
            define('SHIELDON_VIEW', true);
317
        }
318 3
319 3
        $ui = [
320 3
            'background_image' => '',
321 3
            'bg_color'         => '#ffffff',
322 3
            'header_bg_color'  => '#212531',
323 3
            'header_color'     => '#ffffff',
324 3
            'shadow_opacity'   => '0.2',
325
        ];
326 3
327 3
        foreach (array_keys($ui) as $key) {
328 3
            if (!empty($this->dialog[$key])) {
329
                $ui[$key] = $this->dialog[$key];
330
            }
331
        }
332 3
333
        return $ui;
334
    }
335
336
    /**
337
     * Confirm UI information settings.
338
     *
339
     * @param int $statusCode HTTP status code.
340
     *
341
     * @return array
342 3
     */
343
    private function confirmUiInfoSettings(int $statusCode): array
344 3
    {
345
        $uiInfo = [];
346 3
347
        $reasonCode = $this->reason;
348 3
349 3
        $uiInfo['http_status_code'] = $statusCode;
350 3
        $uiInfo['reason_code']      = $reasonCode;
351
        $uiInfo['reason_text']      = __('core', 'messenger_text_reason_code_' . $reasonCode);
352 3
353 3
        $uiInfo['is_display_online_user_amount']  = $this->properties['display_online_info'];
354 3
        $uiInfo['is_display_user_information']    = $this->properties['display_user_info'];
355 3
        $uiInfo['is_display_display_http_code']   = $this->properties['display_http_code'];
356 3
        $uiInfo['is_display_display_reason_code'] = $this->properties['display_reason_code'];
357
        $uiInfo['is_display_display_reason_text'] = $this->properties['display_reason_text'];
358 3
359
        return $uiInfo;
360
    }
361
}
362