Globals::__get()
last analyzed

Size

Total Lines 41
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 20
c 4
b 0
f 0
nc 6
nop 1
dl 0
loc 41
ccs 14
cts 14
cp 1

5 Methods

Rating   Name   Duplication   Size   Complexity  
A Globals.php$0 ➔ has() 0 4 1
A Globals.php$0 ➔ set() 0 4 1
A Globals.php$0 ➔ __construct() 0 3 1
A Globals.php$0 ➔ getAll() 0 3 1
A Globals.php$0 ➔ get() 0 3 1
1
<?php
2
3
/**
4
 * @author Marwan Al-Soltany <[email protected]>
5
 * @copyright Marwan Al-Soltany 2021
6
 * For the full copyright and license information, please view
7
 * the LICENSE file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace MAKS\Velox\Backend;
13
14
use MAKS\Velox\Backend\Exception;
15
use MAKS\Velox\Backend\Session;
16
use MAKS\Velox\Helper\Misc;
17
18
/**
19
 * A class that serves as an abstraction/wrapper to work with superglobals.
20
 *
21
 * @package Velox\Backend
22
 * @since 1.0.0
23
 * @api
24
 *
25
 * @method static mixed getGet(string $key = null) Gets a value from $_GET. Dot-notation can be used for nested values.
26
 * @method static mixed getPost(string $key = null) Gets a value from $_POST. Dot-notation can be used for nested values.
27
 * @method static mixed getFiles(string $key = null) Gets a value from $_FILES. Dot-notation can be used for nested values.
28
 * @method static mixed getCookie(string $key = null) Gets a value from $_COOKIE. Dot-notation can be used for nested values.
29
 * @method static mixed getSession(string $key = null) Gets a value from $_SESSION. Dot-notation can be used for nested values.
30
 * @method static mixed getRequest(string $key = null) Gets a value from $_REQUEST. Dot-notation can be used for nested values.
31
 * @method static mixed getServer(string $key = null) Gets a value from $_SERVER. Dot-notation can be used for nested values.
32
 * @method static mixed getEnv(string $key = null) Gets a value from $_ENV. Dot-notation can be used for nested values.
33
 * @method static static setGet(string $key, $value) Sets a value in $_GET. Dot-notation can be used for nested values.
34
 * @method static static setPost(string $key, $value) Sets a value in $_POST. Dot-notation can be used for nested values.
35
 * @method static static setFiles(string $key, $value) Sets a value in $_FILES. Dot-notation can be used for nested values.
36
 * @method static static setCookie(string $key, $value) Sets a value in $_COOKIE. Dot-notation can be used for nested values.
37
 * @method static static setSession(string $key, $value) Sets a value in $_SESSION. Dot-notation can be used for nested values.
38
 * @method static static setRequest(string $key, $value) Sets a value in $_REQUEST. Dot-notation can be used for nested values.
39
 * @method static static setServer(string $key, $value) Sets a value in $_SERVER. Dot-notation can be used for nested values.
40
 * @method static static setEnv(string $key, $value) Sets a value in $_ENV. Dot-notation can be used for nested values.
41
 * @method static mixed cutGet(string $key = null) Cuts a value from $_GET. Dot-notation can be used for nested values.
42
 * @method static mixed cutPost(string $key = null) Cuts a value from $_POST. Dot-notation can be used for nested values.
43
 * @method static mixed cutFiles(string $key = null) Cuts a value from $_FILES. Dot-notation can be used for nested values.
44
 * @method static mixed cutCookie(string $key = null) Cuts a value from $_COOKIE. Dot-notation can be used for nested values.
45
 * @method static mixed cutSession(string $key = null) Cuts a value from $_SESSION. Dot-notation can be used for nested values.
46
 * @method static mixed cutRequest(string $key = null) Cuts a value from $_REQUEST. Dot-notation can be used for nested values.
47
 * @method static mixed cutServer(string $key = null) Cuts a value from $_SERVER. Dot-notation can be used for nested values.
48
 * @method static mixed cutEnv(string $key = null) Cuts a value from $_ENV. Dot-notation can be used for nested values.
49
 *
50
 * @property object $get A class around the superglobal `$_GET` that has the methods `has($key)`, `get($key, $default)`, `set($key, $value)`, and `getAll()`.
51
 * @property object $post A class around the superglobal `$_POST` that has the methods `has($key)`, `get($key, $default)`, `set($key, $value)`, and `getAll()`.
52
 * @property object $files A class around the superglobal `$_FILES` that has the methods `has($key)`, `get($key, $default)`, `set($key, $value)`, and `getAll()`.
53
 * @property object $cookie A class around the superglobal `$_COOKIE` that has the methods `has($key)`, `get($key, $default)`, `set($key, $value)`, and `getAll()`.
54
 * @property object $session A class around the superglobal `$_SESSION` that has the methods `has($key)`, `get($key, $default)`, `set($key, $value)`, and `getAll()`.
55
 * @property object $request A class around the superglobal `$_REQUEST` that has the methods `has($key)`, `get($key, $default)`, `set($key, $value)`, and `getAll()`.
56
 * @property object $server A class around the superglobal `$_SERVER` that has the methods `has($key)`, `get($key, $default)`, `set($key, $value)`, and `getAll()`.
57
 * @property object $env A class around the superglobal `$_ENV` that has the methods `has($key)`, `get($key, $default)`, `set($key, $value)`, and `getAll()`.
58
 */
