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

ProposedOkFilenameLoader::propsedOkFilenames()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 26

Duplication

Lines 11
Ratio 42.31 %

Importance

Changes 0
Metric Value
dl 11
loc 26
c 0
b 0
f 0
rs 9.504
cc 3
nc 3
nop 0
1
<?php
2
3
/**
4
 * TechDivision\Import\Loaders\ProposedOkFilenameLoader
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 2020 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\Loaders;
22
23
use TechDivision\Import\Utils\BunchKeys;
24
use TechDivision\Import\Utils\RegistryKeys;
25
use TechDivision\Import\Loaders\Sorters\UasortImpl;
26
use TechDivision\Import\Loaders\Filters\PregMatchFilter;
27
use TechDivision\Import\Adapter\FilesystemAdapterInterface;
28
use TechDivision\Import\Services\RegistryProcessorInterface;
29
use TechDivision\Import\Loaders\Filters\FilterImplInterface;
30
use TechDivision\Import\Loaders\Sorters\SorterImplInterface;
31
use TechDivision\Import\Configuration\Subject\FileResolverConfigurationInterface;
32
33
/**
34
 * Loader implementation that loads a list of proposed .OK filenames and their
35
 * matching CSV files, based on the found CSV files that'll be loaded by the
36
 * passed loader instance.
37
 *
38
 * @author    Tim Wagner <[email protected]>
39
 * @copyright 2020 TechDivision GmbH <[email protected]>
40
 * @license   http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
41
 * @link      https://github.com/techdivision/import
42
 * @link      http://www.techdivision.com
43
 * @link      https://www.php.net/array_filter
44
 */
