Passed
Pull Request — master (#429)
by Théo
02:35
created

parallel_map()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 39
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 39
rs 9.8333
c 0
b 0
f 0
cc 3
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box;
16
17
use function Amp\ParallelFunctions\parallelMap;
18
use Amp\Promise;
19
use Amp\Success;
20
use function array_key_exists;
21
use function array_map;
22
use Assert\Assertion;
23
use function bin2hex;
24
use function class_alias;
25
use function class_exists;
26
use Closure;
27
use function constant;
28
use function define;
29
use function defined;
30
use ErrorException;
31
use function extension_loaded;
32
use function floor;
33
use function function_exists;
34
use KevinGH\Box\Console\IO\IO;
35
use KevinGH\Box\Console\Php\PhpSettingsHandler;
36
use function KevinGH\Box\FileSystem\copy;
37
use function log;
38
use function number_format;
39
use Opis\Closure\SerializableClosure;
40
use PackageVersions\Versions;
41
use Phar;
42
use function posix_getrlimit;
43
use function posix_setrlimit;
44
use Prophecy\Promise\ReturnPromise;
45
use function random_bytes;
46
use function sprintf;
47
use function str_replace;
48
use function strlen;
49
use function strtolower;
50
use Symfony\Component\Console\Helper\Helper;
51
use Symfony\Component\Console\Logger\ConsoleLogger;
52
use Symfony\Component\Console\Output\OutputInterface;
53
use parallel\Future;
0 ignored issues
show
Bug introduced by
The type parallel\Future was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
54
55
/**
56
 * @private
57
 */
58
function get_box_version(): string
59
{
60
    $rawVersion = Versions::getVersion('humbug/box');
61
62
    [$prettyVersion, $commitHash] = explode('@', $rawVersion);
63
64
    return $prettyVersion.'@'.substr($commitHash, 0, 7);
65
}
66
67
/**
68
 * @private
69
 *
70
 * @return array<string,int>
71
 */
72
function get_phar_compression_algorithms(): array
73
{
74
    static $algorithms = [
75
        'GZ' => Phar::GZ,
76
        'BZ2' => Phar::BZ2,
77
        'NONE' => Phar::NONE,
78
    ];
79
80
    return $algorithms;
81
}
82
83
/**
84
 * @private
85
 */
86
function get_phar_compression_algorithm_extension(int $algorithm): ?string
87
{
88
    static $extensions = [
89
        Phar::GZ => 'zlib',
90
        Phar::BZ2 => 'bz2',
91
        Phar::NONE => null,
92
    ];
93
94
    Assertion::true(
95
        array_key_exists($algorithm, $extensions),
96
        sprintf('Unknown compression algorithm code "%d"', $algorithm)
97
    );
98
99
    return $extensions[$algorithm];
100
}
101
102
/**
103
 * @private
104
 *
105
 * @return array<string,int>
106
 */
107
function get_phar_signing_algorithms(): array
108
{
109
    static $algorithms = [
110
        'MD5' => Phar::MD5,
111
        'SHA1' => Phar::SHA1,
112
        'SHA256' => Phar::SHA256,
113
        'SHA512' => Phar::SHA512,
114
        'OPENSSL' => Phar::OPENSSL,
115
    ];
116
117
    return $algorithms;
118
}
119
120
/**
121
 * @private
122
 */
123
function format_size(int $size, int $decimals = 2): string
124
{
125
    if (-1 === $size) {
126
        return '-1';
127
    }
128
129
    $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
130
131
    $power = $size > 0 ? (int) floor(log($size, 1024)) : 0;
132
133
    return sprintf(
134
        '%s%s',
135
        number_format(
136
            $size / (1024 ** $power),
137
            $decimals,
138
            '.',
139
            ','
140
        ),
141
        $units[$power]
142
    );
143
}
144
145
/**
146
 * @private
147
 */
148
function memory_to_bytes(string $value): int
149
{
150
    $unit = strtolower($value[strlen($value) - 1]);
151
152
    $bytes = (int) $value;
153
154
    switch ($unit) {
155
        case 'g':
156
            $bytes *= 1024;
157
        // no break (cumulative multiplier)
158
        case 'm':
159
            $bytes *= 1024;
160
        // no break (cumulative multiplier)
161
        case 'k':
162
            $bytes *= 1024;
163
    }
164
165
    return $bytes;
166
}
167
168
/**
169
 * @private
170
 */
171
function format_time(float $secs): string
172
{
173
    return str_replace(
174
        ' ',
175
        '',
176
        Helper::formatTime($secs)
177
    );
178
}
179
180
/**
181
 * @private
182
 */
183
function register_aliases(): void
184
{
185
    // Exposes the finder used by PHP-Scoper PHAR to allow its usage in the configuration file.
186
    if (false === class_exists(\Isolated\Symfony\Component\Finder\Finder::class)) {
187
        class_alias(\Symfony\Component\Finder\Finder::class, \Isolated\Symfony\Component\Finder\Finder::class);
188
    }
189
190
    // Register compactors aliases
191
    if (false === class_exists(\Herrera\Box\Compactor\Json::class, false)) {
192
        class_alias(\KevinGH\Box\Compactor\Json::class, \Herrera\Box\Compactor\Json::class);
193
    }
194
195
    if (false === class_exists(\Herrera\Box\Compactor\Php::class, false)) {
196
        class_alias(\KevinGH\Box\Compactor\Php::class, \Herrera\Box\Compactor\Php::class);
197
    }
198
}
199
200
/**
201
 * @private
202
 */
203
function disable_parallel_processing(): void
204
{
205
    if (false === defined(_NO_PARALLEL_PROCESSING)) {
206
        define(_NO_PARALLEL_PROCESSING, true);
207
    }
208
}
209
210
/**
211
 * @private
212
 */
213
function is_parallel_processing_enabled(): bool
214
{
215
    return false === defined(_NO_PARALLEL_PROCESSING) || false === constant(_NO_PARALLEL_PROCESSING);
216
}
217
218
/**
219
 * @private
220
 *
221
 * @return string Random 12 characters long (plus the prefix) string composed of a-z characters and digits
222
 */
223
function unique_id(string $prefix): string
224
{
225
    return $prefix.bin2hex(random_bytes(6));
226
}
227
228
/**
229
 * @private
230
 */
231
function create_temporary_phar(string $file): string
232
{
233
    $tmpFile = sys_get_temp_dir().'/'.unique_id('').basename($file);
234
235
    if ('' === pathinfo($file, PATHINFO_EXTENSION)) {
236
        $tmpFile .= '.phar';
237
    }
238
239
    copy($file, $tmpFile, true);
240
241
    return $tmpFile;
242
}
243
244
/**
245
 * @private
246
 */
247
function check_php_settings(IO $io): void
248
{
249
    (new PhpSettingsHandler(
250
        new ConsoleLogger(
251
            $io->getOutput()
252
        )
253
    ))->check();
254
}
255
256
/**
257
 * @private
258
 */
259
function noop(): Closure
260
{
261
    return static function (): void {};
262
}
263
264
/**
265
 * Converts errors to exceptions.
266
 *
267
 * @private
268
 */
269
function register_error_handler(): void
270
{
271
    set_error_handler(
272
        static function (int $code, string $message, string $file = '', int $line = -1): void {
273
            if (error_reporting() & $code) {
274
                throw new ErrorException($message, 0, $code, $file, $line);
275
            }
276
        }
277
    );
278
}
279
280
/**
281
 * Bumps the maximum number of open file descriptor if necessary.
282
 *
283
 * @return Closure callable to call to restore the original maximum number of open files descriptors
284
 */
285
function bump_open_file_descriptor_limit(Box $box, IO $io): Closure
286
{
287
    $filesCount = count($box) + 128;  // Add a little extra for good measure
288
289
    if (false === function_exists('posix_getrlimit') || false === function_exists('posix_setrlimit')) {
290
        $io->writeln(
291
            '<info>[debug] Could not check the maximum number of open file descriptors: the functions "posix_getrlimit()" and '
292
            .'"posix_setrlimit" could not be found.</info>',
293
            OutputInterface::VERBOSITY_DEBUG
294
        );
295
296
        return static function (): void {};
297
    }
298
299
    $softLimit = posix_getrlimit()['soft openfiles'];
300
    $hardLimit = posix_getrlimit()['hard openfiles'];
301
302
    if ($softLimit >= $filesCount) {
303
        return static function (): void {};
304
    }
305
306
    $io->writeln(
307
        sprintf(
308
            '<info>[debug] Increased the maximum number of open file descriptors from ("%s", "%s") to ("%s", "%s")'
309
            .'</info>',
310
            $softLimit,
311
            $hardLimit,
312
            $filesCount,
313
            'unlimited'
314
        ),
315
        OutputInterface::VERBOSITY_DEBUG
316
    );
317
318
    posix_setrlimit(
319
        POSIX_RLIMIT_NOFILE,
320
        $filesCount,
321
        'unlimited' === $hardLimit ? POSIX_RLIMIT_INFINITY : $hardLimit
322
    );
323
324
    return static function () use ($io, $softLimit, $hardLimit): void {
325
        if (function_exists('posix_setrlimit') && isset($softLimit, $hardLimit)) {
326
            posix_setrlimit(
327
                POSIX_RLIMIT_NOFILE,
328
                $softLimit,
329
                'unlimited' === $hardLimit ? POSIX_RLIMIT_INFINITY : $hardLimit
330
            );
331
332
            $io->writeln(
333
                '<info>[debug] Restored the maximum number of open file descriptors</info>',
334
                OutputInterface::VERBOSITY_DEBUG
335
            );
336
        }
337
    };
338
}
339
340
function parallel_map(array $array, Closure $callable): Promise
341
{
342
    if (false === extension_loaded('parallel')) {
343
        return parallelMap($array, $callable);
344
    }
345
346
//    $events = new \parallel\Events();
347
348
//    foreach ($array as $item) {
349
//
350
//        $events->addFuture(\parallel\run(static function () use ($callable, $item) {
351
//            $callable($item);
352
//        }));
353
//    }
354
355
//    $event = $events->poll();
356
357
    $item = current($array);
358
359
    $future  = \parallel\run(static function() use ($callable, $item) {
0 ignored issues
show
Bug introduced by
The function run was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

359
    $future  = /** @scrutinizer ignore-call */ \parallel\run(static function() use ($callable, $item) {
Loading history...
360
        return $callable($item);
361
    });
362
363
    \var_dump($future->value());
0 ignored issues
show
Security Debugging Code introduced by
var_dump($future->value()) looks like debug code. Are you sure you do not want to remove it?
Loading history...
364
die;
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return Amp\Promise. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
365
366
    $item = current($array);
0 ignored issues
show
Unused Code introduced by
$item = current($array) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
367
368
    $future = \parallel\run(static function () use ($callable, $item) {
369
        $callable($item);
370
    });
371
372
    \var_dump($future->value());die;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
373
374
    while (null === $event = $events->poll()) {
375
        // Continue
376
    }
377
378
    return new Success($event->value);
379
}
380