GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#154)
by joseph
34:48
created

FileOverrider   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 276
Duplicated Lines 0 %

Test Coverage

Coverage 89.92%

Importance

Changes 0
Metric Value
eloc 106
dl 0
loc 276
ccs 107
cts 119
cp 0.8992
rs 9.36
c 0
b 0
f 0
wmc 38

19 Methods

Rating   Name   Duplication   Size   Complexity  
A setPathToProjectRoot() 0 6 1
A __construct() 0 7 2
A getFileNameNoExtensionForPath() 0 5 1
A getOverrideDirectoryForFile() 0 8 4
A getRealPath() 0 11 4
A setPathToOverridesDirectory() 0 5 1
A getRelativePathToFile() 0 3 1
A getOverrideForPath() 0 14 3
A createNewOverride() 0 14 2
A getPathToOverridesDirectory() 0 3 1
A getRelativePathFromOverridePath() 0 9 1
A projectFileIsSameAsOverride() 0 6 1
A overrideFileHashIsCorrect() 0 10 2
A updateOverrideFiles() 0 15 3
A getFileHash() 0 5 1
A getProjectFileHash() 0 3 1
A sortFiles() 0 5 1
A getOverridesIterator() 0 21 3
A applyOverrides() 0 23 5
1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\PostProcessor;
4
5
/**
6
 * This class provides the necessary functionality to allow you to maintain a set of file overrides and to safely apply
7
 * them as part of a post process to your main build process
8
 */
