Completed
Push — 16.x ( 0056bb...bd3602 )
by Tim
02:11 queued 10s
created

OkFileAwareFileResolver::loadFiles()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 28
c 0
b 0
f 0
ccs 0
cts 6
cp 0
rs 9.1608
cc 5
nc 3
nop 1
crap 30
1
<?php
2
3
/**
4
 * TechDivision\Import\Subjects\FileResolver\OkFileAwareFileResolver
5
 *
6
 * NOTICE OF LICENSE
7
 *
8
 * This source file is subject to the Open Software License (OSL 3.0)
9
 * that is available through the world-wide-web at this URL:
10
 * http://opensource.org/licenses/osl-3.0.php
11
 *
12
 * PHP version 5
13
 *
14
 * @author    Tim Wagner <[email protected]>
15
 * @copyright 2016 TechDivision GmbH <[email protected]>
16
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
17
 * @link      https://github.com/techdivision/import
18
 * @link      http://www.techdivision.com
19
 */
20
21
namespace TechDivision\Import\Subjects\FileResolver;
22
23
use TechDivision\Import\Exceptions\LineNotFoundException;
24
use TechDivision\Import\Exceptions\MissingOkFileException;
25
use TechDivision\Import\ApplicationInterface;
26
use TechDivision\Import\Services\RegistryProcessorInterface;
27
use TechDivision\Import\Loaders\FilteredLoaderInterface;
28
29
/**
30
 * Plugin that processes the subjects.
31
 *
32
 * @author    Tim Wagner <[email protected]>
33
 * @copyright 2016 TechDivision GmbH <[email protected]>
34
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
35
 * @link      https://github.com/techdivision/import
36
 * @link      http://www.techdivision.com
37
 */