45
class ProposedOkFilenameLoader extends FilteredLoader implements FilteredLoaderInterface, SortedLoaderInterface
46
{
47
48
    /**
49
     * The regular expression used to load the files with.
50
     *
51
     * @var string
52
     */
53
    private $regex = '/^.*\/%s\\.%s$/';
54
55
    /**
56
     * The actual source directory to use.
57
     *
58
     * @var string
59
     */
60
    private $sourceDir;
61
62
    /**
63
     * The file resolver configuration instance.
64
     *
65
     * @var \TechDivision\Import\Configuration\Subject\FileResolverConfigurationInterface
66
     */
67
    private $fileResolverConfiguration;
68
69
    /**
70
     * The sorter instance to use.
71
     *
72
     * @var \TechDivision\Import\Loaders\Sorters\SorterImplInterface
73
     */
74
    private $sorterImpl;
75
76
    /**
77
     * Initializes the file handler instance.
78
     *
79
     * @param \TechDivision\Import\Services\RegistryProcessorInterface  $registryProcessor  The registry processor instance
0 ignored issues
show
Bug introduced by
There is no parameter named $registryProcessor. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
80
     * @param \TechDivision\Import\Loaders\FilteredLoaderInterface      $loader             The parent loader instance
81
     * @param \TechDivision\Import\Loaders\Filters\FilterImplInterface  $filterImpl         The filter instance to use
82
     * @param \TechDivision\Import\Loaders\Sorters\SorterImplInterface  $sorterImpl         The sorter instance to use
83
     */
84
    public function __construct(
85
        FilteredLoaderInterface $loader,
86
        FilterImplInterface $filterImpl = null,
87
        SorterImplInterface $sorterImpl = null
88
    ) {
89
90
        // initialize the sorter instance
91
        $this->sorterImpl = $sorterImpl ?? new UasortImpl();
92
93
        // pass parent loader and filter instance to the parent constructor
94
        parent::__construct($loader, $filterImpl);
95
    }
96
97
    /**
98
     * Returns the file resolver configuration instance.
99
     *
100
     * @return \TechDivision\Import\Configuration\Subject\FileResolverConfigurationInterface The configuration instance
101
     */
102
    protected function getFileResolverConfiguration() : FileResolverConfigurationInterface
103
    {
104
        return $this->fileResolverConfiguration;
105
    }
106
107
    /**
108
     * Return's the sorter instance to use.
109
     *
110
     * @return \TechDivision\Import\Loaders\Sorters\SorterImplInterface The sorter instance to use
111
     */
112
    protected function getSorterImpl() : SorterImplInterface
113
    {
114
        return $this->sorterImpl;
115
    }
116
117
    /**
118
     * Returns the regular expression used to load the files with.
119
     *
120
     * @return string The regular expression
121
     */
122
    protected function getRegex() : string
123
    {
124
        return $this->regex;
125
    }
126
127
    /**
128
     * Remove's the passed line from the file with the passed name.
129
     *
130
     * @param string $line     The line to be removed
131
     * @param string $filename The name of the file the line has to be removed
132
     *
133
     * @return void
134
     * @throws \Exception Is thrown, if the file doesn't exists, the line is not found or can not be removed
135
     */
136
    protected function removeLineFromFile(string $line, string $filename) : void
137
    {
138
        $this->getGenericFileHandler()->removeLineFromFile($line, $filename);
0 ignored issues
show
Bug introduced by
The method getGenericFileHandler() does not seem to exist on object<TechDivision\Impo...oposedOkFilenameLoader>.

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...
139
    }
140
141
    /**
142
     * Query whether or not the basename, without suffix, of the passed filenames are equal.
143
     *
144
     * @param string $filename1 The first filename to compare
145
     * @param string $filename2 The second filename to compare
146
     *
147
     * @return boolean TRUE if the passed files basename are equal, else FALSE
148
     * @todo Refactorig required, because of duplicate method
149
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::isEqualFilename()
150
     */
151
    protected function isEqualFilename(string $filename1, string $filename2) : bool
152
    {
153
        return $this->stripSuffix($filename1, $this->getSuffix()) === $this->stripSuffix($filename2, $this->getOkFileSuffix());
154
    }
155
156
    /**
157
     * Strips the passed suffix, including the (.), from the filename and returns it.
158
     *
159
     * @param string $filename The filename to return the suffix from
160
     * @param string $suffix   The suffix to return
161
     *
162
     * @return string The filname without the suffix
163
     * @todo Refactorig required, because of duplicate method
164
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::stripSuffix()
165
     */
166
    protected function stripSuffix(string $filename, string $suffix) : string
167
    {
168
        return basename($filename, sprintf('.%s', $suffix));
169
    }
170
171
    /**
172
     * Prepares and returns an OK filename from the passed parts.
173
     *
174
     * @param array $parts The parts to concatenate the OK filename from
175
     *
176
     * @return string The OK filename
177
     * @todo Refactorig required, because of duplicate method
178
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::prepareOkFilename()
179
     */
180
    protected function prepareOkFilename(array $parts) : string
181
    {
182
        return sprintf('%s/%s.%s', $this->getSourceDir(), implode($this->getElementSeparator(), $parts), $this->getOkFileSuffix());
183
    }
184
185
    /**
186
     * Returns the delement separator char.
187
     *
188
     *  @return string The element separator char
189
     */
190
    protected function getElementSeparator() : string
191
    {
192
        return $this->getFileResolverConfiguration()->getElementSeparator();
193
    }
194
195
    /**
196
     * Returns the elements the filenames consists of.
197
     *
198
     * @return array The array with the filename elements
199
     * @todo Refactorig required, because of duplicate method
200
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::getPatternElements()
201
     */
202
    protected function getPatternElements() : array
203
    {
204
        return $this->getFileResolverConfiguration()->getPatternElements();
205
    }
206
207
    /**
208
     * Returns the suffix for the import files.
209
     *
210
     * @return string The suffix
211
     */
212
    protected function getSuffix() : string
213
    {
214
        return $this->getFileResolverConfiguration()->getSuffix();
215
    }
216
217
    /**
218
     * Returns the elements the filenames consists of.
219
     *
220
     * @return array The array with the filename elements
221
     * @todo Refactorig required, because of duplicate method
222
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::getOkFileSuffix()
223
     */
224
    protected function getOkFileSuffix() : string
225
    {
226
        return $this->getFileResolverConfiguration()->getOkFileSuffix();
227
    }
228
229
    /**
230
     * Returns the actual source directory to load the files from.
231
     *
232
     * @return string The actual source directory
233
     */
234
    protected function getSourceDir() : string
235
    {
236
        return $this->sourceDir;
237
    }
238
239
    /**
240
     * Returns the elements the filenames consists of, converted to lowercase.
241
     *
242
     * @return array The array with the filename elements
243
     * @todo Refactorig required, because of duplicate method
244
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::getPatternKeys()
245
     */
246
    protected function getPatternKeys() : array
247
    {
248
249
        // load the pattern keys from the configuration
250
        $patternKeys = $this->getPatternElements();
251
252
        // make sure that they are all lowercase
253
        array_walk($patternKeys, function (&$value) {
254
            $value = strtolower($value);
255
        });
256
257
        // return the pattern keys
258
        return $patternKeys;
259
    }
260
261
    /**
262
     * Resolves the pattern value for the given element name.
263
     *
264
     * @param string $element The element name to resolve the pattern value for
265
     *
266
     * @return string The resolved pattern value
267
     * @throws \InvalidArgumentException Is thrown, if the element value can not be loaded from the file resolver configuration
268
     * @todo Refactorig required, because of duplicate method
269
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::resolvePatternValue()
270
     */
271 View Code Duplication
    protected function resolvePatternValue(string $element) : string
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...
272
    {
273
274
        // query whether or not matches has been found OR the counter element has been passed
275
        if ($this->getLoader()->countMatches() === 0 || BunchKeys::COUNTER === $element) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Loaders\LoaderInterface as the method countMatches() does only exist in the following implementations of said interface: TechDivision\Import\Load...PregMatchFilteredLoader.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
276
            // prepare the method name for the callback to load the pattern value with
277
            $methodName = sprintf('get%s', ucfirst($element));
278
279
            // load the pattern value
280
            if (in_array($methodName, get_class_methods($this->getFileResolverConfiguration()))) {
281
                return call_user_func(array($this->getFileResolverConfiguration(), $methodName));
282
            }
283
284
            // stop processing and throw an exception
285
            throw new \InvalidArgumentException('Can\'t load pattern value for element "%s" from file resolver configuration', $element);
286
        }
287
288
        // try to load the pattern value from the matches
289
        return $this->getLoader()->getMatch($element);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Loaders\LoaderInterface as the method getMatch() does only exist in the following implementations of said interface: TechDivision\Import\Load...PregMatchFilteredLoader.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
290
    }
