Completed
Push — master ( dcddf7...d2fb4d )
by Théo
05:16 queued 03:22
created

HandleAddPrefix   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 225
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 80.48%

Importance

Changes 0
Metric Value
dl 0
loc 225
ccs 66
cts 82
cp 0.8048
rs 10
c 0
b 0
f 0
wmc 20
lcom 1
cbo 6

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
B __invoke() 0 24 2
B createGlobalWhitelister() 0 21 5
B retrieveFiles() 0 69 6
A scopeFiles() 0 16 2
B scopeFile() 0 37 4
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the humbug/php-scoper package.
7
 *
8
 * Copyright (c) 2017 Théo FIDRY <[email protected]>,
9
 *                    Pádraic Brady <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Humbug\PhpScoper\Handler;
16
17
use Closure;
18
use Humbug\PhpScoper\Logger\ConsoleLogger;
19
use Humbug\PhpScoper\Scoper;
20
use Humbug\PhpScoper\Throwable\Exception\ParsingException;
21
use Humbug\PhpScoper\Throwable\Exception\RuntimeException;
22
use SplFileInfo;
23
use Symfony\Component\Filesystem\Filesystem;
24
use Symfony\Component\Finder\Finder;
25
use Throwable;
26
use function Humbug\PhpScoper\get_common_path;
27
28
/**
29
 * @final
30
 */
