Completed
Push — master ( 0d4b6e...5bcf65 )
by Xeriab
02:57
created

Properties::parseProperties()   C

Complexity

Conditions 11
Paths 6

Size

Total Lines 61
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 1
Bugs 1 Features 0
Metric Value
dl 0
loc 61
ccs 0
cts 0
cp 0
rs 6.2318
c 1
b 1
f 0
cc 11
eloc 36
nc 6
nop 1
crap 132

How to fix   Long Method    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
/**
4
 * Konfig.
5
 *
6
 * Yet another simple configuration loader library.
7
 *
8
 * PHP version 5
9
 *
10
 * @category Library
11
 * @package  Konfig
12
 * @author   Xeriab Nabil (aka KodeBurner) <[email protected]>
13
 * @license  https://raw.github.com/xeriab/konfig/master/LICENSE MIT
14
 * @link     https://xeriab.github.io/projects/konfig
15
 */
16
17
namespace Exen\Konfig\FileParser;
18
19
use Exception;
20
use Exen\Konfig\Arr;
21
use Exen\Konfig\Utils;
22
use Exen\Konfig\Exception\ParseException;
23
24
/**
25
 * Konfig's Java-Properties parser class.
26
 *
27
 * @category FileParser
28
 * @package  Konfig
29
 * @author   Xeriab Nabil (aka KodeBurner) <[email protected]>
30
 * @license  https://raw.github.com/xeriab/konfig/master/LICENSE MIT
31
 * @link     https://xeriab.github.io/projects/konfig
32
 *
33
 * @implements Exen\Konfig\FileParser\AbstractFileParser
34
 */
35
class Properties extends AbstractFileParser
36
{
37
    /**
38
     * Parsed configuration file.
39
     *
40
     * @var array $parsedFile
41
     *
42
     * @since 0.2.5
43
     */
44
    protected $parsedFile;
45
46
    /**
47
     * Loads a PROPERTIES file as an array.
48
     *
49
     * @param string $path File path
50
     *
51
     * @throws ParseException If there is an error parsing PROPERTIES file
52
     *
53
     * @return array The parsed data
54
     *
55
     * @since 0.2.4
56
     */
57
    public function parse($path)
58 6
    {
59
        $this->loadFile($path);
60 6
61
        $data = $this->parsedFile;
62 6
63
        unset($this->parsedFile);
64
65 6
        if (!$data || !is_array($data) || is_null($data)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
66 4
            throw new ParseException(
67 3
                [
68
                    'message' => 'Error parsing PROPERTIES file',
69 3
                    'file' => $this->file,
70 2
                ]
71 1
            );
72 1
        }
73
74
        return $data;
75 3
    }
76
77
    /**
78
     * {@inheritdoc}
79
     *
80
     * @return array Supported extensions
81
     *
82
     * @since 0.1.0
83
     */
84
    public function getSupportedFileExtensions()
85 3
    {
86
        return ['properties'];
87 3
    }
88
89
    /**
90
     * Parse Java-Properties
91
     *
92
     * @param string|null $string The string to parse
93
     *
94
     * @return             array The parsed data
95
     * @since              0.2.6
96
     * @codeCoverageIgnore
97
     */
98
    private function parseProperties($string = null)
99
    {
100
        $result = [];
101
        $lines = preg_split('/\n\t|\n/', $string);
102
        $key = '';
103
104
        static $isWaitingForOtherLine = false;
105
106
        foreach ($lines as $k => $line) {
107
            if (empty($line) || (!$isWaitingForOtherLine
108
                && strpos($line, '#') === 0)
109
            ) {
110
                continue;
111
            }
112
113
            if (!strpos($line, '=') && !$isWaitingForOtherLine) {
114
                break;
115
                return false;
0 ignored issues
show
Unused Code introduced by
return false; 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...
116
            }
117
118
            if (!$isWaitingForOtherLine) {
119
                $key = substr($line, 0, strpos($line, '='));
120
                $key = trim($key);
121
                $value = substr($line, strpos($line, '=') + 1, strlen($line));
122
            } else {
123
                $value .= $line;
0 ignored issues
show
Bug introduced by
The variable $value 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...
124
            }
125
126
            // Trim unnecessary white spaces from $value
127
            $value = trim($value);
128
            $value = Utils::trimWhitespace($value);
129
130
            // Remove unnecessary double/single qoutes from $value
131
            $value = Utils::removeQuotes($value);
132
133
            if (strpos($value, '\\') === strlen($value) - strlen('\\')) {
134
                $value = substr($value, 0, strlen($value) - 1);
135
                $isWaitingForOtherLine = true;
136
            } else {
137
                $isWaitingForOtherLine = false;
138
            }
139
140
            $result[$key] = empty($value) ? '' : $value;
141
142
            unset($lines[$k]);
143
        }
144
145
        Utils::unescapeProperties($result);
146
        Utils::trimArrayElements($result);
147
        Utils::stripBackslashes($result);
148
        Utils::fixArrayValues($result);
149
150
        // Fix for dotted properties
151
        $data = [];
152
153
        foreach ($result as $k => $v) {
0 ignored issues
show
Bug introduced by
The expression $result of type null|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
154
            Arr::set($data, $k, $v);
155
        }
156
157
        return $data;
158
    }
159
160
    /**
161
     * Loads in the given file and parses it.
162
     *
163
     * @param string|bool|null $file File to load
164
     *
165
     * @return array The parsed file data
166
     *
167
     * @since              0.2.4
168
     * @codeCoverageIgnore
169
     */
170
    protected function loadFile($file = null)
171
    {
172
        $this->file = is_file($file) ? $file : false;
0 ignored issues
show
Documentation Bug introduced by
It seems like is_file($file) ? $file : false can also be of type boolean. However, the property $file is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
173
174
        $contents = $this->parseVars(Utils::getContent($this->file));
0 ignored issues
show
Bug introduced by
It seems like $this->file can also be of type boolean; however, Exen\Konfig\Utils::getContent() does only seem to accept string|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
175
176
        if ($this->file && !is_null($file)) {
177
            $this->parsedFile = $this->parseProperties($contents);
178
        }
179
    }
180
181
    /**
182
     * Returns the formatted configuration file contents.
183
     *
184
     * @param array $contents configuration array
185
     *
186
     * @return string formatted configuration file contents
187
     *
188
     * @since              0.2.4
189
     * @codeCoverageIgnore
190
     */
191
    protected function exportFormat($contents = null)
192
    {
193
        throw new Exception(
194
            'Saving configuration to `Properties` is not supported at this time'
195
        );
196
    }
197
198
    /**
199
     * __toString.
200
     *
201
     * @return             string
202
     * @since              0.1.2
203
     * @codeCoverageIgnore
204
     */
205
    public function __toString()
206
    {
207
        return 'Exen\Konfig\FileParser\Properties' . PHP_EOL;
208
    }
209
}
210
211
// END OF ./src/FileParser/Properties.php FILE
212