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

rotexsoft / versatile-collections / 9815431462

14 Mar 2024 09:55PM UTC coverage: 99.802%. Remained the same
9815431462

push

github

rotimi
PHP 8.1 minimum version prep

15 of 16 new or added lines in 3 files covered. (93.75%)

1 existing line in 1 file now uncovered.

1009 of 1011 relevant lines covered (99.8%)

29.99 hits per line

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

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

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

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

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

52
                    // use some reflection gymnastics to retrieve the value
53
                    $reflection_class = new ReflectionClass($obj::class);
12✔
54
                    $property = $reflection_class->getProperty($property);
12✔
55
                    $property->setAccessible(true);
12✔
56
                    $return_val = $property->getValue($obj); //$property->setAccessible(false);
12✔
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__;
12✔
63
                    $ns = __NAMESPACE__;
12✔
64
                    $obj_type = $obj::class;
12✔
65
                    $msg = "Error [{$ns}::{$function}(...)]:"
12✔
66
                    . " Trying to access a protected or private property named `{$property}` on the instance of `$obj_type` below:" . PHP_EOL . var_to_string($obj)
12✔
67
                    . PHP_EOL . "To access a protected or private property named `{$property}` call `{$ns}::{$function}()` with `true` as the fourth argument.";
12✔
68
                    throw new RuntimeException($msg);
16✔
69
                }
70

71
            } else {
72

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

77
        return $return_val;
42✔
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;
102✔
91

92
        return (
102✔
93
                    \property_exists($obj, $property) // check if property is public, protected or private
102✔
94
                    ||
102✔
95
                    (
102✔
96
                        \method_exists($obj, '__isset')
102✔
97
                        && \method_exists($obj, '__get')
102✔
98
                        && $obj->__isset($property)
102✔
99
                    ) // check if property is accessible via magic method
102✔
100
                    ||
102✔
101
                    (
102✔
102
                        \array_key_exists( $property, ((array)$obj) )
102✔
103
                    ) // works for arrays with numeric keys that were
102✔
104
                      // cast into an object. E.g $item === ((object)[777=>'boo'])
102✔
105
                      // Also detects properties that are not defined in the class
102✔
106
                      // (i.e. they were not explicitly defined as public, private
102✔
107
                      // or protected) but were assigned during run-time, like 
102✔
108
                      // properties assigned to instances of \stdClass (which 
102✔
109
                      // could also be assigned to instances of any php class)
102✔
110
               );
102✔
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|null a random key from the specified array
125
     *  
126
     * @throws LengthException
127
     */
128
    function random_array_key(array $array): string|int|null
129
    {
130
        if( \count($array) <= 0 ) {
42✔
131

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

138
        $keys = \array_keys($array);
42✔
139
        $random_key = null;
42✔
140

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

NEW
148
        } catch ( Throwable) {
×
149

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

155
        return $random_key;
42✔
156
    }
157

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

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

185
        if(  $number_of_random_keys > \count($array) ) {
30✔
186

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

195
        $random_keys = [];
24✔
196

197
        for( $i=0; $i < $number_of_random_keys; $i++) {
24✔
198

199
            $current_random_key = null;
24✔
200

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

204
            } while( \in_array($current_random_key, $random_keys, true) );
24✔
205

206
            $random_keys[] = $current_random_key;
24✔
207
        }
208

209
        return $random_keys; 
24✔
210
    }
211

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

223
        var_dump($var);
216✔
224

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

228
        return ($output === false) ? '' : $output;
216✔
229
    }
230

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

© 2025 Coveralls, Inc