Complex classes like Filtrator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Filtrator, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
5 | class Filtrator implements FiltratorInterface |
||
6 | { |
||
7 | // selector to specify that the filter is applied to the entire data set |
||
8 | const SELECTOR_ROOT = '/'; |
||
9 | |||
10 | // selector to specify that the filter is applied to all ITEMS of a set |
||
11 | const SELECTOR_ANY = '*'; |
||
12 | |||
13 | protected $filterFactory; |
||
14 | |||
15 | /** |
||
16 | * The list of filters available in the filtrator |
||
17 | * |
||
18 | * @var array |
||
19 | */ |
||
20 | protected $filters = []; |
||
21 | |||
22 | /** |
||
23 | * @var array |
||
24 | */ |
||
25 | protected $allowedSelectors = []; |
||
26 | |||
27 | /** |
||
28 | * @var array |
||
29 | */ |
||
30 | protected $compiledAllowedSelectors = []; |
||
31 | |||
32 | 20 | public function __construct(FilterFactory $filterFactory = null) |
|
39 | |||
40 | 1 | public function setAllowed(array $allowedSelectors = []) |
|
44 | |||
45 | /** |
||
46 | * Add a filter to the filters stack |
||
47 | * |
||
48 | * @example // normal callback |
||
49 | * $filtrator->add('title', '\strip_tags'); |
||
50 | * // anonymous function |
||
51 | * $filtrator->add('title', function($value){ return $value . '!!!'; }); |
||
52 | * // filter class from the library registered on the $filtersMap |
||
53 | * $filtrator->add('title', 'normalizedate', ['format' => 'm/d/Y']); |
||
54 | * // custom class |
||
55 | * $filtrator->add('title', '\MyApp\Filters\CustomFilter'); |
||
56 | * // multiple filters as once with different ways to pass options |
||
57 | * $filtrator->add('title', [ |
||
58 | * ['truncate', 'limit=10', true, 10], |
||
59 | * ['censor', ['words' => ['idiot']] |
||
60 | * ]); |
||
61 | * // multiple fitlers as a single string |
||
62 | * $filtrator->add('title', 'stringtrim(side=left)(true)(10) | truncate(limit=100)'); |
||
63 | * @param string|array $selector |
||
64 | * @param mixed $callbackOrFilterName |
||
65 | * @param array|null $options |
||
66 | * @param bool $recursive |
||
67 | * @param integer $priority |
||
68 | * @throws \InvalidArgumentException |
||
69 | * @internal param $ callable|filter class name|\Sirius\Filtration\Filter\AbstractFilter $callbackOrFilterName |
||
70 | * @internal param array|string $params |
||
71 | * @return self |
||
72 | */ |
||
73 | 20 | public function add($selector, $callbackOrFilterName = null, $options = null, $recursive = false, $priority = 0) |
|
137 | |||
138 | /** |
||
139 | * Converts a rule that was supplied as string into a set of options that define the rule |
||
140 | * |
||
141 | * @example 'minLength({"min":2})(true)(10)' |
||
142 | * |
||
143 | * will be converted into |
||
144 | * |
||
145 | * [ |
||
146 | * 'minLength', // validator name |
||
147 | * ['min' => 2'], // validator options |
||
148 | * true, // recursive |
||
149 | * 10 // priority |
||
150 | * ] |
||
151 | * @param string $ruleAsString |
||
152 | * @return array |
||
153 | */ |
||
154 | 2 | protected function parseRule($ruleAsString) |
|
186 | |||
187 | /** |
||
188 | * Remove a filter from the stack |
||
189 | * |
||
190 | * @param string $selector |
||
191 | * @param bool|callable|string|TRUE $callbackOrName |
||
192 | * @throws \InvalidArgumentException |
||
193 | * @return \Sirius\Filtration\Filtrator |
||
194 | */ |
||
195 | 3 | public function remove($selector, $callbackOrName = true) |
|
215 | |||
216 | /** |
||
217 | * Retrieve all filters stack |
||
218 | * |
||
219 | * @return array |
||
220 | */ |
||
221 | 1 | public function getFilters() |
|
225 | |||
226 | /** |
||
227 | * Apply filters to an array |
||
228 | * |
||
229 | * @param array $data |
||
230 | * @return array |
||
231 | */ |
||
232 | 15 | public function filter(array $data = []) |
|
251 | |||
252 | /** |
||
253 | * Apply filters on a single item in the array |
||
254 | * |
||
255 | * @param array $data |
||
256 | * @param string $valueIdentifier |
||
257 | * @return mixed |
||
258 | */ |
||
259 | 15 | public function filterItem($data, $valueIdentifier) |
|
274 | |||
275 | /** |
||
276 | * Apply filters to a single value |
||
277 | * |
||
278 | * @param mixed $value |
||
279 | * value of the item |
||
280 | * @param string $valueIdentifier |
||
281 | * array element path (eg: 'key' or 'key[0][subkey]') |
||
282 | * @param mixed $context |
||
283 | * @return mixed |
||
284 | */ |
||
285 | 15 | public function applyFilters($value, $valueIdentifier, $context) |
|
295 | |||
296 | 15 | private function itemIsAllowed($item) |
|
308 | |||
309 | 15 | private function compileAllowedSelectors() |
|
338 | } |
||
339 |
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.