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 |
|
0 ignored issues
–
show
|
|||
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
Consider using a different name than the imported variable
$key , 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)
![]() |
|||
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 |
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
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 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.