8
8
9
9
namespace Redbitcz \DebugMode ;
10
10
11
+ use InvalidArgumentException ;
12
+ use LogicException ;
13
+
11
14
class Detector
12
15
{
13
- private const DEBUG_ENV_NAME = 'APP_DEBUG ' ;
14
- private const DEBUG_COOKIE_NAME = 'app-debug-mode ' ;
16
+ /** Name of Environment variable used to detect Debug mode */
17
+ public const DEBUG_ENV_NAME = 'APP_DEBUG ' ;
18
+
19
+ /** Name of Cookie used to detect Debug mode */
20
+ public const DEBUG_COOKIE_NAME = 'app-debug-mode ' ;
21
+
22
+
23
+ /** Enables Debug mode detection by client IP */
24
+ public const MODE_IP = 0b0001 ;
25
+
26
+ /** Enables Debug mode detection by PHP process Environment variable */
27
+ public const MODE_ENV = 0b0010 ;
28
+
29
+ /** Enables Debug mode detection by request Cookie */
30
+ public const MODE_COOKIE = 0b0100 ;
15
31
16
- public const MODE_ENABLER = 0b0001 ;
17
- public const MODE_COOKIE = 0b0010 ;
18
- public const MODE_ENV = 0b0100 ;
19
- public const MODE_IP = 0b1000 ;
20
- public const MODE_ALL = self ::MODE_ENABLER | self ::MODE_COOKIE | self ::MODE_ENV | self ::MODE_IP ;
32
+ /** Enables Debug mode detection by Enabler */
33
+ public const MODE_ENABLER = 0b1000 ;
21
34
22
- /** @var Enabler */
35
+ /** Simple mode without Enabler */
36
+ public const MODE_SIMPLE = self ::MODE_COOKIE | self ::MODE_ENV | self ::MODE_IP ;
37
+
38
+ /** Full mode with Enabler */
39
+ public const MODE_ALL = self ::MODE_ENABLER | self ::MODE_SIMPLE ;
40
+
41
+
42
+ /** @var Enabler|null */
23
43
private $ enabler ;
24
44
/** @var int */
25
45
private $ mode ;
46
+ /** @var string[] */
47
+ private $ ips = ['::1 ' , '127.0.0.1 ' ];
26
48
27
- public function __construct (string $ tempDir , int $ mode = self ::MODE_ALL )
49
+ /**
50
+ * @param int $mode Enables methods which is used to detect Debug mode
51
+ * @param Enabler|null $enabler Enabler instance. Optional, but required when Enabler mode is enabled
52
+ */
53
+ public function __construct (int $ mode = self ::MODE_SIMPLE , ?Enabler $ enabler = null )
28
54
{
29
- $ this ->enabler = new Enabler ($ tempDir );
55
+ if ($ enabler === null && $ mode & self ::MODE_ENABLER ) {
56
+ throw new InvalidArgumentException ('Enabler mode requires Enabler instance in constructor ' );
57
+ }
58
+
59
+ $ this ->enabler = $ enabler ;
30
60
$ this ->mode = $ mode ;
31
61
}
32
62
33
63
public function getEnabler (): Enabler
34
64
{
65
+ if ($ this ->enabler === null ) {
66
+ throw new LogicException ('Detector constructed without Enabler ' );
67
+ }
68
+
35
69
return $ this ->enabler ;
36
70
}
37
71
72
+ /**
73
+ * Detect Debug mode by all method enabled by Detector mode
74
+ * Returned value:
75
+ * - `false` (force to turn-off debug mode)
76
+ * - `true` (force to turn-on debug mode)
77
+ * - `null` (unknown/automatic debug mode state)
78
+ */
38
79
public function isDebugMode (?bool $ default = false ): ?bool
39
80
{
40
81
return ($ this ->mode & self ::MODE_ENABLER ? $ this ->isDebugModeByEnabler () : null )
@@ -45,17 +86,15 @@ public function isDebugMode(?bool $default = false): ?bool
45
86
}
46
87
47
88
/**
48
- * Detect debug state by DobugModeEnabler helper
89
+ * Detect Debug mode by `DebugMode\Enabler` helper
49
90
* Returned value:
50
91
* - `false` (force to turn-off debug mode)
51
92
* - `true` (force to turn-on debug mode)
52
93
* - `null` (enabler is not activated)
53
- *
54
- * @return bool|null
55
94
*/
56
95
public function isDebugModeByEnabler (): ?bool
57
96
{
58
- return $ this ->enabler ->isDebug ();
97
+ return $ this ->getEnabler () ->isDebug ();
59
98
}
60
99
61
100
/**
@@ -67,8 +106,6 @@ public function isDebugModeByEnabler(): ?bool
67
106
*
68
107
* Note: This cookie allows only turn-off Debug mode.
69
108
* Using cookie to turn-on debug mode is unsecure!
70
- *
71
- * @return bool|null
72
109
*/
73
110
public function isDebugModeByCookie (): ?bool
74
111
{
@@ -81,31 +118,27 @@ public function isDebugModeByCookie(): ?bool
81
118
}
82
119
83
120
/**
84
- * Detect debug state by ENV parameter
121
+ * Detect Debug mode by ENV parameter
85
122
* ENV value vs. returned value:
86
123
* - `0`: `false` (force to turn-off debug mode)
87
124
* - `1`: `true` (force to turn-on debug mode)
88
125
* - `undefined` or any other value: `null`
89
- *
90
- * @return bool|null
91
126
*/
92
127
public function isDebugModeByEnv (): ?bool
93
128
{
94
129
$ envValue = getenv (self ::DEBUG_ENV_NAME );
95
- if (is_numeric ($ envValue ) && in_array ((int )$ envValue , [0 , 1 ], true )) {
130
+ if ($ envValue !== false && is_numeric ($ envValue ) && in_array ((int )$ envValue , [0 , 1 ], true )) {
96
131
return (int )$ envValue === 1 ;
97
132
}
98
133
99
134
return null ;
100
135
}
101
136
102
137
/**
103
- * Detect debug state by locahost IP
138
+ * Detect debug state by match allowed IP addresses
104
139
* Returned value:
105
- * - is localhost: `true` (force to turn-on debug mode)
106
- * - otherwise: `null`
107
- *
108
- * @return bool|null
140
+ * - is matched: `true` (force to turn-on debug mode)
141
+ * - not matched: `null`
109
142
*/
110
143
public function isDebugModeByIp (): ?bool
111
144
{
@@ -114,21 +147,73 @@ public function isDebugModeByIp(): ?bool
114
147
// Security check: Prevent false-positive match behind reverse proxy
115
148
$ result = isset ($ _SERVER ['HTTP_X_FORWARDED_FOR ' ]) === false
116
149
&& isset ($ _SERVER ['HTTP_FORWARDED ' ]) === false
117
- && (
118
- $ addr === '::1 '
119
- || preg_match ('/^127\.0\.0\.\d+$/D ' , $ addr )
120
- );
150
+ && isset ($ _SERVER ['HTTP_X_REAL_IP ' ]) === false
151
+ && in_array ($ addr , $ this ->ips , true );
121
152
122
153
return $ result ?: null ;
123
154
}
124
155
125
- public static function detect (string $ tempDir , int $ mode = self ::MODE_ALL , ?bool $ default = false ): ?bool
156
+ /**
157
+ * Set client IP address with allowed Debug mode
158
+ */
159
+ public function allowIp (string ...$ ips ): self
126
160
{
127
- return (new self ($ tempDir , $ mode ))->isDebugMode ($ default );
161
+ $ this ->ips = $ ips ;
162
+ return $ this ;
128
163
}
129
164
130
- public static function detectProductionMode (string $ tempDir , ?bool $ default = false ): ?bool
165
+ /**
166
+ * Add client IP address with allowed Debug mode
167
+ */
168
+ public function addAllowedIp (string ...$ ips ): self
131
169
{
132
- return self ::detect ($ tempDir , $ default ) === false ;
170
+ /** @noinspection AdditionOperationOnArraysInspection */
171
+ $ this ->ips += $ ips ;
172
+ return $ this ;
173
+ }
174
+
175
+ /**
176
+ * Shortcut to simple detect Debug mode by all method enabled by Detector mode (argument `$mode`)
177
+ *
178
+ * @param int $mode Enables methods which is used to detect Debug mode
179
+ * @param string|null $tempDir Path to temp directory. Optional, but required when Enabler mode is enabled
180
+ * @param bool|null $default Default value when no method matches
181
+ */
182
+ public static function detect (
183
+ int $ mode = self ::MODE_SIMPLE ,
184
+ ?string $ tempDir = null ,
185
+ ?bool $ default = false
186
+ ): ?bool {
187
+ if ($ tempDir === null && $ mode & self ::MODE_ENABLER ) {
188
+ throw new InvalidArgumentException ('Enabler mode requires `tempDir` argument ' );
189
+ }
190
+
191
+ $ enabler = $ tempDir === null ? null : new Enabler ($ tempDir );
192
+ return (new self ($ mode , $ enabler ))->isDebugMode ($ default );
193
+ }
194
+
195
+ /**
196
+ * Shortcut to simple detect Production mode by all method enabled by Detector mode (argument `$mode`)
197
+ *
198
+ * @param int $mode Enables methods which is used to detect Debug mode
199
+ * @param string|null $tempDir Path to temp directory. Optional, but required when Enabler mode is enabled
200
+ * @param bool|null $default Default value when no method matches
201
+ */
202
+ public static function detectProductionMode (
203
+ int $ mode = self ::MODE_SIMPLE ,
204
+ ?string $ tempDir = null ,
205
+ ?bool $ default = false
206
+ ): ?bool {
207
+ if (is_bool ($ default )) {
208
+ $ default = !$ default ;
209
+ }
210
+
211
+ $ result = self ::detect ($ mode , $ tempDir , $ default );
212
+
213
+ if (is_bool ($ result )) {
214
+ $ result = !$ result ;
215
+ }
216
+
217
+ return $ result ;
133
218
}
134
219
}
0 commit comments