Passed
Push — master ( c67eec...d6e32a )
by Tom
04:05
created

find_file_recursive()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 5
eloc 8
c 1
b 0
f 1
nc 5
nop 2
dl 0
loc 15
rs 9.6111
1
#!/usr/bin/env php
2
<?php
3
4
/*
5
 * composer packages license information to markdown
6
 *
7
 * obtain license information from composer packages
8
 * and turn them into a markdown report for the
9
 * pipelines HTML documentation
10
 *
11
 * markdown is written to stdout
12
 *
13
 * usage: script/vendor-licensing.php [--dev] [<composer-dir>]
14
 *
15
 *   --dev            include require-dev packages
16
 *   <composer-dir>  path to directory with the composer.json to use
17
 */
18
19
$config['dev'] = false;
20
$config['composer_dir'] = __DIR__ . '/../../../..';
21
22
if (isset($argv[1]) && '--dev' === $argv[1]) {
23
    $config['dev'] = true;
24
    array_splice($argv, 1, 1);
25
}
26
27
if (isset($argv[1]) && '' !== $argv[1]) {
28
    $config['composer_dir'] = $argv[1];
29
}
30
31
$composerFile = $config['composer_dir'] . '/composer.json';
32
$project = json_decode((string)@file_get_contents($composerFile), true);
33
if (!is_array($project)) {
34
    fprintf(STDERR, "fatal: not a composer.json: '%s'\n", $composerFile);
35
    exit(1);
36
}
37
$project += array('require' => array(), 'require-dev' => array());
38
39
$require = $project['require'];
40
$config['dev'] && $require += $project['require-dev'];
41
$pkgs = packages($require, dirname($composerFile) . '/vendor');
42
43
tpl_render_pkgs($pkgs);
44
45
// done
46
47
/**
48
 * render packages as markdown
49
 *
50
 * @param array $pkgs
51
 */
52
function tpl_render_pkgs(array $pkgs)
53
{
54
    foreach ($pkgs as $pkg) {
55
        list($pkg, $lic, $lic_file) = array_values($pkg);
56
?>
57
### <?= tpl_func_name_nice($pkg) ?> (<?= tpl_func_spdx_full($lic) ?>)
58
59
From the package https://packagist.org/packages/<?= $pkg ?>
60
61
62
```
63
<?= rtrim(file_get_contents($lic_file)), "\n"; ?>
64
```
65
66
#### SPDX
67
68
* Full Name: `<?= tpl_func_spdx_full($lic) ?>`
69
* Short Identifier: `<?= $lic ?>`
70
* Reference: <?= tpl_func_spdx_url($lic) ?>
71
72
73
<?php
74
    }
75
}
76
77
/**
78
 * SPDX license URL of short identifier
79
 *
80
 * @param string $lic short identifier
81
 *
82
 * @return string license URL
83
 */
84
function tpl_func_spdx_url($lic)
85
{
86
    return sprintf('https://spdx.org/licenses/%s.html', $lic);
87
}
88
89
/**
90
 * SPDX full name of short identifier
91
 *
92
 * @param string $lic short identifier
93
 *
94
 * @return string full license name
95
 */
96
function tpl_func_spdx_full($lic)
97
{
98
    $fuller = array(
99
        'MIT' => 'MIT License',
100
        'BSD-3-Clause' => 'BSD 3-Clause "New" or "Revised" License',
101
    );
102
    if (!isset($fuller[$lic])) {
103
        throw new RuntimeException(sprintf('Unable to resolve SPDX full license for "%s"', $lic));
104
    }
105
    return $fuller[$lic];
106
}
107
108
function tpl_func_name_nice($name)
109
{
110
    list($ven, $lib) = explode('/', $name, 2) + array(1 => null);
111
    return sprintf('%s', ucwords($lib, " \t\r\n\f\v-"));
112
}
113
114
/**
115
 * parse composer requires packages for licensing
116
 *
117
 * @param array $require
118
 * @param string $vendorFolder
119
 *
120
 * @return array packages information regarding licensing
121
 */
122
function packages(array $require, $vendorFolder)
123
{
124
    $packages = array();
125
126
    foreach ($require as $pkg => $ver) {
127
        $dir = $vendorFolder . '/' . $pkg;
128
        if (!is_dir($dir)) { // filter non-project packages, e.g. php and extensions
129
            continue;
130
        }
131
        $composer = find_file_recursive($dir, 'composer.json');
132
        if (null === $composer) {
133
            throw new UnexpectedValueException(sprintf('Unable to find composer.json in %s', $pkg));
134
        }
135
        $package = json_decode(file_get_contents($composer), true);
136
        $packages[$pkg] = array(
137
            'pkg' => $pkg,
138
            'lic' => $package['license'],
139
            'lic_file' => find_license_file(dirname($composer)),
140
        );
141
    }
142
143
    return $packages;
144
}
145
146
/**
147
 * find a file within a folder and all its subfolders
148
 *
149
 * @param string $folder
150
 * @param string $file
151
 *
152
 * @return string|null file found or null in case not found
153
 */
154
function find_file_recursive($folder, $file)
155
{
156
    $folders = array($folder);
157
    while ($folder = array_pop($folders)) {
158
        if (is_file($folder . '/' . $file)) {
159
            return $folder . '/' . $file;
160
        }
161
        foreach (array_diff((array)@scandir($folder), array('..', '.')) as $name) {
162
            if (is_dir($folder . '/' . $name)) {
163
                $folders[] = $folder . '/' . $name;
164
            }
165
        }
166
    }
167
168
    return null;
169
}
170
171
/**
172
 * find license file in a folder
173
 *
174
 * @param string $folder
175
 *
176
 * @return string path to license file
177
 */
178
function find_license_file($folder)
179
{
180
    $files = array(
181
        'COPYING',
182
        'LICENSE',
183
    );
184
185
    foreach ($files as $file) {
186
        if (is_file($folder . '/' . $file)) {
187
            return $folder . '/' . $file;
188
        }
189
    }
190
191
    throw new RuntimeException(sprintf('Unable to find license file in "%s"', $folder));
192
}
193