Passed
Push — master ( 6c6d7e...b48c4f )
by Marwan
01:39
created

Globals::initialize()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

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