9
class FileOverrider
10
{
11
    /**
12
     * The default path to the overrides folder, relative to the project root
13
     */
14
    public const OVERRIDES_PATH = '/build/overrides';
15
16
    private const EXTENSION_LENGTH_NO_HASH   = 4;
17
    private const EXTENSION_LENGTH_WITH_HASH = 37;
18
19
    /**
20
     * @var string
21
     */
22
    private $pathToProjectRoot;
23
24
    /**
25
     * @var string
26
     */
27
    private $pathToOverridesDirectory;
28
29 15
    public function __construct(
30
        string $pathToProjectRoot = null,
31
        string $relativePathToOverridesDirectory = self::OVERRIDES_PATH
32
    ) {
33 15
        if (null !== $pathToProjectRoot) {
34 15
            $this->setPathToProjectRoot($pathToProjectRoot);
35 15
            $this->setPathToOverridesDirectory($this->pathToProjectRoot . '/' . $relativePathToOverridesDirectory);
36
        }
37 15
    }
38
39
    /**
40
     * @param string $pathToProjectRoot
41
     *
42
     * @return $this
43
     * @throws \RuntimeException
44
     */
45 15
    public function setPathToProjectRoot(string $pathToProjectRoot): self
46
    {
47 15
        $this->pathToProjectRoot = $this->getRealPath($pathToProjectRoot);
48 15
        $this->setPathToOverridesDirectory($this->pathToProjectRoot . self::OVERRIDES_PATH);
49
50 15
        return $this;
51
    }
52
53 15
    private function getRealPath(string $path): string
54
    {
55 15
        $realPath = \realpath($path);
56 15
        if (false === $realPath) {
57
            if (!mkdir($path, 0777, true) && !is_dir($path)) {
58
                throw new \RuntimeException(sprintf('Directory "%s" was not created', $path));
59
            }
60
            $realPath = realpath($path);
61
        }
62
63 15
        return $realPath;
64
    }
65
66
    /**
67
     * Create a new Override File by copying the file from the project into the project's overrides directory
68
     *
69
     * @param string $pathToFileInProject
70
     *
71
     * @return string
72
     */
73 6
    public function createNewOverride(string $pathToFileInProject): string
74
    {
75 6
        $relativePathToFileInProject = $this->getRelativePathToFile($pathToFileInProject);
76 6
        if (null !== $this->getOverrideForPath($relativePathToFileInProject)) {
77 3
            throw new \RuntimeException('Override already exists for path ' . $relativePathToFileInProject);
78
        }
79
        $overridePath =
80 3
            $this->getOverrideDirectoryForFile($relativePathToFileInProject) .
81 3
            '/' . $this->getFileNameNoExtensionForPath($relativePathToFileInProject) .
82 3
            '.' . $this->getProjectFileHash($relativePathToFileInProject) .
83 3
            '.php';
84 3
        copy($this->pathToProjectRoot . '/' . $relativePathToFileInProject, $overridePath);
85
86 3
        return $this->getRelativePathToFile($overridePath);
87
    }
88
89 15
    private function getRelativePathToFile(string $pathToFileInProject): string
90
    {
91 15
        return str_replace($this->pathToProjectRoot, '', $this->getRealPath($pathToFileInProject));
92
    }
93
94 6
    private function getOverrideForPath(string $relativePathToFileInProject): ?string
95
    {
96 6
        $fileDirectory       = $this->getOverrideDirectoryForFile($relativePathToFileInProject);
97 6
        $fileNameNoExtension = $this->getFileNameNoExtensionForPath($relativePathToFileInProject);
98 6
        $filesInDirectory    = glob("$fileDirectory/$fileNameNoExtension*");
99 6
        if ([] === $filesInDirectory) {
100 3
            return null;
101
        }
102 3
        if (1 === count($filesInDirectory)) {
103 3
            return $fileDirectory . '/' . current($filesInDirectory);
104
        }
105
        throw new \RuntimeException(
106
            'Found more than one override in path ' . $fileDirectory . ': '
107
            . print_r($filesInDirectory, true)
108
        );
109
    }
110
111 6
    private function getOverrideDirectoryForFile(string $relativePathToFileInProject): string
112
    {
113 6
        $path = $this->getPathToOverridesDirectory() . \dirname($relativePathToFileInProject);
114 6
        if (!is_dir($path) && !(mkdir($path, 0777, true) && is_dir($path))) {
115
            throw new \RuntimeException('Failed making override directory path ' . $path);
116
        }
117
118 6
        return $this->getRealPath($path);
119
    }
120
121
    /**
122
     * @return string
123
     */
124 15
    public function getPathToOverridesDirectory(): string
125
    {
126 15
        return $this->getRealPath($this->pathToOverridesDirectory);
127
    }
128
129
    /**
130
     * @param string $pathToOverridesDirectory
131
     *
132
     * @return FileOverrider
133
     */
134 15
    public function setPathToOverridesDirectory(string $pathToOverridesDirectory): FileOverrider
135
    {
136 15
        $this->pathToOverridesDirectory = $this->getRealPath($pathToOverridesDirectory);
137
138 15
        return $this;
139
    }
140
141 6
    private function getFileNameNoExtensionForPath(string $relativePathToFileInProject): string
142
    {
143 6
        $fileName = basename($relativePathToFileInProject);
144
145 6
        return substr($fileName, 0, -self::EXTENSION_LENGTH_NO_HASH);
146
    }
147
148 9
    private function getProjectFileHash(string $relativePathToFileInProject): string
149
    {
150 9
        return $this->getFileHash($this->pathToProjectRoot . '/' . $relativePathToFileInProject);
151
    }
152
153 12
    private function getFileHash(string $path): string
154
    {
155 12
        $contents = \ts\file_get_contents($path);
156
157 12
        return md5($contents);
158
    }
159
160
    /**
161
     * Loop over all the override files and update with the file contents from the project
162
     *
163
     * @return array[] the file paths that have been updated
164
     */
165 3
    public function updateOverrideFiles(): array
166
    {
167 3
        $filesUpdated = [];
168 3
        $fileSame     = [];
169 3
        foreach ($this->getOverridesIterator() as $pathToFileInOverrides) {
170 3
            $relativePathToFileInProject = $this->getRelativePathFromOverridePath($pathToFileInOverrides);
171 3
            if ($this->projectFileIsSameAsOverride($pathToFileInOverrides)) {
172
                $fileSame[] = $relativePathToFileInProject;
173
                continue;
174
            }
175 3
            copy($this->pathToProjectRoot . $relativePathToFileInProject, $pathToFileInOverrides);
176 3
            $filesUpdated[] = $relativePathToFileInProject;
177
        }
178
179 3
        return [$this->sortFiles($filesUpdated), $this->sortFiles($fileSame)];
180
    }
181
182
    /**
183
     * Yield file paths in the override folder
184
     *
185
     * @return \Generator|string[]
186
     */
187 9
    private function getOverridesIterator(): \Generator
188
    {
189
        try {
190 9
            $recursiveIterator = new \RecursiveIteratorIterator(
191 9
                new \RecursiveDirectoryIterator(
192 9
                    $this->getPathToOverridesDirectory(),
193 9
                    \RecursiveDirectoryIterator::SKIP_DOTS
194
                ),
195 9
                \RecursiveIteratorIterator::SELF_FIRST
196
            );
197 9
            foreach ($recursiveIterator as $fileInfo) {
198
                /**
199
                 * @var \SplFileInfo $fileInfo
200
                 */
201 9
                if ($fileInfo->isFile()) {
202 9
                    yield $fileInfo->getPathname();
203
                }
204
            }
205 9
        } finally {
206 9
            $recursiveIterator = null;
207 9
            unset($recursiveIterator);
208
        }
209 9
    }
210
211 9
    private function getRelativePathFromOverridePath(string $pathToFileInOverrides): string
212
    {
213 9
        $relativePath = substr($pathToFileInOverrides, strlen($this->getPathToOverridesDirectory()));
214 9
        $relativeDir  = dirname($relativePath);
215 9
        $filename     = basename($pathToFileInOverrides);
216 9
        $filename     = substr($filename, 0, -self::EXTENSION_LENGTH_WITH_HASH) . '.php';
217
218 9
        return $this->getRelativePathToFile(
219 9
            $this->getRealPath($this->pathToProjectRoot . '/' . $relativeDir . '/' . $filename)
220
        );
221
    }
222
223
    /**
224
     * Is the file in the project the same as the override file already?
225
     *
226
     * @param string $pathToFileInOverrides
227
     *
228
     * @return bool
229
     */
230 6
    private function projectFileIsSameAsOverride(string $pathToFileInOverrides): bool
231
    {
232 6
        $relativePathToFileInProject = $this->getRelativePathFromOverridePath($pathToFileInOverrides);
233
234 6
        return $this->getFileHash($this->pathToProjectRoot . '/' . $relativePathToFileInProject) ===
235 6
               $this->getFileHash($pathToFileInOverrides);
236
    }
237
238 6
    private function sortFiles(array $files): array
239
    {
240 6
        sort($files, SORT_STRING);
241
242 6
        return $files;
243
    }
244
245
    /**
246
     * Loop over all the override files and copy into the project
247
     *
248
     * @return array[] the file paths that have been updated
249
     */
250 6
    public function applyOverrides(): array
251
    {
252 6
        $filesUpdated = [];
253 6
        $filesSame    = [];
254 6
        $errors       = [];
255 6
        foreach ($this->getOverridesIterator() as $pathToFileInOverrides) {
256 6
            $relativePathToFileInProject = $this->getRelativePathFromOverridePath($pathToFileInOverrides);
257 6
            if ($this->overrideFileHashIsCorrect($pathToFileInOverrides)) {
258 3
                copy($pathToFileInOverrides, $this->pathToProjectRoot . $relativePathToFileInProject);
259 3
                $filesUpdated[] = $relativePathToFileInProject;
260 3
                continue;
261
            }
262 3
            if ($this->projectFileIsSameAsOverride($pathToFileInOverrides)) {
263
                $filesSame[] = $relativePathToFileInProject;
264
                continue;
265
            }
266 3
            $errors[$pathToFileInOverrides] = $this->getProjectFileHash($relativePathToFileInProject);
267
        }
268 6
        if ([] !== $errors) {
269 3
            throw new \RuntimeException('These file hashes were not up to date:' . print_r($errors, true));
270
        }
271
272 3
        return [$this->sortFiles($filesUpdated), $this->sortFiles($filesSame)];
273
    }
274
275 6
    private function overrideFileHashIsCorrect(string $pathToFileInOverrides): bool
276
    {
277 6
        $filenameParts = explode('.', basename($pathToFileInOverrides));
278 6
        if (3 !== count($filenameParts)) {
279
            throw new \RuntimeException('Invalid override filename ' . $pathToFileInOverrides);
280
        }
281 6
        $hash                        = $filenameParts[1];
282 6
        $relativePathToFileInProject = $this->getRelativePathFromOverridePath($pathToFileInOverrides);
283
284 6
        return $hash === $this->getProjectFileHash($relativePathToFileInProject);
285
    }
286
}
287