Passed
Push — master ( 28c60c...6c6d7e )
by Marwan
10:15
created

Globals::__call()

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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