Passed
Push — master ( e7e380...72c582 )
by Tom
04:40
created

Lib::phpBinary()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* this file is part of pipelines */
4
5
namespace Ktomk\Pipelines;
6
7
use UnexpectedValueException;
8
9
class Lib
10
{
11 2
    public static function r(&$v, $d)
12
    {
13 2
        if (isset($v)) {
14 1
            return $v;
15
        }
16
17 1
        return $d;
18
    }
19
20
    /**
21
     * @param mixed $v variable reference
22
     * @param mixed $d [optional]  default value (null)
23
     * @return void
24
     */
25 2
    public static function v(&$v, $d = null)
26
    {
27 2
        if (!isset($v)) {
28 1
            $v = $d;
29
        }
30 2
    }
31
32
    /**
33
     * @return string UUID version 4
34
     */
35 1
    public static function generateUuid()
36
    {
37 1
        return sprintf(
38 1
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
39
40
            // 32 bits for "time_low"
41 1
            mt_rand(0, 0xffff),
42 1
            mt_rand(0, 0xffff),
43
44
            // 16 bits for "time_mid"
45 1
            mt_rand(0, 0xffff),
46
47
            // 16 bits for "time_hi_and_version",
48
            // four most significant bits holds version number 4
49 1
            mt_rand(0, 0x0fff) | 0x4000,
50
51
            // 16 bits, 8 bits for "clk_seq_hi_res",
52
            // 8 bits for "clk_seq_low",
53
            // two most significant bits holds zero and one for variant DCE1.1
54 1
            mt_rand(0, 0x3fff) | 0x8000,
55
56
            // 48 bits for "node"
57 1
            mt_rand(0, 0xffff),
58 1
            mt_rand(0, 0xffff),
59 1
            mt_rand(0, 0xffff)
60
        );
61
    }
62
63 2
    public static function cmd($command, array $arguments)
64
    {
65 2
        $buffer = $command;
66
67 2
        $arguments = call_user_func_array('self::merge', $arguments);
68
69 2
        foreach ($arguments as $argument) {
70 2
            $buffer .= ' ' . self::quoteArg($argument);
71
        }
72
73 2
        return $buffer;
74
    }
75
76
    /**
77
     * quote an argument to preserve its value verbatim when used as
78
     * a utility argument in shell
79
     *
80
     * @param string $argument
81
     * @return string
82
     */
83 8
    public static function quoteArg($argument)
84
    {
85 8
        $parts = explode("'", $argument);
86
87 8
        $buffer = '';
88 8
        foreach ($parts as $index => $part) {
89 8
            $index && $buffer .= "\\'";
90 8
            $safe = preg_match('~^[a-zA-Z0-9,._+@%/-]*$~', $part);
91 8
            $buffer .= $safe ? $part : "'${part}'";
92
        }
93
94
        if ('' === $buffer) {
95
            $buffer = "''";
96
        }
97
98
        return $buffer;
99
    }
100
101
    /**
102
     * Turn multi-line string into an array of lines.
103
     *
104
     * Handles no newline at the end of buffer
105
     *
106
     * @param string $buffer
107
     * @return array|string[]
108
     */
109
    public static function lines($buffer)
110
    {
111 1
        $lines = explode("\n", $buffer);
112 1
        if ($c = count($lines) and '' === $lines[$c - 1]) {
113 1
            array_pop($lines);
114
        }
115
116 1
        return $lines;
117
    }
118
119
    /**
120
     * merge n parameters, if a scalar, turned into array, otherwise must be an array
121
     */
122
    public static function merge()
123
    {
124 4
        if (!$arrays = func_get_args()) {
125 1
            return $arrays;
126
        }
127
128 3
        foreach ($arrays as $key => $value) {
129 3
            if (!is_array($value)) {
130 3
                $arrays[$key] = (array)$value;
131
            }
132
        }
133
134 3
        return call_user_func_array('array_merge', $arrays);
135
    }
136
137
    /**
138
     * Chunk an array of strings based on maximum string length per chunk
139
     *
140
     * @param array|string[] $array
141
     * @param $maxLength
142
     * @param int $overHeadPerEntry
143
     * @return array|array[]
144
     */
145
    public static function arrayChunkByStringLength(array $array, $maxLength, $overHeadPerEntry = 0)
146
    {
147 2
        $chunks = array();
148 2
        $chunkStringLength = 0;
149 2
        $chunk = array();
150
151 2
        foreach ($array as $key => $value) {
152 2
            $entryLen = strlen($value) + $overHeadPerEntry;
153 2
            if ($chunkStringLength + $entryLen > $maxLength) {
154 2
                if (empty($chunk)) {
155 1
                    throw new \InvalidArgumentException(
156 1
                        sprintf(
157 1
                            'maximum length of %d is too little to chunk the array at %s %s (%d chunk(s) so far)',
158 1
                            $maxLength,
159 1
                            is_string($key) ? 'key' : 'index',
160 1
                            is_string($key) ? var_export($key, true) : (int)$key,
161 1
                            count($chunks)
162
                        )
163
                    );
164
                }
165 1
                $chunks[] = $chunk;
166 1
                $chunk = array();
167 1
                $chunkStringLength = 0;
168
            }
169 1
            $chunk[] = $value;
170 1
            $chunkStringLength += $entryLen;
171
        }
172
173 1
        if (!empty($chunk)) {
174 1
            $chunks[] = $chunk;
175
        }
176
177 1
        return $chunks;
178
    }
179
180
    /**
181
     * expand brace "{}" in pattern
182
     *
183
     * @param string $pattern
184
     * @throws \UnexpectedValueException
185
     * @return array of all patterns w/o braces, no duplicates
186
     */
187
    public static function expandBrace($pattern)
188
    {
189 16
        $stack = array($pattern);
190
191 16
        for ($i = 0; isset($stack[$i]); $i++) {
192 16
            $subject = $stack[$i];
193 16
            $result = self::expandBraceInnerMatch($subject, $matches);
194 16
            if (0 === $result) {
195 16
                continue;
196
            }
197
            // match
198 9
            $segments = preg_split('~\\\\.(*SKIP)(*FAIL)|,~', $matches[2]);
199 9
            $segments = array_unique(/** @scrutinizer ignore-type */ $segments);
200 9
            foreach ($segments as $segment) {
201 9
                $permutation = $matches[1] . $segment . $matches[3];
202 9
                in_array($permutation, $stack, true) || $stack[] = $permutation;
203
            }
204 9
            unset($stack[$i]);
205
        }
206
207
        // inline escaped brace characters
208
        $stack = array_map(function ($str) {
209 16
            return strtr($str, array(
210 16
                '\\\\' => '\\\\',
211
                '\\{' => '{', '\\}' => '}', '\\,' => ','
212
            ));
213 16
        }, $stack);
214
215 16
        return array_values($stack);
216
    }
217
218
    /**
219
     * check if path is absolute
220
     *
221
     * @param string $path
222
     * @return bool
223
     */
224
    public static function fsIsAbsolutePath($path)
225
    {
226
        // TODO: a variant with PHP stream wrapper prefix support
227
228 8
        $count = strspn($path, '/', 0, 3) % 2;
229
230 8
        return (bool)$count;
231
    }
232
233
    /**
234
     * check if path is basename
235
     *
236
     * @param string $path
237
     * @return bool
238
     */
239
    public static function fsIsBasename($path)
240
    {
241 5
        if (in_array($path, array('', '.', '..'), true)) {
242 1
            return false;
243
        }
244
245 4
        if (false !== strpos($path, '/')) {
246 3
            return false;
247
        }
248
249 1
        return true;
250
    }
251
252
    /**
253
     * create directory if not yet exists
254
     *
255
     * @param string $path
256
     * @throws \RuntimeException
257
     */
258
    public static function fsMkdir($path)
259
    {
260 2
        if (!is_dir($path)) {
261
            /** @noinspection NestedPositiveIfStatementsInspection */
262 2
            if (!mkdir($path, 0777, true) && !is_dir($path)) {
263
                // @codeCoverageIgnoreStart
264
                throw new \RuntimeException(
265
                    sprintf('Directory "%s" was not created', $path)
266
                );
267
                // @codeCoverageIgnoreEnd
268
            }
269
        }
270 2
    }
271
272
    /**
273
     * @param string $link
274
     */
275
    public static function fsUnlink($link)
276
    {
277 1
        if (is_link($link)) {
278 1
            unlink($link);
279
        }
280 1
    }
281
282
    /**
283
     * create symbolic link, recreate if it exists
284
     *
285
     * @param string $target
286
     * @param string $link
287
     */
288
    public static function fsSymlink($target, $link)
289
    {
290 1
        self::fsUnlink($link);
291 1
        symlink($target, $link);
292 1
    }
293
294
    /**
295
     * locate (readable) file by basename upward all parent directories
296
     *
297
     * @param string $basename
298
     * @param string $directory [optional] directory to operate from, defaults
299
     *               to "." (relative path of present working directory)
300
     * @return null|string
301
     */
302
    public static function fsFileLookUp($basename, $directory = null)
303
    {
304 4
        if ('' === $directory || null === $directory) {
305 1
            $directory = '.';
306
        }
307
308
        for (
309 4
            $dirName = $directory, $old = null;
310 4
            $old !== $dirName;
311 2
            $old = $dirName, $dirName = dirname($dirName)
312
        ) {
313 4
            $test = $dirName . '/' . $basename;
314 4
            if (Lib::fsIsReadableFile($test)) {
315 3
                return $test;
316
            }
317
        }
318
319 1
        return null;
320
    }
321
322
    /**
323
     * @param string $path
324
     * @return bool
325
     */
326
    public static function fsIsStreamUri($path)
327
    {
328 2
        $scheme = parse_url($path, PHP_URL_SCHEME);
329 2
        if (null === $scheme) {
330 1
            return false;
331
        }
332
333 1
        return in_array($scheme, stream_get_wrappers(), true);
334
    }
335
336
    /**
337
     * @param string $path
338
     * @return bool
339
     */
340
    public static function fsIsReadableFile($path)
341
    {
342 6
        if (!is_file($path)) {
343 3
            return false;
344
        }
345
346 4
        return is_readable($path) ?: self::fsCanFopen($path, 'rb');
347
    }
348
349
    /**
350
     * @param string $path
351
     * @param string $mode [optional]
352
     * @return bool
353
     */
354
    public static function fsCanFopen($path, $mode = null)
355
    {
356 2
        if (null === $mode) {
357 1
            $mode = 'rb';
358
        }
359
360 2
        $handle = @fopen($path, $mode);
361 2
        if (false === $handle) {
362 1
            return false;
363
        }
364
365 1
        /** @scrutinizer ignore-unhandled */ @fclose($handle);
366
367 1
        return true;
368
    }
369
370
    /**
371
     * fallback for the php 5.3 version which does not have PHP_BINARY.
372
     *
373
     * @return string
374
     */
375
    public static function phpBinary()
376
    {
377 1
        return defined('PHP_BINARY') ? constant('PHP_BINARY') : PHP_BINDIR . '/php';
378
    }
379
380
    /**
381
     * @param string $subject
382
     * @param array $matches
383
     * @throws UnexpectedValueException
384
     * @return false|int
385
     */
386
    private static function expandBraceInnerMatch($subject, &$matches)
387
    {
388 16
        $result = preg_match_all(
389 16
            '~(\\\\.(*SKIP)(*FAIL)|(?P<token>[,{}]))~',
390 16
            $subject,
391 16
            $lookup,
392 16
            PREG_OFFSET_CAPTURE
393
        );
394
395 16
        if (false === $result) {
396
            throw new UnexpectedValueException('regex pattern failure'); // @codeCoverageIgnore
397
        }
398
399 16
        if (0 === $result) {
400 12
            return $result;
401
        }
402
403 11
        $open = null;
404 11
        $comma = null;
405
406 11
        foreach ($lookup['token'] as $token) {
407 11
            list($type, $pos) = $token;
408 11
            if ('{' === $type) {
409 10
                $open = $token;
410 10
                $comma = null;
411 11
            } elseif (',' === $type) {
412 10
                $comma = $token;
413 11
            } elseif ($open && $comma) {
414
                $matches = array(
415 9
                    $subject,
416 9
                    substr($subject, 0, $open[1]),
417 9
                    substr($subject, $open[1] + 1, $pos - $open[1] - 1),
418 9
                    substr($subject, $pos + 1),
419
                );
420
421 11
                return 1;
422
            }
423
        }
424
425 4
        return 0;
426
    }
427
}
428