Passed
Push — master ( 433a1b...f03357 )
by Marc
01:59
created

FbpDumper   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 174
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 74.68%

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 2
dl 0
loc 174
ccs 59
cts 79
cp 0.7468
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A toJson() 0 4 1
A toYaml() 0 4 1
A toFbp() 0 4 1
C createFbp() 0 34 7
A examineConnectionTouple() 0 10 1
B examineProcess() 0 24 3
A hasElement() 0 12 3
A connectPorts() 0 10 1
A throwDumperException() 0 16 4
1
<?php
2
/*
3
 * This file is part of the phpflo\phpflo-fbp package.
4
 *
5
 * (c) Marc Aschmann <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace PhpFlo\Fbp;
12
13
use PhpFlo\Common\FbpDefinitionsInterface;
14
use PhpFlo\Exception\DumperException;
15
use Symfony\Component\Yaml\Yaml;
16
17
/**
18
 * Class FbpDumper
19
 *
20
 * @package PhpFlo\Fbp
21
 * @author Marc Aschmann <[email protected]>
22
 */
23
final class FbpDumper implements FbpDefinitionsInterface
24
{
25
    /**
26
     * @var array
27
     */
28
    private static $processes;
29
30
    /**
31
     * @param array $definition
32
     * @return string json
33
     */
34 2
    public static function toJson(array $definition)
35
    {
36 2
        return json_encode($definition, JSON_PRETTY_PRINT);
37
    }
38
39
    /**
40
     * @param array $definition
41
     * @param int $inline level until inlining starts
42
     * @return string yaml
43
     */
44 2
    public static function toYaml(array $definition, $inline = 3)
45
    {
46 2
        return Yaml::dump($definition, $inline);
47
    }
48
49
    /**
50
     * @param array $definition
51
     * @return string
52
     */
53 2
    public static function toFbp(array $definition)
54
    {
55 2
        return self::createFbp($definition);
56
    }
57
58
    /**
59
     * @param array $definition
60
     * @return string
61
     */
62 2
    private static function createFbp(array $definition)
63
    {
64 2
        $fbp = [];
65
66
        // first check for process definitions
67 2
        if (self::hasElement(self::PROCESSES_LABEL, $definition)) {
68 2
            self::$processes = $definition[self::PROCESSES_LABEL];
69 2
        }
70
71
        // handle initializer
72 2
        if (!empty($definition[self::INITIALIZERS_LABEL])) {
73 1
            foreach ($definition[self::INITIALIZERS_LABEL] as $initializer) {
74 1
                if (empty($initializer[self::DATA_LABEL])) {
75
                    self::throwDumperException('no_definition', self::DATA_LABEL);
76
                }
77 1
                if (empty($initializer[self::TARGET_LABEL])) {
78
                    self::throwDumperException('no_definition', self::TARGET_LABEL);
79
                }
80 1
                array_push(
81 1
                    $fbp,
82 1
                    self::connectPorts(
83 1
                        $initializer[self::DATA_LABEL],
84 1
                        self::examineProcess(self::TARGET_LABEL, $initializer[self::TARGET_LABEL])
85 1
                    )
86 1
                );
87 1
            }
88 1
        }
89
90 2
        foreach ($definition[self::CONNECTIONS_LABEL] as $connection) {
91 2
            array_push($fbp, self::examineConnectionTouple($connection));
92 2
        }
93
94 2
        return implode(self::FILE_LINEFEED, $fbp);
95
    }
96
97
    /**
98
     * Look for all needed fields and build a port -> port connection.
99
     *
100
     * @param array $connectionTouple
101
     * @return string
102
     */
103 2
    private static function examineConnectionTouple(array $connectionTouple)
104
    {
105 2
        self::hasElement(self::SOURCE_LABEL, $connectionTouple);
106 2
        self::hasElement(self::TARGET_LABEL, $connectionTouple);
107
108 2
        return self::connectPorts(
109 2
            self::examineProcess(self::SOURCE_LABEL, $connectionTouple[self::SOURCE_LABEL]),
110 2
            self::examineProcess(self::TARGET_LABEL, $connectionTouple[self::TARGET_LABEL])
111 2
        );
112
    }
113
114
    /**
115
     * @param string $type
116
     * @param array $processPart
117
     * @throws DumperException
118
     * @return string
119
     */
120 2
    private static function examineProcess($type, array $processPart)
121
    {
122 2
        self::hasElement(self::PROCESS_LABEL, $processPart);
123 2
        self::hasElement(self::PORT_LABEL, $processPart);
124
125 2
        $inport = '';
126 2
        $outport = '';
127 2
        $process = $processPart[self::PROCESS_LABEL];
128 2
        $port = $processPart[self::PORT_LABEL];
129
130 2
        if (self::hasElement($process, self::$processes, false)) {
131 2
            $meta = "(" . self::$processes[$process][self::COMPONENT_LABEL] . ")";
132 2
        } else {
133
            self::throwDumperException('process', $process);
134
        }
135
136 2
        if (self::SOURCE_LABEL == $type) {
137 2
            $outport = " {$port}";
138 2
        } else {
139 2
            $inport = "{$port} ";
140
        }
141
142 2
        return "{$inport}{$process}{$meta}{$outport}";
1 ignored issue
show
Bug introduced by
The variable $meta does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
143
    }
144
145
    /**
146
     * @param string $needle
147
     * @param array $haystack
148
     * @param bool $triggerException
149
     * @return bool
150
     */
151 2
    private static function hasElement($needle, array $haystack, $triggerException = true)
152
    {
153 2
        if (empty($haystack[$needle])) {
154
            if ($triggerException) {
155
                self::throwDumperException('elmeent', $needle);
156
            } else {
157
                return false;
158
            }
159
        }
160
161 2
        return true;
162
    }
163
164
    /**
165
     * @param string $sourcePort
166
     * @param string $targetPort
167
     * @return string
168
     */
169 2
    private static function connectPorts($sourcePort, $targetPort)
170
    {
171 2
        return implode(
172 2
            " " . self::SOURCE_TARGET_SEPARATOR . " ",
173
            [
174 2
                $sourcePort,
175
                $targetPort
176 2
            ]
177 2
        );
178
    }
179
180
    private static function throwDumperException($type, $value)
181
    {
182
        switch ($type) {
183
            case 'element':
184
                throw new DumperException("Element has no {$value}");
185
                break;
1 ignored issue
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
186
            case 'process':
187
                throw new DumperException("{$value} is not defined in " . self::PROCESSES_LABEL);
188
                break;
1 ignored issue
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
189
            case 'no_definition':
190
                throw new DumperException("Defintion has " .
191
                    self::INITIALIZERS_LABEL . " but no {$value} node"
192
                );
193
                break;
1 ignored issue
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
194
        }
195
    }
196
}
197