Passed
Push — test ( 2b0c61...4b22aa )
by Tom
03:19
created

_start()   F

Complexity

Conditions 13
Paths 384

Size

Total Lines 50
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 31
nc 384
nop 0
dl 0
loc 50
c 1
b 0
f 0
cc 13
rs 3.4833

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
#!/usr/bin/env php
2
<?php
3
4
/*
5
 * this file is part of pipelines
6
 *
7
 * vendor-licensing - composer packages license information markdown report
8
 *
9
 * usage: script/vendor-licensing.php [--dev] [--] [<composer-dir>]
10
 *
11
 *   --dev            include require-dev packages
12
 *   <composer-dir>  path to directory with the composer.json to use
13
 *
14
 * obtain license information from composer packages
15
 * and turn them into a markdown report for the
16
 * pipelines HTML documentation
17
 *
18
 * markdown is written to stdout by default
19
 */
20
21
function _start()
22
{
23
    // --dev : include require-dev packages
24
    $config['dev'] = false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$config was never initialized. Although not strictly required by PHP, it is generally a good practice to add $config = array(); before regardless.
Loading history...
25
    // <composer-dir> : directory having the composer.json
26
    $config['composer_dir'] = '';
27
    $config['composer_filename'] = 'composer.json';
28
29
    $argv = $_SERVER['argv'];
30
31
    if (isset($argv[1]) && '--dev' === $argv[1]) {
32
        $config['dev'] = true;
33
        array_splice($argv, 1, 1);
34
    }
35
36
    if (isset($argv[1]) && '--' === $argv[1]) {
37
        array_splice($argv, 1, 1);
38
    }
39
40
    if (isset($argv[1])) {
41
        $buffer = realpath(trim($argv[1]));
42
        is_string($buffer) && is_dir($buffer)
43
        or trigger_error(sprintf('not a directory: %s', trim($argv[1])), E_USER_ERROR);
44
        $config['composer_dir'] = $buffer;
45
        unset($buffer);
46
        array_splice($argv, 1, 1);
47
    }
48
49
    isset($argv[1])
50
    and trigger_error(sprintf("unknown option, argument or parameter: '%s'", $argv[1]), E_USER_ERROR);
51
52
    $srcDir = realpath($config['composer_dir']);
53
    is_string($srcDir) && is_dir($srcDir)
54
    or trigger_error(sprintf('composer-dir not a directory: %s', $config['composer_dir']), E_USER_ERROR);
55
56
    $composerFilePath = $srcDir . '/' . $config['composer_filename'];
57
    $project = json_decode((string)@file_get_contents($composerFilePath), true);
58
    is_array($project)
59
    or trigger_error(sprintf("not a composer.json: '%s'", $composerFilePath), E_USER_ERROR);
60
61
    $project += array('require' => array(), 'require-dev' => array());
62
63
    $require = $project['require'];
64
    $config['dev'] && $require += $project['require-dev'];
65
    $pkgs = packages($require, dirname($composerFilePath) . '/vendor');
66
67
    tpl_render_pkgs($pkgs);
68
69
    // done
70
    return 0;
71
}
72
73
register_shutdown_function(function ($_start) {
74
75
    $start = $_SERVER['REQUEST_TIME'];
76
    if (!is_float($start)) {
77
        $start = isset($_SERVER['REQUEST_TIME_FLOAT']) && is_float($_SERVER['REQUEST_TIME_FLOAT'])
0 ignored issues
show
Unused Code introduced by
The assignment to $start is dead and can be removed.
Loading history...
78
            ? $_SERVER['REQUEST_TIME_FLOAT']
79
            : $_start;
80
    }
81
    $_SERVER['REQUEST_TIME'] = (int)$_SERVER['REQUEST_TIME_FLOAT'] = $_start;
82
83
    $argv = $_SERVER['argv'];
84
    if (!is_array($argv)) {
85
        $argv = array();
86
    }
87
    $argv = array_filter($argv, 'is_string');
88
    $_SERVER['argv'] = $argv;
89
90
    set_error_handler(function ($type, $message, $file, $line) {
91
        if ($type === E_USER_ERROR) {
92
            throw new ErrorException($message, 0, $type, $file, $line);
93
        }
94
        return false;
95
    });
96
97
    $error = null;
98
    try {
99
        $result = _start();
100
    } catch (Exception $error) {
101
        vfprintf(STDERR, "fatal: %s\n", array_slice(explode("\n", $error->getMessage()), 0, 1));
102
    }
103
104
    $time = microtime(true) - $_start;
105
    $php = vsprintf('%d.%d.%d', sscanf(PHP_VERSION, '%d.%d.%d'));
0 ignored issues
show
Bug introduced by
It seems like sscanf(PHP_VERSION, '%d.%d.%d') can also be of type integer and null; however, parameter $values of vsprintf() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

105
    $php = vsprintf('%d.%d.%d', /** @scrutinizer ignore-type */ sscanf(PHP_VERSION, '%d.%d.%d'));
Loading history...
106
    fprintf(STDERR, "%s: took %.5fs (php %s)\n", $argv[0], $time, $php);
107
108
    if (false === empty($result)) {
109
        exit(2);
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...
110
    }
111
    exit($error ? 1 : 0);
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...
112
}, microtime(true));
113
114
/**
115
 * render packages as markdown
116
 *
117
 * @param array $pkgs
118
 */
