Completed
Push — master ( 4c4714...034a3c )
by Arnold
9s
created

DotKey::getValue()   C

Complexity

Conditions 11
Paths 6

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 22
rs 5.9012
cc 11
eloc 11
nc 6
nop 4

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
namespace Jasny;
4
5
/**
6
 * Access objects and arrays through dot notation
7
 */
8
class DotKey
9
{
10
    /**
11
     * @var object|array
12
     */
13
    protected $item;
14
    
15
    /**
16
     * Create new structure as array (when using put)
17
     * @var boolean
18
     */
19
    public $assoc;
20
    
21
    /**
22
     * Class constructor
23
     * 
24
     * @param object|array $item
25
     * @param boolean      $assoc  Create new structure as array (when using put)
26
     */
27
    public function __construct($item, $assoc = null)
28
    {
29
        $this->item = $item;
30
        $this->assoc = isset($assoc) ? $assoc : is_array($item);
31
    }
32
33
    
34
    /**
35
     * Check if property exists
36
     * 
37
     * @param string $key The index to fetch in dot notation
38
     * @return boolean
39
     */
40
    public function exists($key)
41
    {
42
        $index = explode('.', $key);
43
        self::getValue($this->item, $index, false, $err);
0 ignored issues
show
Bug introduced by
It seems like $this->item can also be of type object; however, Jasny\DotKey::getValue() does only seem to accept array, 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...
44
        
45
        return !$err;
46
    }
47
    
48
    /**
49
     * Get a value
50
     * 
51
     * @param string $key The index to fetch in dot notation
52
     * @return mixed
53
     */
54 View Code Duplication
    public function get($key)
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...
55
    {
56
        $index = explode('.', $key);
57
        $ret = self::getValue($this->item, $index, true, $err);
0 ignored issues
show
Bug introduced by
It seems like $this->item can also be of type object; however, Jasny\DotKey::getValue() does only seem to accept array, 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...
Documentation introduced by
true is of type boolean, but the function expects a false|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
58
        
59
        if ($err) {
60
            $invalidPath = join('.', array_slice($index, 0, -1 * $err->incomplete));
61
            trigger_error("Unable to get '$key': '$invalidPath' is a {$err->var}", E_USER_WARNING);
62
        } // @codeCoverageIgnore
63
        
64
        return $ret;
65
    }
66
    
67
    /**
68
     * Navigate through the item and get the value
69
     * 
70
     * @param array  $item
71
     * @param array  $index   The index sequence we are navigating to
72
     * @param array  $ignore  Don't raise an error if not exists
73
     * @param object $err     Error object [OUTPUT]
74
     * @return mixed
75
     */
76
    protected static function getValue($item, $index, $ignore = false, &$err = null)
77
    {
78
        $err = null;
79
        
80
        if (empty($index)) return $item;
81
        
82
        $key = array_shift($index);
83
        
84
        if ((is_array($item) || $item instanceof \Traversable) && isset($item[$key])) {
85
            return static::getValue($item[$key], $index, $ignore, $err);
86
        }
87
        
88
        if (is_object($item) && isset($item->$key)) {
89
            return static::getValue($item->$key, $index, $ignore, $err);
90
        }
91
        
92
        if ((!is_object($item) && !is_array($item)) || !$ignore) {
93
            $err = (object)['var' => isset($item) ? gettype($item) : null, 'incomplete' => count($index) + 1];
94
        }
95
        
96
        return null;
97
    }
98
    
99
    
100
    /**
101
     * Set a value
102
     * 
103
     * @param string $key    The index to fetch in dot notation
104
     * @param mixed  $value
105
     * @return object|array
106
     */
107
    public function set($key, $value)
108
    {
109
        $index = explode('.', $key);
110
        self::setValue($this->item, $index, $value, false, $err);
0 ignored issues
show
Bug introduced by
It seems like $this->item can also be of type object; however, Jasny\DotKey::setValue() does only seem to accept array, 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...
111
        
112
        if ($err) {
113
            $invalidPath = join('.', array_slice($index, 0, -1 * $err->incomplete));
114
            $reason = isset($err->var) ? "'$invalidPath' is a {$err->var}" : "'$invalidPath' doesn't exist";
115
            trigger_error("Unable to set '$key': $reason", E_USER_WARNING);
116
        } // @codeCoverageIgnore
117
        
118
        return $this->item;
119
    }
120
    
121
    /**
122
     * Set a value, creating the structure if required
123
     * 
124
     * @param string  $key    The index to fetch in dot notation
125
     * @param mixed   $value
126
     * @return object|array
127
     */
128
    public function put($key, $value)
129
    {
130
        $index = explode('.', $key);
131
        $err = null;
132
        
133
        self::setValue($this->item, $index, $value, $this->assoc ? 'array' : 'object', $err);
0 ignored issues
show
Bug introduced by
It seems like $this->item can also be of type object; however, Jasny\DotKey::setValue() does only seem to accept array, 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...
134
        
135
        if ($err) {
136
            $invalidPath = join('.', array_slice($index, 0, -1 * $err->incomplete));
137
            trigger_error("Unable to put '$key': '$invalidPath' is a {$err->var}", E_USER_WARNING);
138
        } // @codeCoverageIgnore
139
        
140
        return $this->item;
141
    }
142
    
143
    /**
144
     * Navigate through the item and set the value
145
     * 
146
     * @param array        $item
147
     * @param array        $index   The index sequence we are navigating to
148
     * @param mixed        $value
149
     * @param string|false $create  Create structure if required: 'object', 'array' or false
150
     * @param object       $err     Error object [OUTPUT]
151
     */
152
    protected static function setValue(&$item, $index, $value, $create = false, &$err = null)
153
    {
154
        $err = null;
155
156
        $key = array_shift($index);
157
        
158
        if (is_array($item) || $item instanceof \Traversable) {
159
            if (empty($index)) {
160
                $item[$key] = $value;
161
                return;
162
            }
163
            
164 View Code Duplication
            if (!isset($item[$key]) && $create) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $create of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
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...
165
                $item[$key] = $create === 'array' ? [] : (object)[];
166
            }
167
            
168
            if (isset($item[$key])) {
169
                return static::setValue($item[$key], $index, $value, $create, $err);
170
            }
171
        } elseif (is_object($item)) {
172
            if (empty($index)) {
173
                $item->$key = $value;
174
                return;
175
            }
176
            
177 View Code Duplication
            if (!isset($item->$key) && $create) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $create of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
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...
178
                $item->$key = $create === 'array' ? [] : (object)[];
179
            }
180
            
181
            if (isset($item->$key)) {
182
                return static::setValue($item->$key, $index, $value, $create, $err);
183
            }
184
        } else {
185
            $err = (object)['var' => gettype($item), 'incomplete' => count($index) + 1];
186
            return;
187
        }
