Completed
Push — master ( 3e6b61...e3478f )
by Alexander
03:04
created

AbstractChecker::isParamSignatureCompatible()   D

Complexity

Conditions 9
Paths 37

Size

Total Lines 39
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 39
ccs 21
cts 21
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 19
nc 37
nop 2
crap 9
1
<?php
2
/**
3
 * This file is part of the Code-Insight library.
4
 * For the full copyright and license information, please view
5
 * the LICENSE file that was distributed with this source code.
6
 *
7
 * @copyright Alexander Obuhovich <[email protected]>
8
 * @link      https://github.com/console-helpers/code-insight
9
 */
10
11
namespace ConsoleHelpers\CodeInsight\BackwardsCompatibility\Checker;
12
13
14
use Aura\Sql\ExtendedPdoInterface;
15
use Doctrine\Common\Cache\CacheProvider;
16
17
abstract class AbstractChecker
18
{
19
20
	/**
21
	 * Source database.
22
	 *
23
	 * @var ExtendedPdoInterface
24
	 */
25
	protected $sourceDatabase;
26
27
	/**
28
	 * Target database.
29
	 *
30
	 * @var ExtendedPdoInterface
31
	 */
32
	protected $targetDatabase;
33
34
	/**
35
	 * Cache.
36
	 *
37
	 * @var CacheProvider
38
	 */
39
	protected $cache;
40
41
	/**
42
	 * Incidents.
43
	 *
44
	 * @var array
45
	 */
46
	private $_incidents = array();
47
48
	/**
49
	 * AbstractChecker constructor.
50
	 *
51
	 * @param CacheProvider $cache Cache provider.
52
	 */
53 10
	public function __construct(CacheProvider $cache)
54
	{
55 10
		$this->cache = $cache;
56 10
	}
57
58
	/**
59
	 * Returns backwards compatibility checker name.
60
	 *
61
	 * @return string
62
	 */
63
	abstract public function getName();
64
65
	/**
66
	 * Checks backwards compatibility and returns violations by category.
67
	 *
68
	 * @param ExtendedPdoInterface $source_db Source DB.
69
	 * @param ExtendedPdoInterface $target_db Target DB.
70
	 *
71
	 * @return array
72
	 */
73 6
	public function check(ExtendedPdoInterface $source_db, ExtendedPdoInterface $target_db)
74
	{
75 6
		$this->sourceDatabase = $source_db;
76 6
		$this->targetDatabase = $target_db;
77
78 6
		$this->doCheck();
79
80 6
		return array_filter($this->_incidents);
81
	}
82
83
	/**
84
	 * Collects backwards compatibility violations.
85
	 *
86
	 * @return void
87
	 */
88
	abstract protected function doCheck();
89
90
	/**
91
	 * Builds string representation of a parameter.
92
	 *
93
	 * @param array $parameter_data Parameter data.
94
	 *
95
	 * @return string
96
	 */
97 4
	protected function paramToString(array $parameter_data)
98
	{
99 4
		if ( $parameter_data['HasType'] ) {
100 2
			$type = $parameter_data['TypeName'];
101 2
		}
102 4
		elseif ( $parameter_data['IsArray'] ) {
103
			$type = 'array';
104
		}
105 4
		elseif ( $parameter_data['IsCallable'] ) {
106
			$type = 'callable';
107
		}
108
		else {
109 4
			$type = $parameter_data['TypeClass'];
110
		}
111
112 4
		$hash_part = strlen($type) ? $type . ' ' : '';
113
114 4
		if ( $parameter_data['IsPassedByReference'] ) {
115 2
			$hash_part .= '&$' . $parameter_data['Name'];
116 2
		}
117
		else {
118 4
			$hash_part .= '$' . $parameter_data['Name'];
119
		}
120
121 4
		if ( $parameter_data['HasDefaultValue'] ) {
122 3
			$hash_part .= ' = ';
123
124 3
			if ( $parameter_data['DefaultConstant'] ) {
125
				$hash_part .= $parameter_data['DefaultConstant'];
126
			}
127
			else {
128 3
				$hash_part .= $this->decodeValue($parameter_data['DefaultValue']);
129
			}
130 3
		}
131
132 4
		return $hash_part;
133
	}
134
135
	/**
136
	 * Determines if 2 param signatures are compatible.
137
	 *
138
	 * @param string $source_signature Source signature.
139
	 * @param string $target_signature Target signature.
140
	 *
141
	 * @return boolean
142
	 */
143 4
	protected function isParamSignatureCompatible($source_signature, $target_signature)
144
	{
145 4
		if ( $source_signature === $target_signature ) {
146 4
			return true;
147
		}
148
149 3
		$source_params = $source_signature ? explode(', ', $source_signature) : array();
150 3
		$target_params = $target_signature ? explode(', ', $target_signature) : array();
151 3
		$source_param_count = count($source_params);
152
153
		// Beginning of target signature doesn't match source signature.
154 3
		if ( $source_params !== array_slice($target_params, 0, $source_param_count) ) {
155 3
			$target_params_wo_defaults = preg_replace('/ = [^,]*/', '', $target_params);
156
157
			// Making existing parameter optional doesn't break compatibility.
158 3
			if ( $source_params !== array_slice($target_params_wo_defaults, 0, $source_param_count) ) {
159 3
				return false;
160
			}
161 3
		}
162
163 3
		$added_params = array_slice($target_params, $source_param_count);
164
165
		// No new parameters added.
166 3
		if ( !$added_params ) {
167 3
			return true;
168
		}
169
170 3
		$is_compatible = true;
171
172
		// When all added parameters are optional, then signature is compatible.
173 3
		foreach ( $added_params as $added_param ) {
174 3
			if ( strpos($added_param, '=') === false ) {
175 3
				$is_compatible = false;
176 3
				break;
177
			}
178 3
		}
179
180 3
		return $is_compatible;
181
	}
182
183
	/**
184
	 * Decodes json-encoded PHP value.
185
	 *
186
	 * @param string $json_string JSON string.
187
	 *
188
	 * @return string
189
	 */
190 3
	protected function decodeValue($json_string)
191
	{
192 3
		$value = var_export(json_decode($json_string), true);
193 3
		$value = str_replace(array("\t", "\n"), '', $value);
194 3
		$value = str_replace('array (', 'array(', $value);
195
196 3
		return $value;
197
	}
198
199
	/**
200
	 * Adds incident.
201
	 *
202
	 * @param string      $type      Incident type.
203
	 * @param string      $element   Element affected.
204
	 * @param string|null $old_value Old value.
205
	 * @param string|null $new_value New value.
206
	 *
207
	 * @return void
208
	 */
209 4
	protected function addIncident($type, $element, $old_value = null, $new_value = null)
210
	{
211
		$incident_record = array(
212 4
			'type' => $type,
213 4
			'element' => $element,
214 4
		);
215
216 4
		if ( isset($old_value) || isset($new_value) ) {
217 3
			$incident_record['old'] = $old_value;
218 3
			$incident_record['new'] = $new_value;
219 3
		}
220
221 4
		$this->_incidents[] = $incident_record;
222 4
	}
223
224
	/**
225
	 * Returns cache key valid for specific database only.
226
	 *
227
	 * @param ExtendedPdoInterface $db        Database.
228
	 * @param string               $cache_key Cache key.
229
	 *
230
	 * @return string
231
	 */
232 2
	protected function getCacheKey(ExtendedPdoInterface $db, $cache_key)
233
	{
234 2
		return sha1($db->getDsn()) . ':' . $cache_key;
235
	}
236
237
}
238