Configurable::__set()   C
last analyzed

Complexity

Conditions 13
Paths 13

Size

Total Lines 78
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 13

Importance

Changes 0
Metric Value
eloc 31
c 0
b 0
f 0
dl 0
loc 78
ccs 31
cts 31
cp 1
rs 6.6166
cc 13
nc 13
nop 2
crap 13

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
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) The s9e authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Configurator\Traits;
9
10
use InvalidArgumentException;
11
use RuntimeException;
12
use s9e\TextFormatter\Configurator\Collections\Collection;
13
use s9e\TextFormatter\Configurator\Collections\NormalizedCollection;
14
use Traversable;
15
16
/**
17
* Provides magic __get, __set, __isset and __unset implementations
18
*/
19
trait Configurable
20
{
21
	/**
22
	* Magic getter
23
	*
24
	* Will return $this->foo if it exists, then $this->getFoo() or will throw an exception if
25
	* neither exists
26
	*
27
	* @param  string $propName
28
	* @return mixed
29
	*/
30 42
	public function __get($propName)
31
	{
32 42
		$methodName = 'get' . ucfirst($propName);
33
34
		// Look for a getter, e.g. getDefaultTemplate()
35 42
		if (method_exists($this, $methodName))
36
		{
37 9
			return $this->$methodName();
38
		}
39
40 34
		if (!property_exists($this, $propName))
41
		{
42 1
			throw new RuntimeException("Property '" . $propName . "' does not exist");
43
		}
44
45 33
		return $this->$propName;
46
	}
47
48
	/**
49
	* Magic setter
50
	*
51
	* Will call $this->setFoo($propValue) if it exists, otherwise it will set $this->foo.
52
	* If $this->foo is a NormalizedCollection, we do not replace it, instead we clear() it then
53
	* fill it back up. It will not overwrite an object with a different incompatible object (of a
54
	* different, non-extending class) and it will throw an exception if the PHP type cannot match
55
	* without incurring data loss.
56
	*
57
	* @param  string $propName
58
	* @param  mixed  $propValue
59
	* @return void
60
	*/
61 43
	public function __set($propName, $propValue)
62
	{
63 43
		$methodName = 'set' . ucfirst($propName);
64
65
		// Look for a setter, e.g. setDefaultChildRule()
66 43
		if (method_exists($this, $methodName))
67
		{
68 28
			$this->$methodName($propValue);
69
70 24
			return;
71
		}
72
73
		// If the property isn't already set, we just create/set it
74 17
		if (!isset($this->$propName))
75
		{
76 4
			$this->$propName = $propValue;
77
78 4
			return;
79
		}
80
81
		// If we're trying to replace a NormalizedCollection, instead we clear it then
82
		// iteratively set new values
83 13
		if ($this->$propName instanceof NormalizedCollection)
84
		{
85 3
			if (!is_array($propValue) && !($propValue instanceof Traversable))
86
			{
87 1
				throw new InvalidArgumentException("Property '" . $propName . "' expects an array or a traversable object to be passed");
88
			}
89
90 2
			$this->$propName->clear();
91 2
			foreach ($propValue as $k => $v)
92
			{
93 2
				$this->$propName->set($k, $v);
94
			}
95
96 2
			return;
97
		}
98
99
		// If this property is an object, test whether they are compatible. Otherwise, test if PHP
100
		// types are compatible
101 10
		if (is_object($this->$propName))
102
		{
103 3
			if (!($propValue instanceof $this->$propName))
104
			{
105 3
				throw new InvalidArgumentException("Cannot replace property '" . $propName . "' of class '" . get_class($this->$propName) . "' with instance of '" . get_class($propValue) . "'");
106
			}
107
		}
108
		else
109
		{
110
			// Test whether the PHP types are compatible
111 7
			$oldType = gettype($this->$propName);
112 7
			$newType = gettype($propValue);
113
114
			// If the property is a boolean, we'll accept "true" and "false" as strings
115 7
			if ($oldType === 'boolean' && preg_match('(^(?:fals|tru)e$)', $propValue))
116
			{
117 2
				$newType   = 'boolean';
118 2
				$propValue = ($propValue === 'true');
119
			}
120
121 7
			if ($oldType !== $newType)
122
			{
123
				// Test whether the PHP type roundtrip is lossless
124 2
				$tmp = $propValue;
125 2
				settype($tmp, $oldType);
126 2
				settype($tmp, $newType);
127
128 2
				if ($tmp !== $propValue)
129
				{
130 1
					throw new InvalidArgumentException("Cannot replace property '" . $propName . "' of type " . $oldType . ' with value of type ' . $newType);
131
				}
132
133
				// Finally, set the new value to the correct type
134 1
				settype($propValue, $oldType);
135
			}
136
		}
137
138 8
		$this->$propName = $propValue;
139
	}
140
141
	/**
142
	* Test whether a property is set
143
	*
144
	* @param  string $propName
145
	* @return bool
146
	*/
147 10
	public function __isset($propName)
148
	{
149 10
		$methodName = 'isset' . ucfirst($propName);
150 10
		if (method_exists($this, $methodName))
151
		{
152 6
			return $this->$methodName();
153
		}
154
155 4
		return isset($this->$propName);
156
	}
157
158
	/**
159
	* Unset a property, if the class supports it
160
	*
161
	* @param  string $propName
162
	* @return void
163
	*/
164 5
	public function __unset($propName)
165
	{
166 5
		$methodName = 'unset' . ucfirst($propName);
167 5
		if (method_exists($this, $methodName))
168
		{
169 3
			$this->$methodName();
170
		}
171 2
		elseif (isset($this->$propName))
172
		{
173 2
			if ($this->$propName instanceof Collection)
174
			{
175 1
				$this->$propName->clear();
176
			}
177
			else
178
			{
179 1
				throw new RuntimeException("Property '" . $propName . "' cannot be unset");
180
			}
181
		}
182
	}
183
}