119
function tpl_render_pkgs(array $pkgs)
120
{
121
    foreach ($pkgs as $pkg) {
122
        list($pkg, $lic, $lic_file) = array_values($pkg);
123
?>
124
### <?= tpl_func_name_nice($pkg) ?> (<?= tpl_func_spdx_full($lic) ?>)
125
126
From the package https://packagist.org/packages/<?= $pkg ?>
127
128
129
```
130
<?= strtr(rtrim(file_get_contents($lic_file)), '`', '#'), "\n"; ?>
131
```
132
133
#### SPDX
134
135
* Full Name: `<?= tpl_func_spdx_full($lic) ?>`
136
* Short Identifier: `<?= $lic ?>`
137
* Reference: <?= tpl_func_spdx_url($lic) ?>
138
139
140
<?php
141
    }
142
}
143
144
/**
145
 * SPDX license URL of short identifier
146
 *
147
 * @param string $lic short identifier
148
 *
149
 * @return string license URL
150
 */
151
function tpl_func_spdx_url($lic)
152
{
153
    return sprintf('https://spdx.org/licenses/%s.html', $lic);
154
}
155
156
/**
157
 * SPDX full name of short identifier
158
 *
159
 * @param string $lic short identifier
160
 *
161
 * @return string full license name
162
 */
163
function tpl_func_spdx_full($lic)
164
{
165
    $fuller = array(
166
        'MIT' => 'MIT License',
167
        'BSD-3-Clause' => 'BSD 3-Clause "New" or "Revised" License',
168
    );
169
    if (!isset($fuller[$lic])) {
170
        throw new RuntimeException(sprintf('Unable to resolve SPDX full license for "%s"', $lic));
171
    }
172
    return $fuller[$lic];
173
}
174
175
function tpl_func_name_nice($name)
176
{
177
    list($ven, $lib) = explode('/', $name, 2) + array(1 => null);
178
    return sprintf('%s', ucwords($lib, " \t\r\n\f\v-"));
179
}
180
181
/**
182
 * parse composer requires packages for licensing
183
 *
184
 * @param array $require
185
 * @param string $vendorFolder
186
 *
187
 * @return array packages information regarding licensing
188
 */
189
function packages(array $require, $vendorFolder)
190
{
191
    $packages = array();
192
193
    foreach ($require as $pkg => $ver) {
194
        $dir = $vendorFolder . '/' . $pkg;
195
        if (!is_dir($dir)) { // filter non-project packages, e.g. php and extensions
196
            continue;
197
        }
198
        $composer = find_file_down($dir, 'composer.json');
199
        if (null === $composer) {
200
            throw new UnexpectedValueException(sprintf('Unable to find composer.json in %s', $pkg));
201
        }
202
        $package = json_decode(file_get_contents($composer), true);
203
        $packages[$pkg] = array(
204
            'pkg' => $pkg,
205
            'lic' => $package['license'],
206
            'lic_file' => find_license_file(dirname($composer)),
207
        );
208
    }
209
210
    return $packages;
211
}
212
213
/**
214
 * find a file within a folder and all its subfolders
215
 *
216
 * @param string $folder
217
 * @param string $file
218
 *
219
 * @return string|null file found or null in case not found
220
 */
221
function find_file_down($folder, $file)
222
{
223
    $folders = array($folder);
224
    while ($folder = array_pop($folders)) {
225
        if (is_file($folder . '/' . $file)) {
226
            return $folder . '/' . $file;
227
        }
228
        foreach (array_diff((array)@scandir($folder), array('..', '.')) as $name) {
229
            if (is_dir($folder . '/' . $name)) {
230
                $folders[] = $folder . '/' . $name;
231
            }
232
        }
233
    }
234
235
    return null;
236
}
237
238
/**
239
 * @param string $folder
240
 * @param string|string[] ...$file
241
 *
242
 * @return null|string
243
 */
244
function find_file($folder, $file)
245
{
246
    $files = flatten(array_slice(func_get_args(), 1));
247
    if (empty($files)) {
248
249
        return null;
250
    }
251
252
    foreach ($files as $file) {
0 ignored issues
show
introduced by
$file is overwriting one of the parameters of this function.
Loading history...
253
        if (is_file($folder . '/' . $file)) {
254
255
            return $folder . '/' . $file;
256
        }
257
    }
258
259
    return null;
260
}
261
262
/**
263
 * @param array $args
264
 *
265
 * @return array
266
 */
267
function flatten(array $args)
268
{
269
    $flattened = array();
270
271
    foreach ($args as $arg) {
272
        $values = is_array($arg) ? flatten($arg) : array($arg);
273
        foreach ($values as $value) {
274
            $flattened[] = $value;
275
        }
276
    }
277
278
    return $flattened;
279
}
280
281
/**
282
 * find license file in a folder
283
 *
284
 * @param string $folder
285
 *
286
 * @return string path to license file
287
 */
288
function find_license_file($folder, array $files = array('COPYING', 'LICENSE'))
289
{
290
    $license = find_file($folder, $files);
291
    if (null !== $license) {
292
293
        return $license;
294
    }
295
296
    throw new RuntimeException(sprintf('Unable to find license file in "%s"', $folder));
297
}
298