Passed
Push — master ( 574b00...b5f26b )
by Arthur
03:04
created

HistoryTrait::composeDiffs()   B

Complexity

Conditions 11
Paths 25

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 12
dl 0
loc 23
rs 7.3166
c 0
b 0
f 0
cc 11
nc 25
nop 2

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
<?php
2
3
namespace SoliDry\Controllers;
4
5
use SoliDry\Exceptions\DirectoryException;
6
use SoliDry\Types\ConsoleInterface;
7
use SoliDry\Types\CustomsInterface;
8
use SoliDry\Types\DirsInterface;
9
use SoliDry\Types\ErrorsInterface;
10
use SoliDry\Types\ApiInterface;
11
use SoliDry\Types\PhpInterface;
12
use Symfony\Component\Yaml\Yaml;
13
14
/**
15
 * Trait HistoryTrait
16
 *
17
 * @package SoliDry\Controllers
18
 */
19
trait HistoryTrait
20
{
21
    /**
22
     *  Collects all attrs, Types and diffs for further code-generation
23
     *
24
     * @throws \Symfony\Component\Yaml\Exception\ParseException
25
     */
26
    private function setMergedTypes(): void
27
    {
28
        $opMerge = $this->options[ConsoleInterface::OPTION_MERGE];
29
        $timeCheck = strtotime($opMerge); // only for validation - with respect to diff timezones
30
31
        if (false !== $timeCheck) {
32
            try {
33
                $this->mergeTime($opMerge);
34
            } catch (DirectoryException $e) {
35
                $this->error($e->getTraceAsString());
0 ignored issues
show
Bug introduced by
It seems like error() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

35
                $this->/** @scrutinizer ignore-call */ 
36
                       error($e->getTraceAsString());
Loading history...
36
            }
37
        } else if (is_numeric($opMerge) !== false) {
38
            $this->mergeStep($opMerge);
39
        } else if ($opMerge === ConsoleInterface::MERGE_DEFAULT_VALUE) {
40
            $this->mergeLast();
41
        }
42
    }
43
44
    /**
45
     * Merges history OAS files with current by time in the past
46
     *
47
     * @param string $opMerge
48
     * @throws \Symfony\Component\Yaml\Exception\ParseException
49
     * @throws DirectoryException
50
     */
51
    private function mergeTime(string $opMerge): void
52
    {
53
        $dateTime = explode(PhpInterface::SPACE, $opMerge);
54
        $this->composeTypes($this->composeTimeFiles($dateTime), $this->files);
55
    }
56
57
    /**
58
     * @param array $dateTime
59
     * @return array
60
     * @throws DirectoryException
61
     */
62
    private function composeTimeFiles(array $dateTime): array
63
    {
64
        $time = str_replace(':', '', $dateTime[1]);
65
66
        $this->genDir = $dateTime[0];
0 ignored issues
show
Bug Best Practice introduced by
The property genDir does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
67
        $path = DirsInterface::GEN_DIR . DIRECTORY_SEPARATOR . $this->genDir . DIRECTORY_SEPARATOR;
68
69
        if (is_dir($path) === false) {
70
            throw new DirectoryException('The directory: ' . $path . ' was not found.',
71
                ErrorsInterface::CODE_DIR_NOT_FOUND);
72
        }
73
74
        $files = glob($path . $time . '*');
75
        foreach ($files as &$fullPath) {
76
            $fullPath = str_replace($path, '', $fullPath);
77
        }
78
79
        $files = array_diff($files, DirsInterface::EXCLUDED_DIRS);
80
81
        return $this->adjustFiles($files);
82
    }
83
84
    /**
85
     * Merges history OAS files with current by backward steps
86
     *
87
     * @param int $step
88
     * @throws \Symfony\Component\Yaml\Exception\ParseException
89
     */
90
    private function mergeStep(int $step): void
91
    {
92
        $dirs = scandir(DirsInterface::GEN_DIR . DIRECTORY_SEPARATOR, SCANDIR_SORT_DESCENDING);
93
        if ($dirs !== false) {
94
            $dirs = array_diff($dirs, DirsInterface::EXCLUDED_DIRS);
95
            $this->composeTypes($this->composeStepFiles($dirs, $step), $this->files);
96
        }
97
    }
98
99
    /**
100
     * Composes files for step back in history via .gen dir
101
     *
102
     * @param array $dirs
103
     * @param int $step
104
     * @return array
105
     */
106
    private function composeStepFiles(array $dirs, int $step): array
107
    {
108
        $filesToPass = [];
109
        foreach ($dirs as $dir) {
110
            $files = scandir(DirsInterface::GEN_DIR . DIRECTORY_SEPARATOR . $dir, SCANDIR_SORT_DESCENDING);
111
            $files = array_diff($files, DirsInterface::EXCLUDED_DIRS);
112
113
            $prefixFlag = '';
114
            foreach ($files as $kFile => $file) {
115
                $prefix = substr($file, 0, 6); // Hms
116
                $template = '/^' . $prefix . '.*$/i';
117
118
                if ($prefix !== $prefixFlag) {
119
                    --$step;
120
                    $prefixFlag = $prefix;
121
                    if ($step > 0) {
122
                        $skip = preg_grep($template, $files);
123
                        $files = array_diff($files, $skip);
124
                    }
125
                }
126
127
                if ($step <= 0) {
128
                    $files = preg_grep($template, $files);
129
                    $this->genDir = $dir;
0 ignored issues
show
Bug Best Practice introduced by
The property genDir does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
130
                    $filesToPass = $files;
131
                    break 2;
132
                }
133
            }
134
        }
135
136
        return $this->adjustFiles($filesToPass);
137
    }
138
139
    /**
140
     *  Merges current state with the last one
141
     *
142
     * @throws \Symfony\Component\Yaml\Exception\ParseException
143
     */
144
    private function mergeLast(): void
145
    {
146
        $lastFiles = $this->getLastFiles();
147
        if (empty($lastFiles) === false) {
148
            $this->composeTypes($lastFiles, $this->files);
149
        }
150
    }
151
152
    /**
153
     * Gets last files according to main file named "openapi" by spec of OAS
154
     * and it's included files defined in "uses" property
155
     *
156
     * @return array
157
     * @throws \Symfony\Component\Yaml\Exception\ParseException
158
     */
159
    private function getLastFiles(): array
160
    {
161
        $dirs = scandir(DirsInterface::GEN_DIR . DIRECTORY_SEPARATOR, SCANDIR_SORT_DESCENDING);
162
        if ($dirs !== false) {
163
            $dirs = array_diff($dirs, DirsInterface::EXCLUDED_DIRS);
164
            $this->genDir = $dirs[0]; // desc last date YYYY-mm-dd
0 ignored issues
show
Bug Best Practice introduced by
The property genDir does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
165
166
            $files = scandir(DirsInterface::GEN_DIR . DIRECTORY_SEPARATOR . $this->genDir, SCANDIR_SORT_DESCENDING);
167
            $files = array_diff($files, DirsInterface::EXCLUDED_DIRS);
168
169
            $lastFiles = [];
170
            foreach ($files as $file) {
171
                if (($pos = strpos($file, ApiInterface::OPEN_API_KEY)) !== false) {
172
                    $lastFiles[] = $file;
173
                    $content = Yaml::parse(file_get_contents($this->formatGenPathByDir() . $file));
0 ignored issues
show
Bug introduced by
It seems like formatGenPathByDir() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

173
                    $content = Yaml::parse(file_get_contents($this->/** @scrutinizer ignore-call */ formatGenPathByDir() . $file));
Loading history...
174
                    if (empty($content[ApiInterface::RAML_KEY_USES]) === false) {
175
                        foreach ($content[ApiInterface::RAML_KEY_USES] as $subFile) {
176
                            $lastFiles[] = substr($file, 0, $pos) . basename($subFile);
177
                        }
178
                    }
179
                    break;
180
                }
181
            }
182
183
            return $this->adjustFiles($lastFiles);
184
        }
185
186
        return [];
187
    }
188
189
    /**
190
     * Gets history files and merges them with current OAS files
191
     *
192
     * @param array $files      files from .gen/ dir saved history
193
     * @param array $inputFiles file that were passed as an option + files from uses RAML property
194
     * @throws \Symfony\Component\Yaml\Exception\ParseException
195
     */
196
    private function composeTypes(array $files, array $inputFiles): void
197
    {
198
        $attrsCurrent = [];
199
        $attrsHistory = [];
200
201
        $path = DirsInterface::GEN_DIR . DIRECTORY_SEPARATOR . $this->genDir . DIRECTORY_SEPARATOR;
202
        foreach ($files as $file) {
203
            foreach ($inputFiles as $inFile) {
204
205
                if (mb_strpos($file, basename($inFile), null, PhpInterface::ENCODING_UTF8) !== false) {
206
                    $dataCurrent = Yaml::parse(file_get_contents($inFile));
207
                    $dataHistory = Yaml::parse(file_get_contents($path . $file));
208
209
                    $this->currentTypes = $dataCurrent[ApiInterface::API_COMPONENTS][ApiInterface::API_SCHEMAS];
0 ignored issues
show
Bug Best Practice introduced by
The property currentTypes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
210
                    $this->historyTypes = $dataHistory[ApiInterface::API_COMPONENTS][ApiInterface::API_SCHEMAS];
0 ignored issues
show
Bug Best Practice introduced by
The property historyTypes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
211
                    $this->types += array_merge_recursive($this->historyTypes, $this->currentTypes);
212
213
                    $attrsCurrent += array_filter($this->currentTypes, function ($k) {
214
                        return strpos($k, CustomsInterface::CUSTOM_TYPES_ATTRIBUTES) !== false;
215
                    }, ARRAY_FILTER_USE_KEY);
216
                    $attrsHistory += array_filter($this->historyTypes, function ($k) {
217
                        return strpos($k, CustomsInterface::CUSTOM_TYPES_ATTRIBUTES) !== false;
218
                    }, ARRAY_FILTER_USE_KEY);
219
                }
220
            }
221
        }
222
223
        $this->composeDiffs($attrsCurrent, $attrsHistory);
224
    }
225
226
    /**
227
     * Compares attributes for current and previous history and sets the diffTypes prop
228
     * to process additional migrations creation
229
     *
230
     * @param array $attrsCurrent Current attributes
231
     * @param array $attrsHistory History attributes
232
     */
233
    private function composeDiffs(array $attrsCurrent, array $attrsHistory): void
234
    {
235
        // make diffs on current array to add columns/indices to migrations
236
        foreach ($attrsCurrent as $k => $v) {
237
            if (empty($attrsHistory[$k][ApiInterface::RAML_PROPS]) === false
238
                && (empty($v[ApiInterface::RAML_PROPS]) === false)) {
239
240
                foreach ($v[ApiInterface::RAML_PROPS] as $attr => $attrValue) {
241
                    if (empty($attrsHistory[$k][ApiInterface::RAML_PROPS][$attr])) { // if there is no such element in history data - collect
242
                        $this->diffTypes[$k][$attr] = $attrValue;
0 ignored issues
show
Bug Best Practice introduced by
The property diffTypes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
243
                    }
244
                }
245
            }
246
        }
247
248
        // reflect array from history to append lost props
249
        foreach ($attrsHistory as $k => $v) {
250
            if (empty($attrsCurrent[$k][ApiInterface::RAML_PROPS]) === false
251
                && (empty($v[ApiInterface::RAML_PROPS]) === false)) {
252
253
                foreach ($v[ApiInterface::RAML_PROPS] as $attr => $attrValue) {
254
                    if (empty($attrsCurrent[$k][ApiInterface::RAML_PROPS][$attr])) { // if there is no such element in current data - collect
255
                        $this->diffTypes[$k][$attr] = $attrValue;
256
                    }
257
                }
258
            }
259
        }
260
    }
261
262
    /**
263
     * Gets an unordered array of files and returns an ordered one
264
     * stating from *openapi.yaml
265
     *
266
     * @param array $files
267
     * @return array
268
     */
269
    private function adjustFiles(array $files): array
270
    {
271
        $tmpFile = '';
272
        foreach ($files as $k => $file) {
273
            if (strpos($file, ApiInterface::OPEN_API_KEY) !== false) {
274
                $tmpFile = $file;
275
                unset($files[$k]);
276
                break;
277
            }
278
        }
279
        array_unshift($files, $tmpFile);
280
281
        return $files;
282
    }
283
}