This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php namespace nyx\utils; |
||
2 | |||
3 | /** |
||
4 | * Func |
||
5 | * |
||
6 | * Utilities related to functions/methods/callables/closures. |
||
7 | * |
||
8 | * @package Nyx\Utils\Func |
||
9 | * @version 0.0.5 |
||
10 | * @author Michal Chojnacki <[email protected]> |
||
11 | * @copyright 2012-2016 Nyx Dev Team |
||
12 | * @link http://docs.muyo.io/nyx/utils/func.html |
||
13 | * @todo Decide: Return a custom value instead of null when a Closure prevents the invocation of a callable? |
||
14 | * @todo Decide: Add Func::timeout()? |
||
15 | */ |
||
16 | class Func |
||
17 | { |
||
18 | /** |
||
19 | * The traits of the Func class. |
||
20 | */ |
||
21 | use traits\StaticallyExtendable; |
||
22 | |||
23 | /** |
||
24 | * Special constant used to pass through the arguments from a main wrapped callable to a related callable. Concrete |
||
25 | * behaviour depends on the method being used, therefore check the method docs for actual usage info - self::unless() |
||
26 | * and self::when(). |
||
27 | */ |
||
28 | const PASSTHROUGH = "__passthrough__"; |
||
29 | |||
30 | /** |
||
31 | * @var array The in-memory results cache for self::memoize(). |
||
32 | */ |
||
33 | private static $memory = []; |
||
34 | |||
35 | /** |
||
36 | * Creates a Closure that, when called, ensures the wrapped callable only gets invoked after being called |
||
37 | * at least the given number of $times. |
||
38 | * |
||
39 | * @param int $times The number of times the function should get called before being executed. |
||
40 | * @param callable $callback The callable to wrap. |
||
41 | * @return \Closure The wrapper. |
||
42 | */ |
||
43 | View Code Duplication | public static function after(int $times = 1, callable $callback) : \Closure |
|
44 | { |
||
45 | return function(...$args) use ($callback, $times) { |
||
46 | static $count = 0; |
||
47 | |||
48 | if (++$count >= $times) { |
||
49 | return $callback(...$args); |
||
50 | } |
||
51 | }; |
||
52 | } |
||
53 | |||
54 | /** |
||
55 | * Returns a Closure which will return the subsequent given value (argument to this method) on each call. |
||
56 | * While this is primarily a utility for strings, it can be used with any type of values. |
||
57 | * |
||
58 | * When the Closure gets called with false as its argument, it will return the current internal value without |
||
59 | * alternating the next time (ie. the same value will be returned with the next call). |
||
60 | * |
||
61 | * @param mixed ...$between Two or more values to alternate between, given as separate arguments. |
||
62 | * @return \Closure |
||
63 | */ |
||
64 | public static function alternate(...$between) : \Closure |
||
65 | { |
||
66 | return function($next = true) use ($between) { |
||
67 | static $i = 0; |
||
68 | return $between[($next ? $i++ : $i) % count($between)]; |
||
69 | }; |
||
70 | } |
||
71 | |||
72 | /** |
||
73 | * Returns a Closure which, once invoked, applies each given callable to the result of the previous callable, |
||
74 | * in right-to-left order. As such, the arguments passed to the Closure are passed through to the last |
||
75 | * callable given to the composition. |
||
76 | * |
||
77 | * Example: Func::compose(f, g, h)(x) is the equivalent of f(g(h(x))). |
||
78 | * |
||
79 | * @param callable[] ...$callables The callables to compose. |
||
80 | * @return \Closure |
||
81 | */ |
||
82 | public static function compose(callable ...$callables) : \Closure |
||
83 | { |
||
84 | // Reverse the order of the given callables outside of the Closure. With the assumption |
||
85 | // the Closure may get invoked n > 1 times, this shaves off some execution time. |
||
86 | $callables = array_reverse($callables); |
||
87 | |||
88 | return function(...$args) use ($callables) { |
||
89 | foreach ($callables as $callable) { |
||
90 | $args = [$callable(...$args)]; |
||
91 | } |
||
92 | |||
93 | return current($args); |
||
94 | }; |
||
95 | } |
||
96 | |||
97 | /** |
||
98 | * Generates a hash (signature) for a given callable with the given arguments. |
||
99 | * |
||
100 | * @param callable $callable The callable to hash. |
||
101 | * @param array $args The arguments to hash. |
||
102 | * @return string The hash. |
||
103 | */ |
||
104 | public static function hash(callable $callable, array $args) : string |
||
105 | { |
||
106 | if ($callable instanceof \Closure) { |
||
107 | $callable = var_export($callable, true); |
||
108 | } elseif (is_array($callable)) { |
||
109 | $callable = is_string($callable[0]) ? implode('::', $callable) : $callable[1]; |
||
110 | } |
||
111 | |||
112 | return md5(implode('_', [$callable, var_export($args, true)])); |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * Creates a Closure that memoizes the result of the wrapped callable and returns it once the wrapper gets |
||
117 | * called. |
||
118 | * |
||
119 | * @param callable $callback The callable to wrap. |
||
120 | * @param callable|string $key - When a (resolver) callable is given, it will be invoked with 1 |
||
121 | * or more arguments: the wrapped callable and any arguments |
||
122 | * passed to the wrapped callable (variadic). Its return value |
||
123 | * will be cast to a string and used as the cache key for the result. |
||
124 | * - When any other value except for null is given, it will be cast to |
||
125 | * a string and used as the cache key. |
||
126 | * - When not given, self::hash() will be used to create a hash |
||
127 | * of the call signature to use as key. |
||
128 | * @return \Closure The wrapper. |
||
129 | * @todo Expose the cached result inside the callable (limited to the same |
||
130 | * callable?) |
||
131 | * @todo Use the first argument for the callable as cache key if not given? |
||
132 | */ |
||
133 | public static function memoize(callable $callback, $key = null) : \Closure |
||
134 | { |
||
135 | return function(...$args) use ($callback, $key) { |
||
136 | |||
137 | // Determine which cache key to use. |
||
138 | $key = null === $key |
||
0 ignored issues
–
show
|
|||
139 | ? static::hash($callback, $args) |
||
140 | : (string) (is_callable($key) ? $key($callback, ...$args) : $key); |
||
141 | |||
142 | // If we don't already have a hit for the cache key, populate it with the result of invocation. |
||
143 | if (!array_key_exists($key, static::$memory)) { |
||
0 ignored issues
–
show
Since
$memory is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self , or increasing the visibility of $memory to at least protected.
Let’s assume you have a class which uses late-static binding: class YourClass
{
private static $someVariable;
public static function getSomeVariable()
{
return static::$someVariable;
}
}
The code above will run fine in your PHP runtime. However, if you now create a
sub-class and call the class YourSubClass extends YourClass { }
YourSubClass::getSomeVariable(); // Will cause an access error.
In the case above, it makes sense to update class SomeClass
{
private static $someVariable;
public static function getSomeVariable()
{
return self::$someVariable; // self works fine with private.
}
}
![]() |
|||
144 | static::$memory[$key] = $callback(...$args); |
||
0 ignored issues
–
show
Since
$memory is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self , or increasing the visibility of $memory to at least protected.
Let’s assume you have a class which uses late-static binding: class YourClass
{
private static $someVariable;
public static function getSomeVariable()
{
return static::$someVariable;
}
}
The code above will run fine in your PHP runtime. However, if you now create a
sub-class and call the class YourSubClass extends YourClass { }
YourSubClass::getSomeVariable(); // Will cause an access error.
In the case above, it makes sense to update class SomeClass
{
private static $someVariable;
public static function getSomeVariable()
{
return self::$someVariable; // self works fine with private.
}
}
![]() |
|||
145 | } |
||
146 | |||
147 | return static::$memory[$key]; |
||
0 ignored issues
–
show
Since
$memory is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self , or increasing the visibility of $memory to at least protected.
Let’s assume you have a class which uses late-static binding: class YourClass
{
private static $someVariable;
public static function getSomeVariable()
{
return static::$someVariable;
}
}
The code above will run fine in your PHP runtime. However, if you now create a
sub-class and call the class YourSubClass extends YourClass { }
YourSubClass::getSomeVariable(); // Will cause an access error.
In the case above, it makes sense to update class SomeClass
{
private static $someVariable;
public static function getSomeVariable()
{
return self::$someVariable; // self works fine with private.
}
}
![]() |
|||
148 | }; |
||
149 | } |
||
150 | |||
151 | /** |
||
152 | * Creates a Closure that, when called, ensures the wrapped callable only gets invoked once. |
||
153 | * |
||
154 | * @param callable $callback The callable to wrap. |
||
155 | * @return \Closure The wrapper. |
||
156 | */ |
||
157 | public static function once(callable $callback) : \Closure |
||
158 | { |
||
159 | return static::only(1, $callback); |
||
160 | } |
||
161 | |||
162 | /** |
||
163 | * Creates a Closure that, when called, ensures the wrapped callable may only get invoked $times times at most. |
||
164 | * |
||
165 | * @param int $times The number of times the function may get invoked at most. |
||
166 | * @param callable $callback The callable to wrap. |
||
167 | * @return \Closure The wrapper. |
||
168 | */ |
||
169 | View Code Duplication | public static function only(int $times = 1, callable $callback) : \Closure |
|
170 | { |
||
171 | return function(...$args) use ($callback, $times) { |
||
172 | // Keep track of how many times the Closure was already called. |
||
173 | static $called = 0; |
||
174 | |||
175 | // Invoke the callback when we didn't hit our limit yet. |
||
176 | if ($times >= ++$called) { |
||
177 | return $callback(...$args); |
||
178 | } |
||
179 | }; |
||
180 | } |
||
181 | |||
182 | /** |
||
183 | * Creates a Closure that, when called, invokes the wrapped callable with any additional partial arguments |
||
184 | * prepended to those provided to the new Closure. |
||
185 | * |
||
186 | * @param callable $callback The callable to wrap. |
||
187 | * @param mixed ...$prependedArgs The arguments to prepend to the callback. |
||
188 | * @return \Closure The wrapper. |
||
189 | */ |
||
190 | public static function partial(callable $callback, ...$prependedArgs) : \Closure |
||
191 | { |
||
192 | return function(...$args) use ($callback, $prependedArgs) { |
||
193 | return $callback(...$prependedArgs, ...$args); |
||
194 | }; |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Creates a Closure that, when called, invokes the wrapped callable with any additional partial arguments |
||
199 | * appended to those provided to the new Closure. |
||
200 | * |
||
201 | * @param callable $callback The callable to wrap. |
||
202 | * @param mixed ...$appendedArgs The arguments to append to the callback. |
||
203 | * @return \Closure The wrapper. |
||
204 | */ |
||
205 | public static function partialRight(callable $callback, ...$appendedArgs) : \Closure |
||
206 | { |
||
207 | return function(...$args) use ($callback, $appendedArgs) { |
||
208 | return $callback(...$args, ...$appendedArgs); |
||
209 | }; |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * Wraps a callable in a Closure to ensure the callable can only be executed once every $wait milliseconds. |
||
214 | * Subsequent calls to the wrapped callable before the wait time allows another execution run will return the |
||
215 | * result of the previous execution. |
||
216 | * |
||
217 | * @param callable $callback The callable to wrap. |
||
218 | * @param int $wait The time in milliseconds that must pass before the callable can be executed |
||
219 | * again after each call. |
||
220 | * @return \Closure The wrapper. |
||
221 | */ |
||
222 | public static function throttle(callable $callback, int $wait = null) : \Closure |
||
223 | { |
||
224 | return function(...$args) use ($callback, $wait) { |
||
225 | static $timer = 0; |
||
226 | static $result = null; |
||
227 | |||
228 | if ($timer <= $microtime = microtime(true)) { |
||
229 | // Update the internal timer. |
||
230 | $timer = $microtime + $wait / 1000; |
||
231 | |||
232 | // And call the actual callable. |
||
233 | $result = $callback(...$args); |
||
234 | } |
||
235 | |||
236 | return $result; |
||
237 | }; |
||
238 | } |
||
239 | |||
240 | /** |
||
241 | * Retries the execution of a callable for a given number of $times, optionally delaying each retry |
||
242 | * by $delay seconds. The only condition of failure triggering a retry is when the callable throws |
||
243 | * an Exception. |
||
244 | * |
||
245 | * Unlike most other methods in this utility, this method does not return a Closure due to its nature. |
||
246 | * If you require a "retrier" function, you could of course just wrap the call to Func::retry inside a Closure |
||
247 | * of your own. |
||
248 | * |
||
249 | * Note: Delay is *blocking*. When running though an event loop, instead of using the delay, you should |
||
250 | * schedule retries of your code on loop ticks or via timers (unless, of course, blocking is of no concern |
||
251 | * or even desired). |
||
252 | * |
||
253 | * @param callable $callback The callable to invoke. |
||
254 | * @param int $times The number of times the callable should be invoked upon failure. |
||
255 | * @param float $delay The delay between each retry, in seconds. |
||
256 | * @return mixed The result of the callable's invocation (upon success). |
||
257 | * @throws \Exception When the last allowed retry fails (the type of the exception is a re-throw |
||
258 | * of the last exception thrown by the callable). |
||
259 | */ |
||
260 | public static function retry(callable $callback, int $times = 1, float $delay = null) |
||
261 | { |
||
262 | retry: try { |
||
263 | return $callback(); |
||
264 | } catch (\Exception $exception) { |
||
265 | if (0 < $times--) { |
||
266 | if (isset($delay)) { |
||
267 | usleep($delay * 1000000); |
||
268 | } |
||
269 | |||
270 | goto retry; |
||
271 | } |
||
272 | |||
273 | throw $exception; |
||
274 | } |
||
275 | } |
||
276 | |||
277 | /** |
||
278 | * Wraps a callable in a Closure to only allow it to be invoked when the truth $test callable returns a falsy value. |
||
279 | * |
||
280 | * @param callable $test The truth test. |
||
281 | * @param callable $callback The callable to wrap. |
||
282 | * @param mixed ...$testArgs The arguments to pass to the truth test. If self::PASSTHROUGH gets passed |
||
283 | * (as the first argument, so anything past that gets ignored) the same |
||
284 | * arguments passed to the callable will also be passed to the truth test. |
||
285 | * @return \Closure The wrapper. |
||
286 | */ |
||
287 | public static function unless(callable $test, callable $callback, ...$testArgs) : \Closure |
||
288 | { |
||
289 | return static::whenInternal($test, $callback, false, ...$testArgs); |
||
290 | } |
||
291 | |||
292 | /** |
||
293 | * Wraps a callable in a Closure to only allow it to be invoked when the truth $test callable returns a truthy value. |
||
294 | * |
||
295 | * @param callable $test The truth test. |
||
296 | * @param callable $callback The callable to wrap. |
||
297 | * @param mixed ...$testArgs The arguments to pass to the truth test. If self::PASSTHROUGH gets passed |
||
298 | * (as the first argument, so anything past that gets ignored) the same |
||
299 | * arguments passed to the callable will also be passed to the truth test. |
||
300 | * @return \Closure The wrapper. |
||
301 | */ |
||
302 | public static function when(callable $test, callable $callback, ...$testArgs) : \Closure |
||
303 | { |
||
304 | return static::whenInternal($test, $callback, true, ...$testArgs); |
||
305 | } |
||
306 | |||
307 | /** |
||
308 | * Wraps a callable in a Closure to only allow it to be invoked when the $test callable returns a truthy or falsy |
||
309 | * value. Used internally by self::unless() and self::when() to reduce some code duplication. |
||
310 | * |
||
311 | * @param callable $test The truth test. |
||
312 | * @param callable $callback The callable to wrap. |
||
313 | * @param bool $expect The boolean to expect to allow the callable to be invoked. |
||
314 | * @param mixed ...$testArgs The arguments to pass to the truth test. If self::PASSTHROUGH gets passed |
||
315 | * (as the first argument, so anything past that gets ignored) the same |
||
316 | * arguments passed to the callable will also be passed to the truth test. |
||
317 | * @return \Closure The wrapper. |
||
318 | */ |
||
319 | protected static function whenInternal(callable $test, callable $callback, $expect = true, ...$testArgs) : \Closure |
||
320 | { |
||
321 | return function(...$callbackArgs) use ($callback, $test, $testArgs, $expect) { |
||
322 | $testArgs = (isset($testArgs[0]) && $testArgs[0] === self::PASSTHROUGH) ? $callbackArgs : $testArgs; |
||
0 ignored issues
–
show
Consider using a different name than the imported variable
$testArgs , or did you forget to import by reference?
It seems like you are assigning to a variable which was imported through a For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope. Change not visible in outer-scope$x = 1;
$callable = function() use ($x) {
$x = 2; // Not visible in outer scope. If you would like this, how
// about using a different variable name than $x?
};
$callable();
var_dump($x); // integer(1)
Change visible in outer-scope$x = 1;
$callable = function() use (&$x) {
$x = 2;
};
$callable();
var_dump($x); // integer(2)
![]() |
|||
323 | |||
324 | // Loose comparison on purpose to make this more elastic. |
||
325 | if ($expect == $test(...$testArgs)) { |
||
326 | return $callback(...$callbackArgs); |
||
327 | } |
||
328 | }; |
||
329 | } |
||
330 | } |
||
331 |
It seems like you are assigning to a variable which was imported through a
use
statement which was not imported by reference.For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.
Change not visible in outer-scope
Change visible in outer-scope