188
        
189
        $err = (object)['var' => null, 'incomplete' => count($index)];
190
    }
191
192
    
193
    /**
194
     * Get a particular value back from the config array
195
     * 
196
     * @param string $key The index to fetch in dot notation
197
     * @return object|array
198
     */
199 View Code Duplication
    public function remove($key)
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...
200
    {
201
        $index = explode('.', $key);
202
        self::removeValue($this->item, $index, $err);
0 ignored issues
show
Bug introduced by
It seems like $this->item can also be of type object; however, Jasny\DotKey::removeValue() does only seem to accept array, 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...
203
        
204
        if ($err) {
205
            $invalidPath = join('.', array_slice($index, 0, -1 * $err->incomplete));
206
            trigger_error("Unable to remove '$key': '$invalidPath' is a {$err->var}", E_USER_WARNING);
207
        } // @codeCoverageIgnore
208
        
209
        return $this->item;
210
    }
211
    
212
    /**
213
     * Navigate through the item and remove the value
214
     * 
215
     * @param array  $item
216
     * @param array  $index  The index sequence we are navigating to
217
     * @param object $err    Error object [OUTPUT]
218
     * @return mixed
219
     */
220
    protected static function removeValue(&$item, $index, &$err = null)
221
    {
222
        $err = null;
223
224
        if (!is_object($item) && !is_array($item)) {
225
            $err = (object)['var' => gettype($item), 'incomplete' => count($index)];
226
            return;
227
        }
228
        
229
        $key = array_shift($index);
230
        
231
        if (empty($index)) {
232
            if (is_object($item) && isset($item->$key)) unset($item->$key);
233
            if (is_array($item) && isset($item[$key])) unset($item[$key]);
234
            return;
235
        }
236
        
237 View Code Duplication
        if (is_object($item) && isset($item->$key)) return static::removeValue($item->$key, $index, $err);
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...
238 View Code Duplication
        if (is_array($item) && isset($item[$key])) return static::removeValue($item[$key], $index, $err);
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...
239
    }
240
241
    
242
    /**
243
     * Factory method
244
     * 
245
     * @param object|array $item
246
     * @param boolean      $assoc  Create new structure as array (when using put)
247
     */
248
    public static function on($item, $assoc = null)
249
    {
250
        return new static($item, $assoc);
251
    }
252
}
253