Completed
Push — master ( 6a9f32...4142c5 )
by Tim
10s
created

SubjectPlugin::processSubject()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 68
Code Lines 25

Duplication

Lines 3
Ratio 4.41 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 3
loc 68
ccs 0
cts 33
cp 0
rs 8.5748
c 0
b 0
f 0
cc 6
eloc 25
nc 7
nop 1
crap 42

How to fix   Long Method   

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
/**
4
 * TechDivision\Import\Plugins\SubjectPlugin
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\Plugins;
22
23
use TechDivision\Import\Utils\BunchKeys;
24
use TechDivision\Import\Utils\RegistryKeys;
25
use TechDivision\Import\Subjects\ExportableSubjectInterface;
26
use TechDivision\Import\Configuration\SubjectConfigurationInterface;
27
use TechDivision\Import\Utils\Generators\CoreConfigDataUidGenerator;
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 SubjectPlugin extends AbstractPlugin
39
{
40
41
    /**
42
     * The matches for the last processed CSV filename.
43
     *
44
     * @var array
45
     */
46
    protected $matches = array();
47
48
    /**
49
     * The number of imported bunches.
50
     *
51
     * @var integer
52
     */
53
    protected $bunches = 0;
54
55
    /**
56
     * Process the plugin functionality.
57
     *
58
     * @return void
59
     * @throws \Exception Is thrown, if the plugin can not be processed
60
     */
61
    public function process()
62
    {
63
        try {
64
            // immediately add the PID to lock this import process
65
            $this->lock();
66
67
            // load the plugin's subjects
68
            $subjects = $this->getPluginConfiguration()->getSubjects();
69
70
            // initialize the array for the status
71
            $status = array();
72
73
            // initialize the status information for the subjects
74
            /** @var \TechDivision\Import\Configuration\SubjectConfigurationInterface $subject */
75
            foreach ($subjects as $subject) {
76
                $status[$subject->getPrefix()] = array();
77
            }
78
79
            // and update it in the registry
80
            $this->getRegistryProcessor()->mergeAttributesRecursive($this->getSerial(), $status);
81
82
            // process all the subjects found in the system configuration
83
            /** @var \TechDivision\Import\Configuration\SubjectConfigurationInterface $subject */
84
            foreach ($subjects as $subject) {
85
                $this->processSubject($subject);
86
            }
87
88
            // update the number of imported bunches
89
            $this->getRegistryProcessor()->mergeAttributesRecursive(
90
                $this->getSerial(),
91
                array(RegistryKeys::BUNCHES => $this->bunches)
92
            );
93
94
            // stop the application if we don't process ANY bunch
95
            if ($this->bunches === 0) {
96
                $this->getApplication()->stop(
97
                    sprintf(
98
                        'Operation %s has been stopped by %s, because no import files has been found in directory %s',
99
                        $this->getConfiguration()->getOperationName(),
100
                        get_class($this),
101
                        $this->getConfiguration()->getSourceDir()
102
                    )
103
                );
104
            }
105
106
            // finally, if a PID has been set (because CSV files has been found),
107
            // remove it from the PID file to unlock the importer
108
            $this->unlock();
109
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
110
        } catch (\Exception $e) {
111
            // finally, if a PID has been set (because CSV files has been found),
112
            // remove it from the PID file to unlock the importer
113
            $this->unlock();
114
115
            // re-throw the exception
116
            throw $e;
117
        }
118
    }
119
120
    /**
121
     * Process the subject with the passed name/identifier.
122
     *
123
     * We create a new, fresh and separate subject for EVERY file here, because this would be
124
     * the starting point to parallelize the import process in a multithreaded/multiprocessed
125
     * environment.
126
     *
127
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subject The subject configuration
128
     *
129
     * @return void
130
     * @throws \Exception Is thrown, if the subject can't be processed
131
     */
132
    protected function processSubject(SubjectConfigurationInterface $subject)