291
292
    /**
293
     * Returns the values to create the regex pattern from.
294
     *
295
     * @return array The array with the pattern values
296
     * @todo Refactorig required, because of duplicate method
297
     * @see \TechDivision\Import\Loaders\Filters\OkFileFilter::resolvePatternValues()
298
     */
299
    protected function resolvePatternValues() : array
300
    {
301
302
        // initialize the array
303
        $elements = array();
304
305
        // prepare the pattern values
306
        foreach ($this->getPatternKeys() as $element) {
307
            $elements[] = sprintf('(?<%s>%s)', $element, $this->resolvePatternValue($element));
308
        }
309
310
        // return the pattern values
311
        return $elements;
312
    }
313
314
    /**
315
     * Prepares and returns the pattern for the regex to load the files from the
316
     * source directory for the passed subject.
317
     *
318
     * @return string The prepared regex pattern
319
     */
320
    protected function getPattern() : string
321
    {
322
        return sprintf(
323
            $this->getRegex(),
324
            implode($this->getElementSeparator(), $this->resolvePatternValues()),
325
            $this->getSuffix()
326
        );
327
    }
328
329
    /**
330
     * Returns the match with the passed name.
331
     *
332
     * @param array $keys The keys of the match to return
333
     *
334
     * @return string|null The match itself
335
     */
336
    protected function proposedOkFilenamesByKeys(array $keys) : array
337
    {
338
339
        // intialize the array for the .OK filename => .CSV filenames
340
        $result = array();
341
342
        // load the matches from the preg match filesystem loader
343
        $matches = $this->getLoader()->getMatches();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Loaders\LoaderInterface as the method getMatches() does only exist in the following implementations of said interface: TechDivision\Import\Load...PregMatchFilteredLoader.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
344
345
        // process the matches
346
        foreach ($matches as $i => $match) {
347
            // initialize the array for the values of the match elements
348
            $elements = array();
349
            // load the values from the matches
350
            foreach ($keys as $key) {
351
                if (array_key_exists($key, $match)) {
352
                    $elements[$i][] = $match[$key];
353
                }
354
            }
355
356
            // use the values to create the proposed .OK filenames
357
            foreach ($elements as $element) {
358
                // load the source directory
359
                $sourceDir = dirname($csvPath = $match[0]);
360
                // create the filename by concatenating the element values
361
                $okFilename = implode($this->getElementSeparator(), $element);
362
                // create the path to the .OK file
363
                $okPath = sprintf('%s/%s.%s', $sourceDir, $okFilename, $this->getOkFileSuffix());
364
                // register the proposed .OK file in the array
365
                $result[$okPath][] = basename($csvPath);
366
            }
367
        }
368
369
        // return the array with the result
370
        return $result;
371
    }
372
373
    /**
374
     * Return's an array with the proposed .OK filenames as key and the matching CSV
375
     * files as values for the import with the passed serial.
376
     *
377
     * Based on the passed serial, the files with the configured prefix from the also
378
     * configured source directory will be loaded and processed to return the array
379
     * with the proposed .OK filenames.
380
     *
381
     * @return array The array with key => value pairs of the proposed .OK and the matching CSV files
382
     */
383
    protected function propsedOkFilenames() : array
