Passed
Push — master ( 28a3ce...b9357e )
by Siad
05:23
created

ModifiedSelector::useParameter()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 27
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 10.7998

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 25
c 1
b 0
f 0
nc 10
nop 1
dl 0
loc 27
ccs 20
cts 25
cp 0.8
crap 10.7998
rs 7.6666

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
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
/**
21
 * @author  Siad Ardroumli <[email protected]>
22
 * @package phing.types.selectors
23
 */
24
class ModifiedSelector extends BaseExtendSelector implements BuildListener
25
{
26
    private const CACHE_PREFIX = "cache.";
27
    private const ALGORITHM_PREFIX = "algorithm.";
28
    private const COMPARATOR_PREFIX = "comparator.";
29
30
    /** Cache name for later instantiation. */
31
    private $cacheName;
32
33
    /** User specified classname for Cache. */
34
    private $cacheClass;
35
36
    /** Algorithm name for later instantiation. */
37
    private $algoName;
38
39
    /** User specified classname for Algorithm. */
40
    private $algorithmClass;
41
42
    /** Comparator name for later instantiation. */
43
    private $compName;
44
45
    /** User specified classname for Comparator. */
46
    private $comparatorClass;
47
48
    private $update = true;
49
    private $selectDirectories = true;
50
    /** @var Cache */
51
    private $cache;
52
    /** @var Algorithm */
53
    private $algorithm;
54
    /** @var Comparator */
55
    private $comparator;
56
    private $delayUpdate = true;
57
    private $modified = 0;
58
    private $isConfigured = false;
59
    /** @var PhingFile */
60
    private $cachefile;
61
    /** @var Parameter[] */
62
    private $configParameter = [];
63
    /** @var Parameter[] */
64
    private $specialParameter = [];
65
66
    /**
67
     * Get the cache type to use.
68
     * @return Cache the enumerated cache type
69
     */
70
    public function getCache(): Cache
71
    {
72
        return $this->cache;
73
    }
74
75
    /**
76
     * Set the cache type to use.
77
     * @param string $name an enumerated cache type.
78
     */
79 3
    public function setCache(string $name): void
80
    {
81 3
        $this->cacheName = $name;
82 3
    }
83
84 4
    public function verifySettings()
85
    {
86 4
        $this->configure();
87 4
        if ($this->cache === null) {
88 1
            $this->setError('Cache must be set.');
89 3
        } elseif ($this->algorithm === null) {
90 1
            $this->setError('Algorithm must be set.');
91 2
        } elseif (!$this->cache->isValid()) {
92
            $this->setError('Cache must be proper configured.');
93 2
        } elseif (!$this->algorithm->isValid()) {
94
            $this->setError('Algorithm must be proper configured.');
95
        }
96 4
    }
97
98 4
    public function configure(): void
99
    {
100 4
        if ($this->isConfigured) {
101 2
            return;
102
        }
103 4
        $this->isConfigured = true;
104
105 4
        $p = $this->getProject();
106 4
        $filename = 'cache.properties';
107 4
        if ($p !== null) {
108
            // normal use inside Phing
109 2
            $this->cachefile = new PhingFile($p->getBasedir(), $filename);
110
111
            // set self as a BuildListener to delay cachefile saves
112 2
            $this->getProject()->addBuildListener($this);
113
        } else {
114
            // no reference to project - e.g. during normal JUnit tests
115 2
            $this->cachefile = new PhingFile($filename);
116 2
            $this->setDelayUpdate(false);
117
        }
118 4
        $defaultCache = new PropertiesfileCache($this->cachefile);
119 4
        $defaultAlgorithm = new HashfileAlgorithm();
120 4
        $defaultComparator = new EqualComparator();
121
122
        //
123
        // -----  Set the main attributes, pattern '*'  -----
124
        //
125 4
        foreach ($this->configParameter as $parameter) {
126 2
            if (strpos($parameter->getName(), '.') > 0) {
127
                // this is a *.* parameter for later use
128 2
                $this->specialParameter[] = $parameter;
129
            } else {
130 1
                $this->useParameter($parameter);
131
            }
132
        }
133 4
        $this->configParameter = [];
134
135
        // specify the algorithm classname
136 4
        if ($this->algoName !== null) {
137
            // use Algorithm defined via name
138 3
            if ('hashfile' === $this->algoName) {
139 1
                $this->algorithm = new HashfileAlgorithm();
140 2
            } elseif ('lastmodified' === $this->algoName) {
141 3
                $this->algorithm = new LastModifiedAlgorithm();
142
            }
143 1
        } elseif ($this->algorithmClass !== null) {
144
            // use Algorithm specified by classname
145
            $clz = Phing::import($this->algorithmClass);
146
            $this->algorithm = new $clz();
147
            if (!$this->algorithm instanceof Algorithm) {
148
                throw new BuildException($this->algorithmClass . " is not an Algorithm.");
149
            }
150
        } else {
151
            // nothing specified - use default
152 1
            $this->algorithm = $defaultAlgorithm;
153
        }
154
155
        // specify the cache classname
156 4
        if ($this->cacheName !== null) {
157
            // use Cache defined via name
158 3
            if ('propertyfile' === $this->cacheName) {
159 3
                $this->cache = new PropertiesfileCache();
160
            }
161 1
        } elseif ($this->cacheClass !== null) {
162
            // use Cache specified by classname
163
            $clz = Phing::import($this->cacheClass);
164
            $this->cache = new $clz();
165
            if (!$this->cache instanceof Cache) {
166
                throw new BuildException($this->cacheClass . " is not a Cache.");
167
            }
168
        } else {
169
            // nothing specified - use default
170 1
            $this->cache = $defaultCache;
171
        }
172
173
        // specify the comparator classname
174 4
        if ($this->compName !== null) {
175
            // use Algorithm defined via name
176 2
            if ('equal' === $this->compName) {
177 2
                $this->comparator = new EqualComparator();
178
            }
179 2
        } elseif ($this->comparatorClass !== null) {
180
            // use Comparator specified by classname
181
            $localComparator = Phing::import($this->comparatorClass);
182
            if (!$localComparator instanceof Comparator) {
0 ignored issues
show
introduced by
$localComparator is never a sub-type of Comparator.
Loading history...
183
                throw new BuildException($this->comparatorClass . " is not a Comparator.");
184
            }
185
            $this->comparator = $localComparator;
186
        } else {
187
            // nothing specified - use default
188 2
            $this->comparator = $defaultComparator;
189
        }
190 4
        foreach ($this->specialParameter as $special) {
191 2
            $this->useParameter($special);
192
        }
193 4
        $this->specialParameter = [];
194 4
    }
195
196
    /**
197
     * Support for nested <code>&lt;param name="" value=""/&gt;</code> tags.
198
     * Parameter named <i>cache</i>, <i>algorithm</i>,
199
     * <i>comparator</i> or <i>update</i> are mapped to
200
     * the respective set-Method.
201
     * Parameter which names starts with <i>cache.</i> or
202
     * <i>algorithm.</i> or <i>comparator.</i> are tried
203
     * to set on the appropriate object via its set-methods.
204
     * Other parameters are invalid and an BuildException will
205
     * be thrown.
206
     *
207
     * @param parameter  Key and value as parameter object
0 ignored issues
show
Bug introduced by
The type Key was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
208
     */
209 2
    public function useParameter(Parameter $parameter): void
210
    {
211 2
        $key = $parameter->getName();
212 2
        $value = $parameter->getValue();
213 2
        if ('cache' === $key) {
214 1
            $this->setCache($value);
215 2
        } elseif ('algorithm' === $key) {
216 1
            $this->setAlgorithm($value);
217 2
        } elseif ("comparator" === $key) {
218 1
            $this->setComparator($value);
219 2
        } elseif ('update' === $key) {
220 1
            $this->setUpdate('true' === strtolower($value));
221 2
        } elseif ('delayupdate' === $key) {
222
            $this->setDelayUpdate('true' === strtolower($value));
223 2
        } elseif ('seldirs' === $key) {
224 1
            $this->setSeldirs("true" === strtolower($value));
225 2
        } elseif (StringHelper::startsWith(self::CACHE_PREFIX, $key)) {
226 2
            $name = StringHelper::substring($key, strlen(self::CACHE_PREFIX));
227 2
            $this->tryToSetAParameter($this->cache, $name, $value);
228 2
        } elseif (StringHelper::startsWith(self::ALGORITHM_PREFIX, $key)) {
229 2
            $name = StringHelper::substring($key, strlen(self::ALGORITHM_PREFIX));
230 2
            $this->tryToSetAParameter($this->algorithm, $name, $value);
231
        } elseif (StringHelper::startsWith(self::COMPARATOR_PREFIX, $key)) {
232
            $name = StringHelper::substring($key, strlen(self::COMPARATOR_PREFIX));
233
            $this->tryToSetAParameter($this->comparator, $name, $value);
234
        } else {
235
            $this->setError("Invalid parameter " . $key);
236
        }
237 2
    }
238
239
    /**
240
     * Support for <i>update</i> attribute.
241
     * @param bool $update new value
242
     */
243 2
    public function setUpdate(bool $update): void
244
    {
245 2
        $this->update = $update;
246 2
    }
247
248
    /**
249
     * Support for <i>seldirs</i> attribute.
250
     * @param bool $seldirs new value
251
     */
252 2
    public function setSeldirs(bool $seldirs): void
253
    {
254 2
        $this->selectDirectories = $seldirs;
255 2
    }
256
257
    /**
258
     * Try to set a value on an object using reflection.
259
     * Helper method for easier access to IntrospectionHelper.setAttribute().
260
     * @param object $obj the object on which the attribute should be set
261
     * @param string $name the attributename
262
     * @param string $value the new value
263
     */
264 2
    protected function tryToSetAParameter(object $obj, string $name, string $value): void
265
    {
266 2
        $prj = $this->getProject() ?? new Project();
267 2
        $iHelper = IntrospectionHelper::getHelper(get_class($obj));
268
        try {
269 2
            $iHelper->setAttribute($prj, $obj, $name, $value);
270 1
        } catch (BuildException $e) {
271
            // no-op
272
        }
273 2
    }
274
275
    /**
276
     * Support for nested &lt;param&gt; tags.
277
     * @param Parameter $parameter the parameter object
278
     */
279 2
    public function addParam(Parameter $parameter)
280
    {
281 2
        $this->configParameter[] = $parameter;
282 2
    }
283
284
    /**
285
     * Defined in org.apache.tools.ant.types.Parameterizable.
286
     * Overwrite implementation in superclass because only special
287
     * parameters are valid.
288
     * @see #addParam(String,Object)
289
     * @param array $parameters the parameters to set.
290
     */
291
    public function setParameters(array $parameters): void
292
    {
293
        parent::setParameters($parameters);
294
        foreach ($parameters as $param) {
295
            $this->configParameter[] = $param;
296
        }
297
    }
298
299
    /**
300
     * @param BuildEvent $event
301
     * @return mixed
302
     */
303
    public function buildStarted(BuildEvent $event)
304
    {
305
        // do nothing
306
    }
307
308
    /**
309
     * @param BuildEvent $event
310
     * @return mixed
311
     */
312
    public function buildFinished(BuildEvent $event)
313
    {
314
        if ($this->getDelayUpdate()) {
315
            $this->saveCache();
316
        }
317
    }
318
319
    /**
320
     * Getter for the delay update
321
     * @return bool true if we should delay for performance
322
     */
323 2
    public function getDelayUpdate(): bool
324
    {
325 2
        return $this->delayUpdate;
326
    }
327
328 2
    public function setDelayUpdate(bool $false): void
329
    {
330 2
        $this->delayUpdate = $false;
331 2
    }
332
333
    /**
334
     * save the cache file
335
     */
336 2
    protected function saveCache(): void
337
    {
338 2
        if ($this->getModified() > 0) {
339 1
            $this->cache->save();
340 1
            $this->setModified(0);
341
        }
342 2
    }
343
344
    /**
345
     * Getter for the modified count
346
     * @return int $modified count
347
     */
348 2
    public function getModified(): int
349
    {
350 2
        return $this->modified;
351
    }
352
353
    /**
354
     * Setter for the modified count
355
     * @param int $modified count
356
     */
357 1
    public function setModified(int $modified): void
358
    {
359 1
        $this->modified = $modified;
360 1
    }
361
362
    /**
363
     * Setter for algorithmClass.
364
     * @param string $classname
365
     */
366
    public function setAlgorithmClass(string $classname): void
367
    {
368
        $this->algorithmClass = $classname;
369
    }
370
371
    /**
372
     * Setter for comparatorClass.
373
     * @param string $classname
374
     */
375
    public function setComparatorClass(string $classname): void
376
    {
377
        $this->comparatorClass = $classname;
378
    }
379
380
    /**
381
     * Setter for cacheClass.
382
     * @param string $classname
383
     */
384
    public function setCacheClass(string $classname): void
385
    {
386
        $this->cacheClass = $classname;
387
    }
388
389
    /**
390
     * @param BuildEvent $event
391
     * @return mixed
392
     */
393 2
    public function targetStarted(BuildEvent $event)
394
    {
395
        // do nothing
396 2
    }
397
398
    /**
399
     * @param BuildEvent $event
400
     * @return mixed
401
     */
402 2
    public function targetFinished(BuildEvent $event)
403
    {
404 2
        if ($this->getDelayUpdate()) {
405 2
            $this->saveCache();
406
        }
407 2
    }
408
409
    /**
410
     * @param BuildEvent $event
411
     * @return mixed
412
     */
413 2
    public function taskStarted(BuildEvent $event)
414
    {
415
        // do nothing
416 2
    }
417
418
    /**
419
     * @param BuildEvent $event
420
     * @return mixed
421
     */
422 2
    public function taskFinished(BuildEvent $event)
423
    {
424 2
        if ($this->getDelayUpdate()) {
425 2
            $this->saveCache();
426
        }
427 2
    }
428
429
    /**
430
     * @param BuildEvent $event
431
     * @return mixed
432
     */
433 2
    public function messageLogged(BuildEvent $event)
434
    {
435
        // do nothing
436 2
    }
437
438
    /**
439
     * @param PhingFile $basedir
440
     * @param string $filename
441
     * @param PhingFile $file
442
     * @return bool|null
443
     */
444 2
    public function isSelected(PhingFile $basedir, $filename, PhingFile $file)
445
    {
446 2
        $this->validate();
447
        try {
448 2
            $f = new PhingFile($basedir, $filename);
449
450
            // You can not compute a value for a directory
451 2
            if ($f->isDirectory()) {
452 2
                return $this->selectDirectories;
453
            }
454
        } catch (Throwable $t) {
455
            throw new BuildException($t);
456
        }
457
458
        // Get the values and do the comparison
459 2
        $cachedValue = (string) $this->cache->get($f->getAbsolutePath());
460 2
        $newValue = $this->algorithm->getValue($f);
461
462 2
        $rv = $this->comparator->compare($cachedValue, $newValue) !== 0;
463
464
        // Maybe update the cache
465 2
        if ($this->update && $rv) {
466 1
            $this->cache->put($f->getAbsolutePath(), $newValue);
467 1
            $this->setModified($this->getModified() + 1);
468 1
            if (!$this->getDelayUpdate()) {
469
                $this->saveCache();
470
            }
471
        }
472 2
        return $rv;
473
    }
474
475 2
    public function __toString(): string
476
    {
477 2
        $this->configure();
478 2
        return sprintf(
479 2
            '{modifiedselector update=%s seldirs=%s cache=%s algorithm=%s comparator=%s}',
480 2
            $this->update === true ? 'true' : 'false',
481 2
            $this->selectDirectories === true ? 'true' : 'false',
482 2
            $this->cache,
0 ignored issues
show
Bug introduced by
$this->cache of type Cache is incompatible with the type string expected by parameter $args of sprintf(). ( Ignorable by Annotation )

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

482
            /** @scrutinizer ignore-type */ $this->cache,
Loading history...
483 2
            $this->algorithm,
0 ignored issues
show
Bug introduced by
$this->algorithm of type Algorithm is incompatible with the type string expected by parameter $args of sprintf(). ( Ignorable by Annotation )

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

483
            /** @scrutinizer ignore-type */ $this->algorithm,
Loading history...
484 2
            $this->comparator
485
        );
486
    }
487
488 3
    public function setAlgorithm(string $an): void
489
    {
490 3
        $this->algoName = $an;
491 3
    }
492
493 2
    public function setComparator($value): void
494
    {
495 2
        $this->compName = $value;
496 2
    }
497
}
498