• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rotexsoft / versatile-collections / 6042672854

31 Aug 2023 09:32PM UTC coverage: 98.445% (-0.09%) from 98.532%
6042672854

push

github

rotimi
Upgraded rector to 0.18

1076 of 1093 relevant lines covered (98.44%)

28.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.14
/src/helper-functions.php
1
<?php /** @noinspection DuplicatedCode */ /** @noinspection PhpFullyQualifiedNameUsageInspection */
2
declare(strict_types=1);
3
namespace VersatileCollections {
4

5
    use Error;
6
    use Exception;
7
    use InvalidArgumentException;
8
    use LengthException;
9
    use ReflectionClass;
10
    use ReflectionException;
11
    use RuntimeException;
12
    use stdClass;
13
    use TypeError;
14

15
    /**
16
     *
17
     * A robust way of retrieving the value of a specified property in
18
     * an instance of a class.
19
     *
20
     * Works with \stdClass objects created from arrays with numeric key(s)
21
     * (the value of the propertie(s) with numeric key(s) in such \stdClass
22
     * objects will be retrieved by this function).
23
     *
24
     * @param object $obj
25
     * @param string|int $property
26
     * @param mixed $default_val
27
     * @param bool $access_private_or_protected true if value associated with private or protected property should be returned.
28
     *                                          If false is specified and you try to access a private or protected property, a
29
     *                                          \RuntimeException will be thrown.
30
     * @return mixed
31
     *
32
     * @throws InvalidArgumentException
33
     * @throws RuntimeException
34
     * @throws ReflectionException
35
     *
36
     * @noinspection DuplicatedCode
37
     * @psalm-suppress DocblockTypeContradiction
38
     */
39
    function get_object_property_value(object $obj, $property, $default_val=null, bool $access_private_or_protected=false) {
40

41
        if( !\is_string($property) && !\is_int($property) ) {
60✔
42

43
            $function = __FUNCTION__;
6✔
44
            $ns = __NAMESPACE__;
6✔
45
            $property_type = Utils::gettype($property);
6✔
46
            $msg = "Error [{$ns}::{$function}(...)]:"
6✔
47
            . " String or Int expected as second argument, `$property_type` given.";
6✔
48
            throw new InvalidArgumentException($msg);
6✔
49
        }
50

51
        $property = ''.$property;
54✔
52
        $return_val = $default_val;
54✔
53

54
        if( object_has_property($obj, $property) ) {
54✔
55

56
            if( $obj instanceof stdClass ) {
54✔
57

58
                // will work for stdClass instances that were created 
59
                // by casting an array with numeric and / or string keys to an object.
60
                // e.g. ( (object)[ 777=>'Some Value', 'a_string_property'=>'Another Value'] )
61
                $obj_as_array = ((array)$obj);
30✔
62
                $return_val = $obj_as_array[$property];
30✔
63

64
            } else if(
65
                \property_exists ($obj, $property) // is either public, protected or private
42✔
66
                && !\array_key_exists($property, \get_object_vars($obj)) // definitely a protected or a private property
42✔
67
            ) {
68
                if( $access_private_or_protected ) {
24✔
69

70
                    // use some reflection gymnastics to retrieve the value
71
                    $reflection_class = new ReflectionClass(\get_class($obj));
12✔
72
                    $property = $reflection_class->getProperty($property);
12✔
73
                    $property->setAccessible(true);
12✔
74
                    $return_val = $property->getValue($obj); //$property->setAccessible(false);
12✔
75

76
                } else {
77

78
                    // throw exception letting user know that they are
79
                    // trying to access a private or protected value
80
                    $function = __FUNCTION__;
12✔
81
                    $ns = __NAMESPACE__;
12✔
82
                    $obj_type = \get_class($obj);
12✔
83
                    $msg = "Error [{$ns}::{$function}(...)]:"
12✔
84
                    . " Trying to access a protected or private property named `{$property}` on the instance of `$obj_type` below:" . PHP_EOL . var_to_string($obj)
12✔
85
                    . PHP_EOL . "To access a protected or private property named `{$property}` call `{$ns}::{$function}()` with `true` as the fourth argument.";
12✔
86
                    throw new RuntimeException($msg);
20✔
87
                }
88

89
            } else {
90

91
                $return_val = $obj->{$property};
24✔
92
            }
93
        }
94

95
        return $return_val;
42✔
96
    }
97

98
    /**
99
     * 
100
     * A more robust way than property_exists of checking if an instance of a class
101
     * has a specified property.
102
     * 
103
     * @param object $obj
104
     * @param string|int $property
105
     * 
106
     * 
107
     * @throws InvalidArgumentException
108
     *
109
     * @noinspection PhpMissingReturnTypeInspection
110
     * @psalm-suppress DocblockTypeContradiction
111
     */
112
    function object_has_property(object $obj, $property): bool {
113

114
        if( !\is_string($property) && !\is_int($property) ) {
108✔
115

116
            $function = __FUNCTION__;
6✔
117
            $ns = __NAMESPACE__;
6✔
118
            $property_type = Utils::gettype($property);
6✔
119
            $msg = "Error [{$ns}::{$function}(...)]:"
6✔
120
            . " String or Int expected as second argument, `$property_type` given.";
6✔
121
            throw new InvalidArgumentException($msg);
6✔
122
        }
123

124
        $property = ''.$property;
102✔
125

126
        return (
102✔
127
                    \property_exists($obj, $property) // check if property is public, protected or private
102✔
128
                    ||
102✔
129
                    (
102✔
130
                        \method_exists($obj, '__isset')
102✔
131
                        && \method_exists($obj, '__get')
102✔
132
                        && $obj->__isset($property)
102✔
133
                    ) // check if property is accessible via magic method
102✔
134
                    ||
102✔
135
                    (
102✔
136
                        \array_key_exists( $property, ((array)$obj) )
102✔
137
                    ) // works for arrays with numeric keys that were
102✔
138
                      // cast into an object. E.g $item === ((object)[777=>'boo'])
102✔
139
                      // Also detects properties that are not defined in the class
102✔
140
                      // (i.e. they were not explicitly defined as public, private
102✔
141
                      // or protected) but were assigned during run-time, like 
102✔
142
                      // properties assigned to instances of \stdClass (which 
102✔
143
                      // could also be assigned to instances of any php class)
102✔
144
               );
102✔
145
    }
146

147
    /**
148
     *  
149
     * A potentially more cryptographically secure way  (as opposed to array_rand) 
150
     * of getting a random key from an array.
151
     * If the system contains sources of randomness like: 
152
     * On Windows, » CryptGenRandom() will always be used.
153
     * On Linux, the » getrandom(2) syscall will be used if available.
154
     * On other platforms, /dev/urandom will be used.
155
     * If none of the aforementioned sources are available, then array_rand will be used.
156
     *
157
     * @param array $array array from which a random key is to be extracted
158
     *  
159
     * @return string|int|null a random key from the specified array
160
     *  
161
     * @throws LengthException
162
     *  
163
     */
164
    function random_array_key(array $array) {
165

166
        if( \count($array) <= 0 ) {
42✔
167

168
            $function = __FUNCTION__;
6✔
169
            $ns = __NAMESPACE__;
6✔
170
            $msg = "Error [{$ns}::{$function}(...)]: You cannot request a random key from an empty array.";
6✔
171
            throw new LengthException($msg);
6✔
172
        }
173

174
        $error_occurred = false;
42✔
175
        $keys = \array_keys($array);
42✔
176
        $random_key = null;
42✔
177

178
        try {
179
            // random_int is more cryptographically secure than array_rand
180
            $min = 0;
42✔
181
            $max = \count($keys) - 1;
42✔
182
            $random_index = \random_int( $min, $max );
42✔
183
            $random_key = $keys[$random_index];
42✔
184

185
        } catch ( TypeError $e) {
×
186

187
            // random_int: If invalid parameters are given, a TypeError will be thrown.
188
            // This is okay, so long as `Error` is caught before `Exception`.
189
            // Probably will never occur since $min and $max above will always be ints.
190
            $error_occurred = true;
×
191

192
        } catch ( Error $e) {
×
193

194
            // random_int: If max is less than min, an Error will be thrown.
195
            // This is required, if you do not need to do anything just rethrow.
196
            // Probably will never occur since $min and $max above will always have $min < $max.
197
            $error_occurred = true;
×
198

199
        } catch ( Exception $e) {
×
200

201
            // random_int: If an appropriate source of randomness cannot be found, an Exception will be thrown.
202
            // This is optional and maybe omitted if you do not want to handle errors
203
            // during generation.
204
            // Hard to consistently test this since it's an internal 
205
            // random number generator specific logic error.
206
            $error_occurred = true;
×
207
        }
208

209
        if( $error_occurred ) {
42✔
210

211
            // fallback to array_rand since an error / exception occurred
212
            // while trying to use random_int
213
            $random_key = \array_rand($array);
×
214
        }
215

216
        return $random_key;
42✔
217
    }
218

219
    /**
220
     *  
221
     * A potentially more cryptographically secure way  (as opposed to array_rand) 
222
     * of getting unique random keys from an array.
223
     *  
224
     * If the system contains sources of randomness like: 
225
     * On Windows, » CryptGenRandom() will always be used.
226
     * On Linux, the » getrandom(2) syscall will be used if available.
227
     * On other platforms, /dev/urandom will be used.
228
     * If none of the aforementioned sources are available, then array_rand will be used.
229
     *  
230
     * @param array $array array from which a random keys are to be extracted
231
     * @param int $number_of_random_keys number of unique random keys to return
232
     *  
233
     * @return array an array of random keys
234
     * @throws LengthException
235
     * @throws InvalidArgumentException
236
     */
237
    function random_array_keys(array $array, int $number_of_random_keys = 1): array {
238

239
        if( \count($array) <= 0 ) {
36✔
240

241
            $function = __FUNCTION__;
12✔
242
            $ns = __NAMESPACE__;
12✔
243
            $msg = "Error [{$ns}::{$function}(...)]: You cannot request random keys from an empty array.";
12✔
244
            throw new LengthException($msg);
12✔
245
        }
246

247
        if(  $number_of_random_keys > \count($array) ) {
30✔
248

249
            $function = __FUNCTION__;
6✔
250
            $ns = __NAMESPACE__;
6✔
251
            $num_items = \count($array);
6✔
252
            $msg = "Error [{$ns}::{$function}(...)]:"
6✔
253
            . " You requested {$number_of_random_keys} key(s), but there are only {$num_items} keys available.";
6✔
254
            throw new InvalidArgumentException($msg);
6✔
255
        }
256

257
        $random_keys = [];
24✔
258

259
        for( $i=0; $i < $number_of_random_keys; $i++) {
24✔
260

261
            $current_random_key = null;
24✔
262

263
            do { // loop to ensure uniqueness of selected random keys
264
                $current_random_key = random_array_key($array);
24✔
265

266
            } while( \in_array($current_random_key, $random_keys, true) );
24✔
267

268
            $random_keys[] = $current_random_key;
24✔
269
        }
270

271
        return $random_keys; 
24✔
272
    }
273

274
    /**
275
     *  
276
     * Generate a (screen/user)-friendly string representation of a variable. 
277
     *  
278
     * @param mixed $var
279
     *  
280
     * @return string a (screen / user)-friendly string representation of a variable
281
     *  
282
     */
283
    function var_to_string($var): string {
284

285
        // Start capturing the output
286
        ob_start();
252✔
287

288
        /** @psalm-suppress ForbiddenCode */
289
        var_dump($var);
252✔
290

291
        // Get the captured output, close the buffer & return the captured output
292
        $output = ob_get_clean();
252✔
293

294
        return ($output === false) ? '' : $output;
252✔
295
    }
296

297
    /**
298
     *  
299
     * Generate a (screen/user)-friendly string representation of a variable and print it out to the screen. 
300
     *  
301
     * @param mixed $var
302
     *  
303
     * @return void
304
     * @noRector
305
     *  
306
     */
307
    function dump_var($var): void {
308

309
        $line_breaker = (PHP_SAPI === 'cli') ? PHP_EOL : '<br>';
6✔
310
        echo var_to_string($var). $line_breaker . $line_breaker;
6✔
311
    }
312
} // namespace VersatileCollections
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc