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

rotexsoft / versatile-collections / 21502398707

23 Jan 2026 09:55AM UTC coverage: 99.802%. Remained the same
21502398707

push

github

rotexdegba
Minimum PHP 8.2 refactoring

1 of 1 new or added line in 1 file covered. (100.0%)

2 existing lines in 1 file now uncovered.

1007 of 1009 relevant lines covered (99.8%)

20.0 hits per line

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

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

5
    use Throwable;
6
    use InvalidArgumentException;
7
    use LengthException;
8
    use ReflectionClass;
9
    use ReflectionException;
10
    use RuntimeException;
11
    use stdClass;
12

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

36
        if( object_has_property($obj, $property) ) {
36✔
37

38
            if( $obj instanceof stdClass ) {
36✔
39

40
                // will work for stdClass instances that were created 
41
                // by casting an array with numeric and / or string keys to an object.
42
                // e.g. ( (object)[ 777=>'Some Value', 'a_string_property'=>'Another Value'] )
43
                $obj_as_array = ((array)$obj);
20✔
44
                $return_val = $obj_as_array[$property];
20✔
45

46
            } else if(
47
                \property_exists ($obj, $property) // is either public, protected or private
28✔
48
                && !\array_key_exists($property, \get_object_vars($obj)) // definitely a protected or a private property
28✔
49
            ) {
50
                if( $access_private_or_protected ) {
16✔
51

52
                    // use some reflection gymnastics to retrieve the value
53
                    $reflection_class = new ReflectionClass($obj::class);
8✔
54
                    $property = $reflection_class->getProperty($property);
8✔
55
                    $return_val = $property->getValue($obj);
8✔
56

57
                } else {
58

59
                    // throw exception letting user know that they are
60
                    // trying to access a private or protected value
61
                    $function = __FUNCTION__;
8✔
62
                    $ns = __NAMESPACE__;
8✔
63
                    $obj_type = $obj::class;
8✔
64
                    $msg = "Error [{$ns}::{$function}(...)]:"
8✔
65
                    . " Trying to access a protected or private property named `{$property}` on the instance of `$obj_type` below:" . PHP_EOL . var_to_string($obj)
8✔
66
                    . PHP_EOL . "To access a protected or private property named `{$property}` call `{$ns}::{$function}()` with `true` as the fourth argument.";
8✔
67
                    throw new RuntimeException($msg);
8✔
68
                }
69

70
            } else {
71

72
                $return_val = $obj->{$property};
16✔
73
            }
74
        }
75

76
        return $return_val;
28✔
77
    }
78

79
    /**
80
     * A more robust way than property_exists of checking if an instance of a class
81
     * has a specified property.
82
     * 
83
     * @throws InvalidArgumentException
84
     *
85
     * @noinspection PhpMissingReturnTypeInspection
86
     */
87
    function object_has_property(object $obj, string|int $property): bool
88
    {
89
        $property = ''.$property;
68✔
90

91
        return (
68✔
92
                    \property_exists($obj, $property) // check if property is public, protected or private
68✔
93
                    ||
68✔
94
                    (
68✔
95
                        \method_exists($obj, '__isset')
68✔
96
                        && \method_exists($obj, '__get')
68✔
97
                        && $obj->__isset($property)
68✔
98
                    ) // check if property is accessible via magic method
68✔
99
                    ||
68✔
100
                    (
68✔
101
                        \array_key_exists( $property, ((array)$obj) )
68✔
102
                    ) // works for arrays with numeric keys that were
68✔
103
                      // cast into an object. E.g $item === ((object)[777=>'boo'])
68✔
104
                      // Also detects properties that are not defined in the class
68✔
105
                      // (i.e. they were not explicitly defined as public, private
68✔
106
                      // or protected) but were assigned during run-time, like 
68✔
107
                      // properties assigned to instances of \stdClass (which 
68✔
108
                      // could also be assigned to instances of any php class)
68✔
109
               );
68✔
110
    }
111

112
    /**
113
     * A potentially more cryptographically secure way  (as opposed to array_rand) 
114
     * of getting a random key from an array.
115
     * If the system contains sources of randomness like: 
116
     * On Windows, » CryptGenRandom() will always be used.
117
     * On Linux, the » getrandom(2) syscall will be used if available.
118
     * On other platforms, /dev/urandom will be used.
119
     * If none of the aforementioned sources are available, then array_rand will be used.
120
     *
121
     * @param array $array array from which a random key is to be extracted
122
     *  
123
     * @return string|int a random key from the specified array
124
     *  
125
     * @throws LengthException
126
     */
