GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

XmlWorkflowFactory   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 469
Duplicated Lines 0.85 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 100%

Importance

Changes 5
Bugs 0 Features 0
Metric Value
wmc 49
c 5
b 0
f 0
lcom 1
cbo 11
dl 4
loc 469
ccs 164
cts 164
cp 1
rs 8.5454

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A setLayout() 0 3 1
A getLayout() 0 4 1
A getName() 0 4 1
A isModifiable() 0 4 1
A serialize() 0 3 1
A unserialize() 0 3 1
A getWorkflowNames() 0 6 1
A removeWorkflow() 0 4 1
A renameWorkflow() 0 3 1
A save() 0 3 1
A createWorkflow() 0 3 1
A initDefaultPathsToWorkflows() 0 4 1
B initDone() 0 50 4
A buildWorkflowConfig() 0 6 1
B getWorkflow() 4 22 6
A loadWorkflow() 0 10 2
B getBaseDir() 0 22 4
B getPathWorkflowFile() 0 23 5
A getDefaultPathsToWorkflows() 0 4 1
A setDefaultPathsToWorkflows() 0 4 1
A addDefaultPathToWorkflows() 0 6 1
C saveWorkflow() 0 49 8
A createBackupFile() 0 6 2
A createNewWorkflowFile() 0 6 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like XmlWorkflowFactory often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use XmlWorkflowFactory, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @link    https://github.com/old-town/old-town-workflow
4
 * @author  Malofeykin Andrey  <[email protected]>
5
 */
6
namespace OldTown\Workflow\Loader;
7
8
use OldTown\Workflow\Exception\FactoryException;
9
use OldTown\Workflow\Exception\InvalidParsingWorkflowException;
10
use OldTown\Workflow\Exception\InvalidWriteWorkflowException;
11
use OldTown\Workflow\Exception\UnsupportedOperationException;
12
use OldTown\Workflow\Util\Properties\Properties;
13
use Serializable;
14
use OldTown\Workflow\Loader\XMLWorkflowFactory\WorkflowConfig;
15
use DOMElement;
16
use DOMDocument;
17
use OldTown\Workflow\Exception\RuntimeException;
18
19
/**
20
 * Class UrlWorkflowFactory
21
 *
22
 * @package OldTown\Workflow\Loader
23
 */