133
    {
134
135
        // clear the filecache
136
        clearstatcache();
137
138
        // load the actual status
139
        $status = $this->getRegistryProcessor()->getAttribute($serial = $this->getSerial());
140
141
        // query whether or not the configured source directory is available
142 View Code Duplication
        if (!is_dir($sourceDir = $status[RegistryKeys::SOURCE_DIRECTORY])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
143
            throw new \Exception(sprintf('Source directory %s for subject %s is not available!', $sourceDir, $subject->getClassName()));
144
        }
145
146
        // initialize the array with the CSV files found in the source directory
147
        $files = glob(sprintf('%s/*.csv', $sourceDir));
148
149
        // sorting the files for the apropriate order
150
        usort($files, function ($a, $b) {
151
            return strcmp($a, $b);
152
        });
153
154
        // log a debug message
155
        $this->getSystemLogger()->debug(sprintf('Now checking directory %s for files to be imported', $sourceDir));
156
157
        // initialize the bunch number
158
        $bunches = 0;
159
160
        // iterate through all CSV files and process the subjects
161
        foreach ($files as $pathname) {
162
            // query whether or not that the file is part of the actual bunch
163
            if ($this->isPartOfBunch($subject->getPrefix(), $pathname)) {
164
                // initialize the subject and import the bunch
165
                $subjectInstance = $this->subjectFactory($subject);
166
167
                // query whether or not the subject needs an OK file,
168
                // if yes remove the filename from the file
169
                if ($subjectInstance->isOkFileNeeded()) {
170
                    $this->removeFromOkFile($pathname);
171
                }
172
173
                // finally import the CSV file
174
                $subjectInstance->import($serial, $pathname);
175
176
                // query whether or not, we've to export artefacts
177
                if ($subjectInstance instanceof ExportableSubjectInterface) {
178
                    $subjectInstance->export(
179
                        $this->matches[BunchKeys::FILENAME],
180
                        $this->matches[BunchKeys::COUNTER]
181
                    );
182
                }
183
184
                // raise the number of the imported bunches
185
                $bunches++;
186
            }
187
        }
188
189
        // raise the bunch number by the imported bunches
190
        $this->bunches = $this->bunches + $bunches;
191
192
        // reset the matches, because the exported artefacts
193
        $this->matches = array();
194
195
        // and and log a message that the subject has been processed
196
        $this->getSystemLogger()->debug(
197
            sprintf('Successfully processed subject %s with %d bunch(es)!', $subject->getClassName(), $bunches)
198
        );
199
    }
200
201
    /**
202
     * Factory method to create new handler instances.
203
     *
204
     * @param \TechDivision\Import\Configuration\SubjectConfigurationInterface $subjectConfiguration The subject configuration
205
     *
206
     * @return object The handler instance
207
     */
208
    protected function subjectFactory(SubjectConfigurationInterface $subjectConfiguration)
209
    {
210
211
        // load the subject class name
212
        $className = $subjectConfiguration->getClassName();
213
214
        // initialize the instances
215
        $processor = null;
216
        $systemLoggers = $this->getSystemLoggers();
217
        $registryProcessor = $this->getRegistryProcessor();
218
        $coreConfigDataUidGenerator = new CoreConfigDataUidGenerator();
219
220
        // instanciate and set the product processor, if specified
221
        if ($processorFactory = $subjectConfiguration->getProcessorFactory()) {
222
            $processor = $processorFactory::factory(
223
                $this->getImportProcessor()->getConnection(),
224
                $subjectConfiguration
225
            );
226
        }
227
228
        // initialize a new handler with the passed class name
229
        return new $className(
230
            $subjectConfiguration,
231
            $registryProcessor,
232
            $coreConfigDataUidGenerator,
233
            $systemLoggers,
234
            $processor
235
        );
236
    }
237
238
    /**
239
     * Queries whether or not, the passed filename is part of a bunch or not.
240
     *
241
     * @param string $prefix   The prefix to query for
242
     * @param string $filename The filename to query for
243
     *
244
     * @return boolean TRUE if the filename is part, else FALSE
245
     */
246 2
    protected function isPartOfBunch($prefix, $filename)