31
class HandleAddPrefix
32
{
33
    private $fileSystem;
34
    private $scoper;
35
36 10
    public function __construct(Scoper $scoper)
37
    {
38 10
        $this->fileSystem = new Filesystem();
39 10
        $this->scoper = $scoper;
40
    }
41
42
    /**
43
     * Apply prefix to all the code found in the given paths, AKA scope all the files found.
44
     *
45
     * @param string              $prefix                   e.g. 'Foo'
46
     * @param string[]            $paths                    List of files (absolute paths) which will be scoped
47
     * @param string              $output                   Absolute path to the output directory
48
     * @param callable[]          $patchers
49
     * @param string[]            $whitelist                List of classes that will not be scoped
50
     * @param string[]|callable[] $globalNamespaceWhitelist
51
     * @param bool                $stopOnFailure
52
     * @param ConsoleLogger       $logger
53
     */
54 10
    public function __invoke(
55
        string $prefix,
56
        array $paths,
57
        string $output,
58
        array $patchers,
59
        array $whitelist,
60
        array $globalNamespaceWhitelist,
61
        bool $stopOnFailure,
62
        ConsoleLogger $logger
63
    ) {
64 10
        $this->fileSystem->mkdir($output);
65
66
        try {
67 10
            $files = $this->retrieveFiles($paths, $output);
68
69 9
            $globalWhitelister = $this->createGlobalWhitelister($globalNamespaceWhitelist);
70
71 9
            $this->scopeFiles($files, $prefix, $patchers, $whitelist, $globalWhitelister, $stopOnFailure, $logger);
72 3
        } catch (Throwable $throwable) {
73 3
            $this->fileSystem->remove($output);
74
75 3
            throw $throwable;
76
        }
77
    }
78
79
    /**
80
     * @param string[]|callable[] $globalNamespaceWhitelist
81
     *
82
     * @return Closure
83
     */
84 9
    private function createGlobalWhitelister(array $globalNamespaceWhitelist): Closure
85
    {
86
        return function (string $className) use ($globalNamespaceWhitelist): bool {
87
            foreach ($globalNamespaceWhitelist as $whitelister) {
88
                if (is_string($whitelister)) {
89
                    if ($className === $whitelister) {
90
                        return true;
91
                    } else {
92
                        continue;
93
                    }
94
                }
95
96
                /** @var callable $whitelister */
97
                if (true === $whitelister($className)) {
98
                    return true;
99
                }
100
            }
101
102
            return false;
103 9
        };
104
    }
105
106
    /**
107
     * @param string[] $paths
108
     * @param string   $output
109
     *
110
     * @return string[]
111
     */
112 10
    private function retrieveFiles(array $paths, string $output): array
113
    {
114 10
        $pathsToSearch = [];
115 10
        $filesToAppend = [];
116
117 10
        foreach ($paths as $path) {
118 10
            if (false === file_exists($path)) {
119 1
                throw new RuntimeException(
120 1
                    sprintf(
121 1
                        'Could not find the file "%s".',
122 1
                        $path
123
                    )
124
                );
125
            }
126
127 9
            if (is_dir($path)) {
128 3
                $pathsToSearch[] = $path;
129
            } else {
130 9
                $filesToAppend[] = $path;
131
            }
132
        }
133
134 9
        $finder = new Finder();
135
136 9
        $finder->files()
137 9
            ->in($pathsToSearch)
138 9
            ->append($filesToAppend)
139 9
            ->sortByName()
140
        ;
141
142 9
        $files = array_values(
143 9
            array_map(
144
                function (SplFileInfo $fileInfo) {
145 8
                    return $fileInfo->getRealPath();
146 9
                },
147 9
                iterator_to_array($finder)
148
            )
149
        );
150
151 9
        $commonPath = get_common_path($files);
152
153 9
        return array_reduce(
154 9
            $files,
155 9
            function (array $files, string $file) use ($output, $commonPath): array {
156 8
                if (false === file_exists($file)) {
157
                    throw new RuntimeException(
158
                        sprintf(
159
                            'Could not find the file "%s".',
160
                            $file
161
                        )
162
                    );
163
                }
164
165 8
                if (false === is_readable($file)) {
166
                    throw new RuntimeException(
167
                        sprintf(
168
                            'Could not read the file "%s".',
169
                            $file
170
                        )
171
                    );
172
                }
173
174 8
                $files[$file] = $output.str_replace($commonPath, '', $file);
175
176 8
                return $files;
177 9
            },
178 9
            []
179
        );
180
    }
181
182
    /**
183
     * @param string[]      $files
184
     * @param string        $prefix
185
     * @param callable[]    $patchers
186
     * @param string[]      $whitelist
187
     * @param callable      $globalWhitelister
188
     * @param bool          $stopOnFailure
189
     * @param ConsoleLogger $logger
190
     */
191 9
    private function scopeFiles(
192
        array $files,
193
        string $prefix,
194
        array $patchers,
195
        array $whitelist,
196
        callable $globalWhitelister,
197
        bool $stopOnFailure,
198
        ConsoleLogger $logger
199
    ) {
200 9
        $count = count($files);
201 9
        $logger->outputFileCount($count);
202
203 9
        foreach ($files as $inputFilePath => $outputFilePath) {
204 8
            $this->scopeFile($inputFilePath, $outputFilePath, $prefix, $patchers, $whitelist, $globalWhitelister, $stopOnFailure, $logger);
205
        }
206
    }
207
208
    /**
209
     * @param string        $inputFilePath
210
     * @param string        $outputFilePath
211
     * @param string        $prefix
212
     * @param callable[]    $patchers
213
     * @param string[]      $whitelist
214
     * @param callable      $globalWhitelister
215
     * @param bool          $stopOnFailure
216
     * @param ConsoleLogger $logger
217
     */
218 8
    private function scopeFile(
219
        string $inputFilePath,
220
        string $outputFilePath,
221
        string $prefix,
222
        array $patchers,
223
        array $whitelist,
224
        callable $globalWhitelister,
225
        bool $stopOnFailure,
226
        ConsoleLogger $logger
227
    ) {
228
        try {
229 8
            $scoppedContent = $this->scoper->scope($inputFilePath, $prefix, $patchers, $whitelist, $globalWhitelister);
230 3
        } catch (Throwable $error) {
231 3
            $exception = new ParsingException(
232 3
                sprintf(
233 3
                    'Could not parse the file "%s".',
234 3
                    $inputFilePath
235
                ),
236 3
                0,
237 3
                $error
238
            );
239
240 3
            if ($stopOnFailure) {
241 2
                throw $exception;
242
            }
243
244 1
            $logger->outputWarnOfFailure($inputFilePath, $exception);
245
246 1
            $scoppedContent = file_get_contents($inputFilePath);
247
        }
248
249 6
        $this->fileSystem->dumpFile($outputFilePath, $scoppedContent);
250
251 6
        if (false === isset($exception)) {
252 5
            $logger->outputSuccess($inputFilePath);
253
        }
254
    }
255
}
256