59
final class Globals
60
{
61
    public const GLOBALS = [
62
        '_GET'     => '_GET',
63
        '_POST'    => '_POST',
64
        '_FILES'   => '_FILES',
65
        '_COOKIE'  => '_COOKIE',
66
        '_SESSION' => '_SESSION',
67
        '_REQUEST' => '_REQUEST',
68
        '_SERVER'  => '_SERVER',
69
        '_ENV'     => '_ENV',
70
    ];
71
72
73
    /**
74
     * This array holds an anonymous class that acts as a wrapper for each superglobal.
75
     */
76
    protected static array $globals;
77
78
    protected static bool $isInitialized = false;
79
80
    private static array $_GET;
81
    private static array $_POST;
82
    private static array $_FILES;
83
    private static array $_COOKIE;
84
    private static array $_SESSION;
85
    private static array $_REQUEST;
86
    private static array $_SERVER;
87
    private static array $_ENV;
88
89
90
    /**
91
     * Initializes class internal state from superglobals and returns an instance o it.
92
     *
93
     * @return static
94
     */
95 1
    public static function instance()
96
    {
97 1
        static::initialize();
98
99 1
        return new static();
100
    }
101
102
    /**
103
     * Initializes class internal state from superglobals.
104
     *
105
     * @return void
106
     */
107 59
    public static function initialize(): void
108
    {
109 59
        if (!static::$isInitialized) {
110
            // $_SESSION is a special case, unlike other superglobals which are arrays by default,
111
            // $_SESSION has the value null, we need to start it first in order to be able to reference it
112
            // otherwise we will be referencing an array that is not referencing the actual $_SESSION superglobal.
113 1
            Session::start();
114
115 1
            // this is done to initialize the superglobals
116
            // see https://www.php.net/manual/en/ini.core.php#ini.auto-globals-jit
117 1
            is_array($_REQUEST);
118
            is_array($_SERVER);
119
            is_array($_ENV);
120 1
121
            foreach (self::GLOBALS as $global) {
122
                global $$global;
123
                self::$$global = isset($$global) ? self::$$global = &$$global : [];
124
            }
125
126
            static::$isInitialized = true;
127
        }
128
    }
129
130
    /**
131
     * Gets a value from the specified superglobal.
132
     *
133
     * @param string $name The superglobal name to get the value from. Can be written in any case with or without the leading underscore.
134 30
     * @param string $key The array element to get from the superglobal. Dot-notation can be used with nested arrays.
135
     *
136 30
     * @return mixed
137
     *
138 30
     * @throws \OutOfBoundsException If the passed name is not a superglobal.
139
     */
140 29
    public static function get(string $name, string $key = null)
141 28
    {
142
        static::initialize();
143
144 2
        $name = static::getValidNameOrFail($name);
145
146
        if ($key !== null) {
147
            return Misc::getArrayValueByKey(self::$$name, $key, null);
148
        }
149
150
        return self::$$name;
151
    }
152
153
    /**
154
     * Sets a value in the specified superglobal.
155
     *
156
     * @param string $name The superglobal name to set the value in. Can be written in any case with or without the leading underscore.
157
     * @param string $key The array element to set in the superglobal. Dot-notation can be used with nested arrays.
158 32
     * @param mixed $value The value to set.
159
     *
160 32
     * @return static
161
     *
162 32
     * @throws \OutOfBoundsException If the passed name is not a superglobal.
163
     */
164 31
    public static function set(string $name, string $key, $value)
165
    {
166 31
        static::initialize();
167
168
        $name = static::getValidNameOrFail($name);
169
170
        Misc::setArrayValueByKey(self::$$name, $key, $value);
171
172
        return new static();
173
    }
174
175
    /**
176
     * Cuts a value from the specified superglobal. The value will be returned and the key will be unset from the superglobal.
177
     *
178
     * @param string $name The superglobal name to get the value from. Can be written in any case with or without the leading underscore.
179 17
     * @param string $key The array element to get from the superglobal. Dot-notation can be used with nested arrays.
180
     *
181 17
     * @return static
182
     *
183 17
     * @throws \OutOfBoundsException If the passed name is not a superglobal.
184
     */
185 17
    public static function cut(string $name, string $key)
186
    {
187
        static::initialize();
188
189
        $name = static::getValidNameOrFail($name);
190
191
        return Misc::cutArrayValueByKey(self::$$name, $key, null);
192
    }
193 1
194
    /**
195 1
     * Returns all superglobals.
196
     *
197 1
     * @return array
198 1
     */
199 1
    public static function getAll(): array
200
    {
201
        static::initialize();
202 1
203
        $globals = [];
204
        foreach (self::GLOBALS as $global) {
205
            $globals[$global] = self::$$global;
206
        }
207
208
        return $globals;
209
    }
210
211
    /**
212
     * Returns a valid superglobal name from the passed name.
213
     *
214 48
     * @param string $name
215
     *
216 48
     * @return string
217
     *
218 48
     * @throws \OutOfBoundsException
219 6
     */
220
    private static function getValidNameOrFail(string $name): string
221 6
    {
222
        $variable = '_' . trim(strtoupper($name), '_');
223 6
224
        if (!in_array($variable, self::GLOBALS)) {
225
            $available = implode(', ', self::GLOBALS);
226
227 43
            Exception::throw(
228
                'UnknownSuperglobalException:OutOfBoundsException',
229
                "There is no PHP superglobal with the name '{$name}'. Available superglobals are: [{$available}]"
230
            );
231
        }
232
233
        return $variable;
234 53
    }
235
236
237
    /**
238
     * Class constructor.
239 53
     */
240
    final public function __construct()
241
    {
242
        // the constructor is final to allow to the use of
243
        // "return new static()" without caring about class dependencies.
244
245 1
        $this->initialize();
246
    }
247
248 1
    /**
249 1
     * Aliases getters and setter for class members.
250
     */
251 1
    public function __get(string $name)
252 1
    {
253
        try {
254
            $name   = static::getValidNameOrFail($name);
255 1
            $global = &self::$$name;
256
257
            if (isset(static::$globals[$name])) {
258
                return static::$globals[$name];
259 1
            }
260
261
            return static::$globals[$name] = new class ($global) {
262
                private $self;
263 1
                public function __construct(&$self)
264 1
                {
265
                    $this->self = &$self;
266
                }
267
                public function has(string $key)
268 1
                {
269
                    $value = Misc::getArrayValueByKey($this->self, $key, null);
270
                    return isset($value);
271
                }
272 1
                public function get(string $key, $default = null)
273 1
                {
274
                    return Misc::getArrayValueByKey($this->self, $key, $default);
275
                }
276
                public function set(string $key, $value)
277 1
                {
278
                    Misc::setArrayValueByKey($this->self, $key, $value);
279
                    return $this;
280 1
                }
281 1
                public function getAll(): array
282
                {
283 1
                    return $this->self;
284 1
                }
285
            };
286
        } catch (\Exception $error) {
287
            Exception::throw(
288
                'UndefinedPropertyException:OutOfBoundsException',
289
                sprintf('Call to undefined property %s::$%s', static::class, $name),
290
                $error->getCode(),
291
                $error
292
            );
293 7
        }
294
    }
295 7
296
    /**
297
     * Allows static methods handled by `self::__callStatic()` to be accessible via object operator `->`.
298
     */
299
    public function __call(string $method, array $arguments)
300
    {
301 42
        return static::__callStatic($method, $arguments);
302
    }
303
304 42
    /**
305 42
     * Aliases getters and setter for class members.
306 42
     */
307 42
    public static function __callStatic(string $name, array $arguments)
308
    {
309
        try {
310 3
            if (preg_match('/^([gs]et|cut)([_]{0,1}[a-z0-9]+)$/i', $name, $matches)) {
311 3
                return forward_static_call_array(
312
                    [static::class, $matches[1]],
313 3
                    [static::getValidNameOrFail($matches[2]), ...$arguments]
314 3
                );
315
            }
316
        } catch (\Exception $error) {
317
            Exception::throw(
318
                'UndefinedMethodException:BadMethodCallException',
319
                sprintf('Call to undefined method %s::%s', static::class, $name),
320
                $error->getCode(),
321
                $error
322
            );
323
        }
324
    }
325
}
326