127
    function random_array_key(array $array): string|int
128
    {
129
        if( \count($array) <= 0 ) {
28✔
130

131
            $function = __FUNCTION__;
4✔
132
            $ns = __NAMESPACE__;
4✔
133
            $msg = "Error [{$ns}::{$function}(...)]: You cannot request a random key from an empty array.";
4✔
134
            throw new LengthException($msg);
4✔
135
        }
136

137
        $keys = \array_keys($array);
28✔
138

139
        try {
140
            // random_int is more cryptographically secure than array_rand
141
            $min = 0;
28✔
142
            $max = \count($keys) - 1;
28✔
143
            $random_index = \random_int( $min, $max );
28✔
144
            $random_key = $keys[$random_index];
28✔
145

UNCOV
146
        } catch ( Throwable) {
×
147

148
            // fallback to array_rand since an error / exception occurred
149
            // while trying to use random_int
UNCOV
150
            $random_key = \array_rand($array, 1);
×
151
        }
152

153
        return $random_key;
28✔
154
    }
155

156
    /**
157
     * A potentially more cryptographically secure way  (as opposed to array_rand) 
158
     * of getting unique random keys from an array.
159
     *  
160
     * If the system contains sources of randomness like: 
161
     * On Windows, » CryptGenRandom() will always be used.
162
     * On Linux, the » getrandom(2) syscall will be used if available.
163
     * On other platforms, /dev/urandom will be used.
164
     * If none of the aforementioned sources are available, then array_rand will be used.
165
     *  
166
     * @param array $array array from which a random keys are to be extracted
167
     * @param int $number_of_random_keys number of unique random keys to return
168
     *  
169
     * @return array an array of random keys
170
     * @throws LengthException
171
     * @throws InvalidArgumentException
172
     */
173
    function random_array_keys(array $array, int $number_of_random_keys = 1): array
174
    {
175
        if( \count($array) <= 0 ) {
24✔
176

177
            $function = __FUNCTION__;
8✔
178
            $ns = __NAMESPACE__;
8✔
179
            $msg = "Error [{$ns}::{$function}(...)]: You cannot request random keys from an empty array.";
8✔
180
            throw new LengthException($msg);
8✔
181
        }
182

183
        if(  $number_of_random_keys > \count($array) ) {
20✔
184

185
            $function = __FUNCTION__;
4✔
186
            $ns = __NAMESPACE__;
4✔
187
            $num_items = \count($array);
4✔
188
            $msg = "Error [{$ns}::{$function}(...)]:"
4✔
189
            . " You requested {$number_of_random_keys} key(s), but there are only {$num_items} keys available.";
4✔
190
            throw new InvalidArgumentException($msg);
4✔
191
        }
192

193
        $random_keys = [];
16✔
194

195
        for( $i=0; $i < $number_of_random_keys; $i++) {
16✔
196

197
            $current_random_key = null;
16✔
198

199
            do { // loop to ensure uniqueness of selected random keys
200
                $current_random_key = random_array_key($array);
16✔
201

202
            } while( \in_array($current_random_key, $random_keys, true) );
16✔
203

204
            $random_keys[] = $current_random_key;
16✔
205
        }
206

207
        return $random_keys; 
16✔
208
    }
209

210
    /**
211
     * Generate a (screen/user)-friendly string representation of a variable.
212
     *  
213
     * @return string a (screen / user)-friendly string representation of a variable
214
     * 
215
     * @psalm-suppress ForbiddenCode
216
     */
217
    function var_to_string(mixed $var): string 
218
    {
219
        ob_start(); // Start capturing the output
144✔
220

221
        var_dump($var);
144✔
222

223
        // Get the captured output, close the buffer & return the captured output
224
        $output = ob_get_clean();
144✔
225

226
        return ($output === false) ? '' : $output;
144✔
227
    }
228

229
    /**
230
     * Generate a (screen/user)-friendly string representation of a variable and print it out to the screen.
231
     */
232
    function dump_var(mixed $var): void 
233
    {
234
        $line_breaker = (PHP_SAPI === 'cli') ? PHP_EOL : '<br>';
4✔
235
        echo var_to_string($var). $line_breaker . $line_breaker;
4✔
236
    }
237

238
} // 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