Issues (94)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

Upgrade to new PHP Analysis Engine

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
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.

Loading history...
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 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

$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)
Loading history...
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 getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
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 getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
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 getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
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.

Loading history...
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 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

$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)
Loading history...
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