247
    {
248
249
        // initialize the pattern
250 2
        $pattern = '';
0 ignored issues
show
Unused Code introduced by
$pattern is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
251
252
        // query whether or not, this is the first file to be processed
253 2
        if (sizeof($this->matches) === 0) {
254
            // initialize the pattern to query whether the FIRST file has to be processed or not
255 2
            $pattern = sprintf(
256 2
                '/^.*\/(?<%s>%s)_(?<%s>.*)_(?<%s>\d+)\\.csv$/',
257 2
                BunchKeys::PREFIX,
258 2
                $prefix,
259 2
                BunchKeys::FILENAME,
260
                BunchKeys::COUNTER
261 2
            );
262
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
263 2
        } else {
264
            // initialize the pattern to query whether the NEXT file is part of a bunch or not
265 2
            $pattern = sprintf(
266 2
                '/^.*\/(?<%s>%s)_(?<%s>%s)_(?<%s>\d+)\\.csv$/',
267 2
                BunchKeys::PREFIX,
268 2
                $this->matches[BunchKeys::PREFIX],
269 2
                BunchKeys::FILENAME,
270 2
                $this->matches[BunchKeys::FILENAME],
271
                BunchKeys::COUNTER
272 2
            );
273
        }
274
275
        // initialize the array for the matches
276 2
        $matches = array();
277
278
        // update the matches, if the pattern matches
279 2
        if ($result = preg_match($pattern, $filename, $matches)) {
280 2
            $this->matches = $matches;
281 2
        }
282
283
        // stop processing, if the filename doesn't match
284 2
        return (boolean) $result;
285
    }
286
287
    /**
288
     * Return's an array with the names of the expected OK files for the actual subject.
289
     *
290
     * @return array The array with the expected OK filenames
291
     */
292
    protected function getOkFilenames()
293
    {
294
295
        // load the array with the available bunch keys
296
        $bunchKeys = BunchKeys::getAllKeys();
297
298
        // initialize the array for the available okFilenames
299
        $okFilenames = array();
300
301
        // prepare the OK filenames based on the found CSV file information
302
        for ($i = 1; $i <= sizeof($bunchKeys); $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...
303
            // intialize the array for the parts of the names (prefix, filename + counter)
304
            $parts = array();
305
            // load the parts from the matches
306
            for ($z = 0; $z < $i; $z++) {
307
                $parts[] = $this->matches[$bunchKeys[$z]];
308
            }
309
310
            // query whether or not, the OK file exists, if yes append it
311
            if (file_exists($okFilename = sprintf('%s/%s.ok', $this->getSourceDir(), implode('_', $parts)))) {
312
                $okFilenames[] = $okFilename;
313
            }
314
        }
315
316
        // prepare and return the pattern for the OK file
317
        return $okFilenames;
318
    }
319
320
    /**
321
     * Query whether or not, the passed CSV filename is in the OK file. If the filename was found,
322
     * it'll be returned and the method return TRUE.
323
     *
324
     * If the filename is NOT in the OK file, the method return's FALSE and the CSV should NOT be
325
     * imported/moved.
326
     *
327
     * @param string $filename The CSV filename to query for
328
     *
329
     * @return void
330
     * @throws \Exception Is thrown, if the passed filename is NOT in the OK file or it can NOT be removed from it
331
     */
332
    protected function removeFromOkFile($filename)
333
    {
334
335
        try {
336
            // load the expected OK filenames
337
            $okFilenames = $this->getOkFilenames();
338
339
            // iterate over the found OK filenames (should usually be only one, but could be more)
340
            foreach ($okFilenames as $okFilename) {
341
                // if the OK filename matches the CSV filename AND the OK file is empty
342
                if (basename($filename, '.csv') === basename($okFilename, '.ok') && filesize($okFilename) === 0) {
343
                    unlink($okFilename);
344
                    return;
345
                }
346
347
                // else, remove the CSV filename from the OK file
348
                $this->removeLineFromFile(basename($filename), $okFilename);
349
                return;
350
            }
351
352
            // throw an exception if either no OK file has been found,
353
            // or the CSV file is not in one of the OK files
354
            throw new \Exception(
355
                sprintf(
356
                    'Can\'t found filename %s in one of the expected OK files: %s',
357
                    $filename,
358
                    implode(', ', $okFilenames)
359
                )
360
            );
361
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
362
        } catch (\Exception $e) {
363
            // wrap and re-throw the exception
364
            throw new \Exception(
365
                sprintf(
366
                    'Can\'t remove filename %s from OK file: %s',
367
                    $filename,
368
                    $okFilename
369
                ),
370
                null,
371
                $e
372
            );
373
        }
374
    }
375
}
376