Issues (150)

src/Controllers/HistoryTrait.php (9 issues)

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
13
/**
14
 * Trait HistoryTrait
15
 *
16
 * @package SoliDry\Controllers
17
 */
18
trait HistoryTrait
19
{
20
    /**
21
     *  Collects all attrs, Types and diffs for further code-generation
22
     *
23
     * @throws \Symfony\Component\Yaml\Exception\ParseException
24
     */
25
    private function setMergedTypes(): void
26
    {
27
        $opMerge = $this->options[ConsoleInterface::OPTION_MERGE];
28
        $timeCheck = strtotime($opMerge); // only for validation - with respect to diff timezones
29
30
        if (false !== $timeCheck) {
31
            try {
32
                $this->mergeTime($opMerge);
33
            } catch (DirectoryException $e) {
34
                $this->error($e->getTraceAsString());
0 ignored issues
show
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

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

172
                    $content = $this->parser::parse(file_get_contents($this->/** @scrutinizer ignore-call */ formatGenPathByDir() . $file));
Loading history...
173
                    if (empty($content[ApiInterface::RAML_KEY_USES]) === false) {
174
                        foreach ($content[ApiInterface::RAML_KEY_USES] as $subFile) {
175
                            $lastFiles[] = substr($file, 0, $pos) . basename($subFile);
176
                        }
177
                    }
178
                    break;
179
                }
180
            }
181
182
            return $this->adjustFiles($lastFiles);
183
        }
184
185
        return [];
186
    }
187
188
    /**
189
     * Gets history files and merges them with current OAS files
190
     *
191
     * @param array $files      files from .gen/ dir saved history
192
     * @param array $inputFiles file that were passed as an option + files from uses RAML property
193
     * @throws \Symfony\Component\Yaml\Exception\ParseException
194
     */
195
    private function composeTypes(array $files, array $inputFiles): void
196
    {
197
        $attrsCurrent = [];
198
        $attrsHistory = [];
199
200
        $path = DirsInterface::GEN_DIR . DIRECTORY_SEPARATOR . $this->genDir . DIRECTORY_SEPARATOR;
201
        foreach ($files as $file) {
202
            foreach ($inputFiles as $inFile) {
203
204
                if (mb_strpos($file, basename($inFile), null, PhpInterface::ENCODING_UTF8) !== false) {
0 ignored issues
show
null of type null is incompatible with the type integer expected by parameter $offset of mb_strpos(). ( Ignorable by Annotation )

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

204
                if (mb_strpos($file, basename($inFile), /** @scrutinizer ignore-type */ null, PhpInterface::ENCODING_UTF8) !== false) {
Loading history...
205
                    $dataCurrent = $this->parser::parse(file_get_contents($inFile));
206
                    $dataHistory = $this->parser::parse(file_get_contents($path . $file));
207
208
                    $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...
209
                    $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...
210
                    $this->types += array_merge_recursive($this->historyTypes, $this->currentTypes);
211
212
                    $attrsCurrent += array_filter($this->currentTypes, function ($k) {
213
                        return strpos($k, CustomsInterface::CUSTOM_TYPES_ATTRIBUTES) !== false;
214
                    }, ARRAY_FILTER_USE_KEY);
215
                    $attrsHistory += array_filter($this->historyTypes, function ($k) {
216
                        return strpos($k, CustomsInterface::CUSTOM_TYPES_ATTRIBUTES) !== false;
217
                    }, ARRAY_FILTER_USE_KEY);
218
                }
219
            }
220
        }
221
222
        $this->composeDiffs($attrsCurrent, $attrsHistory);
223
    }
224
225
    /**
226
     * Compares attributes for current and previous history and sets the diffTypes prop
227
     * to process additional migrations creation
228
     *
229
     * @param array $attrsCurrent Current attributes
230
     * @param array $attrsHistory History attributes
231
     */
232
    private function composeDiffs(array $attrsCurrent, array $attrsHistory): void
233
    {
234
        // make diffs on current array to add columns/indices to migrations
235
        foreach ($attrsCurrent as $k => $v) {
236
            if (empty($attrsHistory[$k][ApiInterface::RAML_PROPS]) === false
237
                && (empty($v[ApiInterface::RAML_PROPS]) === false)) {
238
239
                foreach ($v[ApiInterface::RAML_PROPS] as $attr => $attrValue) {
240
                    if (empty($attrsHistory[$k][ApiInterface::RAML_PROPS][$attr])) { // if there is no such element in history data - collect
241
                        $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...
242
                    }
243
                }
244
            }
245
        }
246
247
        // reflect array from history to append lost props
248
        foreach ($attrsHistory as $k => $v) {
249
            if (empty($attrsCurrent[$k][ApiInterface::RAML_PROPS]) === false
250
                && (empty($v[ApiInterface::RAML_PROPS]) === false)) {
251
252
                foreach ($v[ApiInterface::RAML_PROPS] as $attr => $attrValue) {
253
                    if (empty($attrsCurrent[$k][ApiInterface::RAML_PROPS][$attr])) { // if there is no such element in current data - collect
254
                        $this->diffTypes[$k][$attr] = $attrValue;
255
                    }
256
                }
257
            }
258
        }
259
    }
260
261
    /**
262
     * Gets an unordered array of files and returns an ordered one
263
     * stating from *openapi.yaml
264
     *
265
     * @param array $files
266
     * @return array
267
     */
268
    private function adjustFiles(array $files): array
269
    {
270
        $tmpFile = '';
271
        foreach ($files as $k => $file) {
272
            if (strpos($file, ApiInterface::OPEN_API_KEY) !== false) {
273
                $tmpFile = $file;
274
                unset($files[$k]);
275
                break;
276
            }
277
        }
278
        array_unshift($files, $tmpFile);
279
280
        return $files;
281
    }
282
}