24
class  XmlWorkflowFactory extends AbstractWorkflowFactory implements Serializable
25
{
26
    /**
27
     *
28
     * @var string
29
     */
30
    const RESOURCE_PROPERTY = 'resource';
31
32
    /**
33
     *
34
     * @var string
35
     */
36
    const RELOAD_PROPERTY = 'reload';
37
38
    /**
39
     * @var WorkflowConfig[]
40
     */
41
    protected $workflows = [];
42
43
    /**
44
     * @var bool
45
     */
46
    protected $reload = false;
47
48
    /**
49
     * Пути по умолчнаию до файла с workflows
50
     *
51
     * @var array
52
     */
53
    protected static $defaultPathsToWorkflows = [];
54
55
    /**
56
     * @param Properties $p
57
     */
58 23
    public function __construct(Properties $p = null)
59
    {
60 23
        parent::__construct($p);
61 23
        $this->initDefaultPathsToWorkflows();
62 23
    }
63
64
65
    /**
66
     * @param string $workflowName
67
     * @param string $layout
68
     *
69
     * @return $this
70
     */
71 1
    public function setLayout($workflowName, $layout)
72
    {
73 1
    }
74
75
    /**
76
     * @param string $workflowName
77
     *
78
     * @return mixed|null
79
     */
80 1
    public function getLayout($workflowName)
81
    {
82 1
        return null;
83
    }
84
85
86
    /**
87
     *
88
     * @return string
89
     */
90 1
    public function getName()
91
    {
92 1
        return '';
93
    }
94
95
    /**
96
     * @param string $name
97
     *
98
     * @return boolean
99
     */
100 1
    public function isModifiable($name)
101
    {
102 1
        return true;
103
    }
104
105
    /**
106
     * String representation of object
107
     *
108
     * @link http://php.net/manual/en/serializable.serialize.php
109
     * @return string the string representation of the object or null
110
     */
111 1
    public function serialize()
112
    {
113 1
    }
114
115
    /**
116
     * Constructs the object
117
     *
118
     * @link http://php.net/manual/en/serializable.unserialize.php
119
     *
120
     * @param string $serialized <p>
121
     *
122
     * @return void
123
     */
124 1
    public function unserialize($serialized)
125
    {
126 1
    }
127
    /**
128
     *
129
     * @return String[]
130
     * @throws FactoryException
131
     */
132 1
    public function getWorkflowNames()
133
    {
134 1
        $workflowNames = array_keys($this->workflows);
135
136 1
        return $workflowNames;
137
    }
138
139
    /**
140
     * @param string $name
141
     *
142
     * @return boolean
143
     * @throws FactoryException
144
     */
145 1
    public function removeWorkflow($name)
146
    {
147 1
        throw new FactoryException('Удаление workflow не поддерживается');
148
    }
149
150
    /**
151
     * @param string $oldName
152
     * @param string $newName
153
     *
154
     * @return void
155
     */
156 1
    public function renameWorkflow($newName, $oldName = null)
157
    {
158 1
    }
159
160
    /**
161
     * @return void
162
     */
163 1
    public function save()
164
    {
165 1
    }
166
167
    /**
168
     * @param string $name
169
     *
170
     * @return void
171
     * @throws FactoryException
172
     */
173 1
    public function createWorkflow($name)
174
    {
175 1
    }
176
177
    /**
178
     * Иницализация путей по которым происходит поиск
179
     *
180
     * @return void
181
     */
182 23
    protected function initDefaultPathsToWorkflows()
183
    {
184 23
        static::$defaultPathsToWorkflows[] = __DIR__ . '/../../config';
185 23
    }
186
187
188
189
    /**
190
     *
191
     * @return void
192
     * @throws FactoryException
193
     * @throws InvalidParsingWorkflowException
194
     */
195 11
    public function initDone()
196
    {
197 11
        $this->reload = 'true' === $this->getProperties()->getProperty(static::RELOAD_PROPERTY, 'false');
198
199 11
        $name = $this->getProperties()->getProperty(static::RESOURCE_PROPERTY, 'workflows.xml');
200
201 11
        $pathWorkflowFile = $this->getPathWorkflowFile($name);
202 10
        $content = file_get_contents($pathWorkflowFile);
203
204
        try {
205 10
            libxml_use_internal_errors(true);
206
207 10
            libxml_clear_errors();
208
209 10
            $xmlDoc = new DOMDocument();
210 10
            $xmlDoc->loadXML($content);
211
212 10
            if ($error = libxml_get_last_error()) {
213 1
                $errMsg = "Error in workflow xml.\n";
214 1
                $errMsg .= "Message: {$error->message}.\n";
215 1
                $errMsg .= "File: {$error->file}.\n";
216 1
                $errMsg .= "Line: {$error->line}.\n";
217 1
                $errMsg .= "Column: {$error->column}.";
218
219 1
                throw new InvalidParsingWorkflowException($errMsg);
220
            }
221
222
            /** @var DOMElement $root */
223 9
            $root = $xmlDoc->getElementsByTagName('workflows')->item(0);
224
225 9
            $basedir = $this->getBaseDir($root, $pathWorkflowFile);
226
227 8
            $list = XmlUtil::getChildElements($root, 'workflow');
228
229
230 8
            foreach ($list as $e) {
231 8
                $type = XmlUtil::getRequiredAttributeValue($e, 'type');
232 8
                $location = XmlUtil::getRequiredAttributeValue($e, 'location');
233 8
                $config = $this->buildWorkflowConfig($basedir, $type, $location);
234 8
                $name = XmlUtil::getRequiredAttributeValue($e, 'name');
235 8
                $this->workflows[$name] = $config;
236 8
            }
237 10
        } catch (\Exception $e) {
238 2
            $errMsg = sprintf(
239 2
                'Ошибка в конфигурации workflow: %s',
240 2
                $e->getMessage()
241 2
            );
242 2
            throw new InvalidParsingWorkflowException($errMsg, $e->getCode(), $e);
243
        }
244 8
    }
245
246
    /**
247
     * @param $basedir
248
     * @param $type
249
     * @param $location
250
     *
251
     * @return WorkflowConfig
252
     * @throws \OldTown\Workflow\Exception\RemoteException
253
     */
254 7
    protected function buildWorkflowConfig($basedir, $type, $location)
255
    {
256 7
        $config = new WorkflowConfig($basedir, $type, $location);
257
258 7
        return $config;
259
    }
260
261
    /**
262
     * @param string $name
263
     * @param bool   $validate
264
     *
265
     * @return WorkflowDescriptor
266
     * @throws FactoryException
267
     */
268 9
    public function getWorkflow($name, $validate = true)
269
    {
270 9
        $name = (string)$name;
271 9 View Code Duplication
        if (!array_key_exists($name, $this->workflows)) {
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...
272 1
            $errMsg = sprintf('Нет workflow с именем %s', $name);
273 1
            throw new FactoryException($errMsg);
274
        }
275 8
        $c = $this->workflows[$name];
276
277 8
        if (null !== $c->descriptor) {
278 1
            if ($this->reload && file_exists($c->url) && (filemtime($c->url) > $c->lastModified)) {
279 1
                $c->lastModified = filemtime($c->url);
280 1
                $this->loadWorkflow($c, $validate);
281 1
            }
282 1
        } else {
283 8
            $this->loadWorkflow($c, $validate);
284
        }
285
286 7
        $c->descriptor->setName($name);
287
288 7
        return $c->descriptor;
289
    }
290
291
    /**
292
     * @param WorkflowConfig $c
293
     * @param boolean        $validate
294
     *
295
     * @return void
296
     * @throws FactoryException
297
     */
298 8
    protected function loadWorkflow(WorkflowConfig $c, $validate = true)
299
    {
300 8
        $validate = (boolean)$validate;
301
        try {
302 8
            $c->descriptor = WorkflowLoader::load($c->url, $validate);
303 8
        } catch (\Exception $e) {
304 1
            $errMsg = "Некорректный дескрипторв workflow: {$c->url}";
305 1
            throw new FactoryException($errMsg, $e->getCode(), $e);
306
        }
307 7
    }
308
309
    /**
310
     * Возвращает абсолютный путь до директории где находится workflow xml файл
311
     *
312
     * @param DOMElement $root
313
     *
314
     * @param            $pathWorkflowFile
315
     *
316
     * @return string
317
     * @throws RuntimeException
318
     */
319 9
    protected function getBaseDir(DOMElement $root, $pathWorkflowFile)
320
    {
321 9
        if (!$root->hasAttribute('basedir')) {
322 1
            return null;
323
        }
324 8
        $basedirAtr = XmlUtil::getRequiredAttributeValue($root, 'basedir');
325
326
327 8
        $basedir = $basedirAtr;
328 8
        if (0 === strpos($basedir, '.')) {
329 8
            $basedir = dirname($pathWorkflowFile) .  substr($basedir, 1);
330 8
        }
331
332 8
        if (file_exists($basedir)) {
333 7
            $absolutePath = realpath($basedir);
334 7
        } else {
335 1
            $errMsg = sprintf('Отсутствует ресурс %s', $basedirAtr);
336 1
            throw new RuntimeException($errMsg);
337
        }
338
339 7
        return $absolutePath;
340
    }
341
342
343
344
345
346
    /**
347
     * @param string $name
348
     *
349
     * @return string
350
     * @throws FactoryException
351
     */
352 10
    protected function getPathWorkflowFile($name)
353
    {
354 10
        $paths = static::getDefaultPathsToWorkflows();
355
356 10
        $pathWorkflowFile = null;
357 10
        foreach ($paths as $path) {
358 10
            $path = realpath($path);
359 10
            if ($path) {
360 10
                $filePath = $path . DIRECTORY_SEPARATOR . $name;
361 10
                if (file_exists($filePath)) {
362 9
                    $pathWorkflowFile = $filePath;
363 9
                    break;
364
                }
365 1
            }
366 10
        }
367
368 10
        if (null === $pathWorkflowFile) {
369 1
            $errMsg = 'Не удалось найти файл workflow';
370 1
            throw new FactoryException($errMsg);
371
        }
372
373 9
        return $pathWorkflowFile;
374
    }
375
376
    /**
377
     * @return array
378
     */
379 23
    public static function getDefaultPathsToWorkflows()
380
    {
381 23
        return static::$defaultPathsToWorkflows;
382
    }
383
384
    /**
385
     * @param array $defaultPathsToWorkflows
386
     */
387 23
    public static function setDefaultPathsToWorkflows(array $defaultPathsToWorkflows = [])
388
    {
389 23
        static::$defaultPathsToWorkflows = $defaultPathsToWorkflows;
390 23
    }
391
392
    /**
393
     * @param string $path
394
     */
395 9
    public static function addDefaultPathToWorkflows($path)
396
    {
397 9
        $path = (string)$path;
398
399 9
        array_unshift(static::$defaultPathsToWorkflows, $path);
400 9
    }
401
402
    /**
403
     * Сохраняет workflow
404
     *
405
     * @param string             $name       имя workflow
406
     * @param WorkflowDescriptor $descriptor descriptor workflow
407
     * @param boolean            $replace    если true - то в случае существования одноименного workflow, оно будет
408
     *                                       заменено
409
     *
410
     * @return boolean true - если workflow было сохранено
411
     * @throws FactoryException
412
     * @throws UnsupportedOperationException
413
     * @throws InvalidWriteWorkflowException
414
     */
415 5
    public function saveWorkflow($name, WorkflowDescriptor $descriptor, $replace = false)
416
    {
417 5
        $name = (string)$name;
418 5
        $c = array_key_exists($name, $this->workflows) ? $this->workflows[$name] : null;
419
420 5
        if (null !== $c && !$replace) {
421 1
            return false;
422
        }
423
424 4
        if (null === $c) {
425 1
            $errMsg = 'Сохранение workflow не поддерживается';
426 1
            throw new UnsupportedOperationException($errMsg);
427
        }
428
429
        try {
430 3
            $content = $descriptor->writeXml();
431 3
            $newFileName = $c->url . '.new';
432 3
            $content->save($newFileName);
433
434
435 3
            $bakFileName = $c->url . '.bak';
436 3
            $isOk = $this->createBackupFile($c->url, $bakFileName);
437
438 3
            if (!$isOk) {
439 1
                $errMsg = sprintf(
440 1
                    'Ошибка при архивирование оригинального файла workflow %s в %s - сохранение прервано',
441 1
                    $c->url,
442
                    $bakFileName
443 1
                );
444 1
                throw new FactoryException($errMsg);
445
            }
446 2
            $isOk = $this->createNewWorkflowFile($newFileName, $c->url);
447
448 2
            if (!$isOk) {
449 1
                $errMsg = sprintf(
450 1
                    'Ошибка при переименовывание нового файла workflow %s в %s - сохранение прервано',
451 1
                    $newFileName,
452 1
                    $c->url
453 1
                );
454 1
                throw new FactoryException($errMsg);
455
            }
456
457 1
            unlink($bakFileName);
458
459 1
            return true;
460 2
        } catch (\Exception $e) {
461 2
            throw new InvalidWriteWorkflowException($e->getMessage(), $e->getCode(), $e);
462
        }
463
    }
464
465
    /**
466
     * Архивирование оригинального файла workflow
467
     *
468
     * @param $original
469
     * @param $backup
470
     *
471
     * @return bool
472
     */
473 2
    protected function createBackupFile($original, $backup)
474
    {
475 2
        $isOk = !file_exists($original) || rename($original, $backup);
476
477 2
        return $isOk;
478
    }
479
480
    /**
481
     * @param $newFileName
482
     * @param $targetFile
483
     *
484
     * @return bool
485
     */
486 1
    protected function createNewWorkflowFile($newFileName, $targetFile)
487
    {
488 1
        $isOk = rename($newFileName, $targetFile);
489
490 1
        return $isOk;
491
    }
492
}
493