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

rotexsoft / versatile-collections / 19055306815

31 Oct 2025 07:53PM UTC coverage: 99.802%. Remained the same
19055306815

push

github

rotimi
Version 7.x tweaks

1008 of 1010 relevant lines covered (99.8%)

14.99 hits per line

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

97.5
/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;
27✔
34
        $return_val = $default_val;
27✔
35

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

38
            if( $obj instanceof stdClass ) {
27✔
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);
15✔
44
                $return_val = $obj_as_array[$property];
15✔
45

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

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

58
                } else {
59

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

71
            } else {
72

73
                $return_val = $obj->{$property};
12✔
74
            }
75
        }
76

77
        return $return_val;
21✔
78
    }
79

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

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

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

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

138
        $keys = \array_keys($array);
21✔
139

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

147
        } catch ( Throwable) {
×
148

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

154
        return $random_key;
21✔
155
    }
156

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

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

184
        if(  $number_of_random_keys > \count($array) ) {
15✔
185

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

194
        $random_keys = [];
12✔
195

196
        for( $i=0; $i < $number_of_random_keys; $i++) {
12✔
197

198
            $current_random_key = null;
12✔
199

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

203
            } while( \in_array($current_random_key, $random_keys, true) );
12✔
204

205
            $random_keys[] = $current_random_key;
12✔
206
        }
207

208
        return $random_keys; 
12✔
209
    }
210

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

222
        var_dump($var);
108✔
223

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

227
        return ($output === false) ? '' : $output;
108✔
228
    }
229

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