384
    {
385
386
        // initialize the array for the available okFilenames
387
        $okFilenames = array();
388
389
        // load the pattern keys (prefix, filename and suffix),
390
        // that has to be used from the configuration
391
        $patternKeys = $this->getPatternKeys();
392
393
        // prepare the .OK filenames based on the found CSV file information
394 View Code Duplication
        for ($i = 1; $i <= sizeof($patternKeys); $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...
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...
395
            // initialize the array for the pattern that has to used to load
396
            // the propsed .OK file for
397
            $keys = array();
398
            // load the parts from the matches
399
            for ($z = 0; $z < $i; $z++) {
400
                $keys[] = $patternKeys[$z];
401
            }
402
            // merge the proposed .OK filenames for the passed keys into the array
403
            $okFilenames = array_merge_recursive($okFilenames, $this->proposedOkFilenamesByKeys($keys));
404
        }
405
406
        // prepare and return the pattern for the OK file
407
        return $okFilenames;
408
    }
409
410
    /**
411
     * Return's the actual source directory.
412
     *
413
     * @param string $sourceDir The actual source directory
414
     */
415
    public function setSourceDir(string $sourceDir) : void
416
    {
417
        $this->sourceDir = $sourceDir;
418
    }
419
420
    /**
421
     * Add's the passed sorter to the loader instance.
422
     *
423
     * @param callable $sorter The sorter to add
424
     *
425
     * @return void
426
     */
427
    public function addSorter(callable $sorter) : void
428
    {
429
        $this->getSorterImpl()->addSorter($sorter);
430
    }
431
432
    /**
433
     * Return's the array with the sorter callbacks.
434
     *
435
     * @return callable[] The sorter callbacks
436
     */
437
    public function getSorters() : array
438
    {
439
        return $this->getSorterImpl()->getSorters();
440
    }
441
442
    /**
443
     * Set's the file resolver configuration.
444
     *
445
     * @param \TechDivision\Import\Configuration\Subject\FileResolverConfigurationInterface $fileResolverConfiguration The file resolver configuration
446
     */
447
    public function setFileResolverConfiguration(FileResolverConfigurationInterface $fileResolverConfiguration) : void
448
    {
449
        $this->fileResolverConfiguration = $fileResolverConfiguration;
450
    }
451
452
    /**
453
     * Loads, sorts and returns the files by using the passed glob pattern.
454
     *
455
     * If no pattern will be passed to the `load()` method, the files of
456
     * the actual directory using `getcwd()` will be returned.
457
     *
458
     * @param string|null $pattern The pattern to load the files from the filesystem
459
     *
460
     * @return array The array with the data
461
     */
462
    public function load(string $pattern = null) : array
463
    {
464
465
        // load the pattern to filter the available CSV files with. We filter
466
        // the .CSV files to only get those that matches the prefix.
467
        $this->getLoader()->addFilter(new PregMatchFilter($this->getPattern()));
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface TechDivision\Import\Loaders\LoaderInterface as the method addFilter() does only exist in the following implementations of said interface: TechDivision\Import\Loaders\FilteredLoader, TechDivision\Import\Load...PregMatchFilteredLoader, TechDivision\Import\Load...roposedOkFilenameLoader.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
468
469
        // load the available CSV files and initialize the matches that
470
        // we need to create the list with the proposed .OK files
471
        $this->getLoader()->load($pattern);
0 ignored issues
show
Unused Code introduced by
The call to LoaderInterface::load() has too many arguments starting with $pattern.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
472
473
        // initialize the array for the available okFilenames
474
        $okFilenames = array();
475
476
        // load the pattern keys (prefix, filename and suffix),
477
        // that has to be used from the configuration
478
        $patternKeys = $this->getPatternKeys();
479
480
        // prepare the .OK filenames based on the found CSV file information
481 View Code Duplication
        for ($i = 1; $i <= sizeof($patternKeys); $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...
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...
482
            // initialize the array for the pattern that has to used to load
483
            // the propsed .OK file for
484
            $keys = array();
485
            // load the parts from the matches
486
            for ($z = 0; $z < $i; $z++) {
487
                $keys[] = $patternKeys[$z];
488
            }
489
            // merge the proposed .OK filenames for the passed keys into the array
490
            $okFilenames = array_merge_recursive($okFilenames, $this->proposedOkFilenamesByKeys($keys));
491
        }
492
493
        // finally, we filter and sort them with the configured behaviour
494
        $this->getFilterImpl()->filter($okFilenames);
495
        $this->getSorterImpl()->sort($okFilenames);
496
497
        // prepare and return the pattern for the OK file
498
        return $okFilenames;
499
    }
500
}
501