38
class OkFileAwareFileResolver extends AbstractFileResolver implements OkFileAwareFileResolverInterface
39
{
40
41
    /**
42
     * The appliation instance.
43
     *
44
     * @var \TechDivision\Import\ApplicationInterface
45
     */
46
    private $application;
47
48
    /**
49
     * Initializes the file resolver with the application and the registry instance.
50
     *
51
     * @param \TechDivision\Import\ApplicationInterface                $application       The application instance
52
     * @param \TechDivision\Import\Services\RegistryProcessorInterface $registryProcessor The registry instance
53
     * @param \TechDivision\Import\Loaders\FilteredLoaderInterface     $filesystemLoader  The filesystem loader instance
54
     */
55
    public function __construct(
56
        ApplicationInterface $application,
57
        RegistryProcessorInterface $registryProcessor,
58
        FilteredLoaderInterface $filesystemLoader
59
    ) {
60 1
61
        // set the application instance
62 1
        $this->application = $application;
63
64
        // pass the processor and filesystem loader instance to the parent constructor
65
        parent::__construct($registryProcessor, $filesystemLoader);
66
    }
67
68
    /**
69
     * Return's the application instance.
70
     *
71
     * @return \TechDivision\Import\ApplicationInterface The application instance
72
     */
73 1
    protected function getApplication() : ApplicationInterface
74
    {
75 1
        return $this->application;
76
    }
77
78
    /**
79
     * Remove's the passed line from the file with the passed name.
80
     *
81
     * @param string $line     The line to be removed
82
     * @param string $filename The name of the file the line has to be removed
83
     *
84
     * @return void
85 1
     * @throws \Exception Is thrown, if the file doesn't exists, the line is not found or can not be removed
86
     */
87 1
    protected function removeLineFromFile(string $line, string $filename) : void
88
    {
89
        $this->getApplication()->removeLineFromFile($line, $filename);
90
    }
91
92
    /**
93
     * Query whether or not the basename, without suffix, of the passed filenames are equal.
94
     *
95 1
     * @param string $filename1 The first filename to compare
96
     * @param string $filename2 The second filename to compare
97
     *
98
     * @return boolean TRUE if the passed files basename are equal, else FALSE
99 1
     * @todo Refactorig required, because of duplicate method
100
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::isEqualFilename()
101
     */
102 1
    protected function isEqualFilename(string $filename1, string $filename2) : bool
103
    {
104 1
        return $this->stripSuffix($filename1, $this->getSuffix()) === $this->stripSuffix($filename2, $this->getOkFileSuffix());
0 ignored issues
show
Bug introduced by
The method getOkFileSuffix() does not seem to exist on object<TechDivision\Impo...kFileAwareFileResolver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
105
    }
106 1
107
    /**
108 1
     * Strips the passed suffix, including the (.), from the filename and returns it.
109
     *
110
     * @param string $filename The filename to return the suffix from
111
     * @param string $suffix   The suffix to return
112 1
     *
113 1
     * @return string The filname without the suffix
114
     * @todo Refactorig required, because of duplicate method
115
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::stripSuffix()
116
     */
117
    protected function stripSuffix(string $filename, string $suffix) : string
118 1
    {
119
        return basename($filename, sprintf('.%s', $suffix));
120
    }
121
122
    /**
123
     * Prepares and returns an OK filename from the passed parts.
124
     *
125
     * @param array $parts The parts to concatenate the OK filename from
126
     *
127
     * @return string The OK filename
128 1
     * @todo Refactorig required, because of duplicate method
129
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::prepareOkFilename()
130
     */
131
    protected function prepareOkFilename(array $parts) : string
132 1
    {
133
        return sprintf('%s/%s.%s', $this->getSourceDir(), implode($this->getElementSeparator(), $parts), $this->getOkFileSuffix());
0 ignored issues
show
Bug introduced by
The method getElementSeparator() does not seem to exist on object<TechDivision\Impo...kFileAwareFileResolver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getOkFileSuffix() does not seem to exist on object<TechDivision\Impo...kFileAwareFileResolver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
134
    }
135 1
136
    /**
137
     * Returns the elements the filenames consists of.
138 1
     *
139
     * @return array The array with the filename elements
140 1
     * @todo Refactorig required, because of duplicate method
141
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::getPatternElements()
142
     */
143
    protected function getPatternElements()
144
    {
145
        return $this->getFileResolverConfiguration()->getPatternElements();
146 1
    }
147
148
    /**
149 1
     * Returns the elements the filenames consists of, converted to lowercase.
150
     *
151 1
     * @return array The array with the filename elements
152
     * @todo Refactorig required, because of duplicate method
153
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::getPatternKeys()
154
     */
155
    protected function getPatternKeys()
156
    {
157 1
158
        // load the pattern keys from the configuration
159
        $patternKeys = $this->getPatternElements();
160 1
161 1
        // make sure that they are all lowercase
162 1
        array_walk($patternKeys, function (&$value) {
163
            $value = strtolower($value);
164
        });
165 1
166 1
        // return the pattern keys
167 1
        return $patternKeys;
168
    }
169
170
    /**
171
     * Return's an array with the names of the expected OK files for the actual subject.
172 1
     *
173 1
     * @return array The array with the expected OK filenames
174
     * @todo Refactorig required, because of duplicate method
175
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::getOkFilenames()
176
     */
177 View Code Duplication
    protected function getOkFilenames() : array
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178 1
    {
179
180
        // initialize the array for the available okFilenames
181
        $okFilenames = array();
182
183
        // prepare the OK filenames based on the found CSV file information
184
        for ($i = 1; $i <= sizeof($patternKeys = $this->getPatternKeys()); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
185
            // intialize the array for the parts of the names (prefix, filename + counter)
186
            $parts = array();
187
            // load the parts from the matches
188
            for ($z = 0; $z < $i; $z++) {
189
                // append the part
190
                $parts[] = $this->getMatch($patternKeys[$z]);
0 ignored issues
show
Bug introduced by
The method getMatch() does not seem to exist on object<TechDivision\Impo...kFileAwareFileResolver>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
191
            }
192
193
            // query whether or not, the OK file exists, if yes append it
194
            if (file_exists($okFilename = $this->prepareOkFilename($parts))) {
195
                $okFilenames[] = $okFilename;
196
            }
197
        }
198
199
        // prepare and return the pattern for the OK file
200
        return $okFilenames;
201
    }
202
203
    /**
204
     * Query whether or not, the passed CSV filename is in the OK file. If the filename was found,
205
     * the OK file will be cleaned-up.
206
     *
207
     * @param string $filename The filename to be cleaned-up
208
     *
209
     * @return void
210
     * @throws \Exception Is thrown, if the passed filename is NOT in the OK file or the OK can not be cleaned-up
211
     */
212 View Code Duplication
    public function cleanUpOkFile(string $filename) : void
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
213
    {
214
215
        // query whether or not the subject needs an OK file, if yes remove the filename from the file
216
        if ($this->getSubjectConfiguration()->isOkFileNeeded() === false) {
217
            return;
218
        }
219
220
        try {
221
            // try to load the expected OK filenames
222
            if (sizeof($okFilenames = $this->getOkFilenames()) === 0) {
223
                throw new MissingOkFileException(sprintf('Can\'t find a OK filename for file %s', $filename));
224
            }
225
226
            // iterate over the found OK filenames (should usually be only one, but could be more)
227
            foreach ($okFilenames as $okFilename) {
228
                // clear the filecache
229
                \clearstatcache();
230
                // if the OK filename matches the CSV filename AND the OK file is empty
231
                if ($this->isEqualFilename($filename, $okFilename) && filesize($okFilename) === 0) {
232
                    unlink($okFilename);
233
                    return;
234
                }
235
236
                // else, remove the CSV filename from the OK file
237
                $this->removeLineFromFile(basename($filename), $fh = fopen($okFilename, 'r+'));
0 ignored issues
show
Documentation introduced by
$fh = fopen($okFilename, 'r+') is of type resource, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
238
                fclose($fh);
239
240
                // if the OK file is empty, delete the file
241
                if (filesize($okFilename) === 0) {
242
                    unlink($okFilename);
243
                }
244
245
                // return immediately
246
                return;
247
            }
248
249
            // throw an exception if either no OK file has been found,
250
            // or the CSV file is not in one of the OK files
251
            throw new \Exception(
252
                sprintf(
253
                    'Can\'t found filename %s in one of the expected OK files: %s',
254
                    $filename,
255
                    implode(', ', $okFilenames)
256
                )
257
            );
258
        } catch (LineNotFoundException $lne) {
259
            // wrap and re-throw the exception
260
            throw new \Exception(
261
                sprintf(
262
                    'Can\'t remove filename %s from OK file: %s',
263
                    $filename,
264
                    $okFilename
265
                ),
266
                null,
267
                $lne
268
            );
269
        }
270
    }
271
272
    /**
273
     * Loads the files from the source directory and return's them sorted.
274
     *
275
     * @param string $serial The unique identifier of the actual import process
276
     *
277
     * @return array The array with the files matching the subjects suffix
278
     * @throws \Exception Is thrown, when the source directory is NOT available
279
     * @throws \TechDivision\Import\Exceptions\MissingOkFileException Is thrown, if files to be processed are available but the mandatory OK file is missing
280
     */
281
    public function loadFiles(string $serial) : array
282
    {
283
284
        // initialize the array with the files that has to be handled
285
        $filesToHandle = parent::loadFiles($serial);
286
287
        // load the size of the files before the filters have been applied
288
        $sizeBeforeFiltersHaveBeenApplied = $this->getFilesystemLoader()->getSizeBeforeFiltersHaveBeenApplied();
289
290
        // stop processing, if files ARE available, an OK file IS mandatory, but
291
        // NO file will be processed (because of a missing/not matching OK file)
292
        if ($this->getSubjectConfiguration()->isOkFileNeeded() && $sizeBeforeFiltersHaveBeenApplied > 0 && sizeof($filesToHandle) === 0) {
293
            throw new MissingOkFileException(
294
                sprintf(
295
                    'Stop processing, because can\'t find the mandatory OK file to process at least one of %d files',
296
                    $sizeBeforeFiltersHaveBeenApplied
297
                )
298
            );
299
        }
300
301
        // clean-up the .OK file for the found files
302
        foreach ($filesToHandle as $fileToHandle) {
303
            $this->cleanUpOkFile($fileToHandle);
304
        }
305
306
        // return the array with the files that has to be handled
307
        return $filesToHandle;
308
    }
309
}
310