Completed
Push — 8.2 ( 737f16...22bfc7 )
by David
17:32
created

PhpVarsCheckRouter::iniGetBytes()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
c 0
b 0
f 0
rs 8.6737
cc 5
eloc 15
nc 8
nop 1
1
<?php
2
3
namespace Mouf\Mvc\Splash\Routers;
4
5
use Interop\Http\ServerMiddleware\DelegateInterface;
6
use Interop\Http\ServerMiddleware\MiddlewareInterface;
7
use Mouf\Mvc\Splash\Utils\SplashException;
8
use Psr\Http\Message\ResponseInterface as Response;
9
use Psr\Http\Message\ServerRequestInterface as Request;
10
use Psr\Log\LoggerInterface;
11
12
/**
13
 * This router :
14
 *  - just checks that some PHP settings are not exceeded : max_input_vars, max_post_size
15
 *  - doesn't actually 'routes' the request. It's more like a filter to me applied and check the request.
16
 *  - should be placed BEFORE the effective applications router and AFTER the Exceptions handling routers.
17
 *
18
 * @author Kevin Nguyen
19
 */
20
class PhpVarsCheckRouter implements MiddlewareInterface
21
{
22
    /**
23
     * The logger used by Splash.
24
     *
25
     * @var LoggerInterface
26
     */
27
    private $log;
28
29
    /**
30
     * A simple counter to check requests' length (GET, POST, REQUEST).
31
     *
32
     * @var int
33
     */
34
    private $count;
35
36
    /**
37
     * @Important
38
     *
39
     * @param LoggerInterface $log The logger used by Splash
40
     */
41
    public function __construct(LoggerInterface $log = null)
42
    {
43
        $this->log = $log;
44
    }
45
46
    /**
47
     * Get the min in 2 values if there exist.
48
     *
49
     * @param int $val1
50
     * @param int $val2
51
     *
52
     * @return int|NULL
53
     */
54
    private function getMinInConfiguration($val1, $val2)
55
    {
56
        if ($val1 && $val2) {
57
            return min(array($val1, $val2));
58
        }
59
        if ($val1) {
60
            return $val1;
61
        }
62
        if ($val2) {
63
            return $val2;
64
        }
65
66
        return null;
67
    }
68
69
    /**
70
     * Returns the number of bytes from php.ini parameter.
71
     *
72
     * @param $val
73
     *
74
     * @return int|string
75
     */
76
    private static function iniGetBytes($val)
77
    {
78
        $val = trim(ini_get($val));
79
        if ($val != '') {
80
            $last = strtolower(
81
                    $val{strlen($val) - 1}
82
            );
83
        } else {
84
            $last = '';
85
        }
86
        switch ($last) {
87
            // The 'G' modifier is available since PHP 5.1.0
88
            case 'g':
89
                $val *= 1024;
90
            case 'm':
91
                $val *= 1024;
92
            case 'k':
93
                $val *= 1024;
94
        }
95
96
        return $val;
97
    }
98
99
    /**
100
     * Count number of element in array.
101
     *
102
     * @param mixed $item
103
     * @param mixed $key
104
     */
105
    private function countRecursive($item, $key)
0 ignored issues
show
Unused Code introduced by
The parameter $item is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
106
    {
107
        ++$this->count;
108
    }
109
110
    /**
111
     * Process an incoming server request and return a response, optionally delegating
112
     * to the next middleware component to create the response.
113
     *
114
     * @param Request $request
115
     * @param DelegateInterface $delegate
116
     *
117
     * @return Response
118
     * @throws \Mouf\Mvc\Splash\Utils\SplashException
119
     */
120
    public function process(Request $request, DelegateInterface $delegate)
121
    {
122
        // Check if there is a limit of input number in php
123
        // Throw exception if the limit is reached
124 View Code Duplication
        if (ini_get('max_input_vars') || ini_get('suhosin.get.max_vars')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
125
            $maxGet = $this->getMinInConfiguration(ini_get('max_input_vars'), ini_get('suhosin.get.max_vars'));
126
            if ($maxGet !== null) {
127
                $this->count = 0;
128
                array_walk_recursive($_GET, array($this, 'countRecursive'));
129
                if ($this->count === $maxGet) {
130
                    if ($this->log !== null) {
131
                        $this->log->error('Max input vars reaches for get parameters ({maxGet}). Check your variable max_input_vars in php.ini or suhosin module suhosin.get.max_vars.', ['maxGet' => $maxGet]);
132
                    }
133
                    throw new SplashException('Max input vars reaches for get parameters ('.$maxGet.'). Check your variable max_input_vars in php.ini or suhosin module suhosin.get.max_vars.');
134
                }
135
            }
136
        }
137 View Code Duplication
        if (ini_get('max_input_vars') || ini_get('suhosin.post.max_vars')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
138
            $maxPost = $this->getMinInConfiguration(ini_get('max_input_vars'), ini_get('suhosin.post.max_vars'));
139
            if ($maxPost !== null) {
140
                $this->count = 0;
141
                array_walk_recursive($_POST, array($this, 'countRecursive'));
142
                if ($this->count === $maxPost) {
143
                    if ($this->log !== null) {
144
                        $this->log->error('Max input vars reaches for post parameters ({maxPost}). Check your variable max_input_vars in php.ini or suhosin module suhosin.post.max_vars.', ['maxPost' => $maxPost]);
145
                    }
146
                    throw new SplashException('Max input vars reaches for post parameters ('.$maxPost.'). Check your variable max_input_vars in php.ini or suhosin module suhosin.post.max_vars.');
147
                }
148
            }
149
        }
150 View Code Duplication
        if (ini_get('max_input_vars') || ini_get('suhosin.request.max_vars')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
151
            $maxRequest = $this->getMinInConfiguration(ini_get('max_input_vars'), ini_get('suhosin.request.max_vars'));
152
            if ($maxRequest !== null) {
153
                $this->count = 0;
154
                array_walk_recursive($_REQUEST, array($this, 'countRecursive'));
155
                if ($this->count === $maxRequest) {
156
                    if ($this->log !== null) {
157
                        $this->log->error('Max input vars reaches for request parameters ({maxRequest}). Check your variable max_input_vars in php.ini or suhosin module suhosin.request.max_vars.', ['maxRequest' => $maxRequest]);
158
                    }
159
                    throw new SplashException('Max input vars reaches for request parameters ('.$maxRequest.'). Check your variable max_input_vars in php.ini or suhosin module suhosin.request.max_vars.');
160
                }
161
            }
162
        }
163
        if (isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['REQUEST_METHOD']) == 'post' && empty($_POST) && empty($_FILES)) {
164
            $maxPostSize = self::iniGetBytes('post_max_size');
165
            if ($_SERVER['CONTENT_LENGTH'] > $maxPostSize) {
166
                if ($this->log !== null) {
167
                    $this->log->error('Max post size exceeded! Got {length} bytes, but limit is {maxPostSize} bytes. Edit post_max_size setting in your php.ini.', ['length' => $_SERVER['CONTENT_LENGTH'], 'maxPostSize' => $maxPostSize]);
168
                }
169
                throw new SplashException(
170
                    sprintf('Max post size exceeded! Got %s bytes, but limit is %s bytes. Edit post_max_size setting in your php.ini.',
171
                        $_SERVER['CONTENT_LENGTH'],
172
                        $maxPostSize
173
                    )
174
                );
175
            }
176
        }
177
178
        //If no Exception has been thrown, call next router
179
        return $delegate->process($request);
180
    }
181
}
182