Passed
Push — master ( e92a23...70bf0a )
by Marwan
08:52
created

Globals.php$0 ➔ get()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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