Complex classes like Connector 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 Connector, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
31 | class Connector implements ConnectorInterface |
||
32 | { |
||
33 | |||
34 | /** |
||
35 | * The official service URI; can be overridden via the constructor |
||
36 | * |
||
37 | * @var string |
||
38 | */ |
||
39 | const SERVICE_PRODUCTION_URL = 'https://api.communibase.nl/0.1/'; |
||
40 | |||
41 | /** |
||
42 | * The API key which is to be used for the api. |
||
43 | * Is required to be set via the constructor. |
||
44 | * |
||
45 | * @var string |
||
46 | */ |
||
47 | private $apiKey; |
||
48 | |||
49 | /** |
||
50 | * The url which is to be used for this connector. Defaults to the production url. |
||
51 | * Can be set via the constructor. |
||
52 | * |
||
53 | * @var string |
||
54 | */ |
||
55 | private $serviceUrl; |
||
56 | |||
57 | /** |
||
58 | * @var array of extra headers to send with each request |
||
59 | */ |
||
60 | private $extraHeaders = []; |
||
61 | |||
62 | /** |
||
63 | * @var QueryLogger |
||
64 | */ |
||
65 | private $logger; |
||
66 | |||
67 | /** |
||
68 | * @var ClientInterface |
||
69 | */ |
||
70 | private $client; |
||
71 | |||
72 | /** |
||
73 | * Create a new Communibase Connector instance based on the given api-key and possible serviceUrl |
||
74 | * |
||
75 | * @param string $apiKey The API key for Communibase |
||
76 | * @param string $serviceUrl The Communibase API endpoint; defaults to self::SERVICE_PRODUCTION_URL |
||
77 | * @param ClientInterface $client An optional GuzzleHttp Client (or Interface for mocking) |
||
78 | */ |
||
79 | public function __construct( |
||
88 | |||
89 | /** |
||
90 | * Returns an array that has all the fields according to the definition in Communibase. |
||
91 | * |
||
92 | * @param string $entityType |
||
93 | * |
||
94 | * @return Promise of result |
||
95 | * |
||
96 | * @throws Exception |
||
97 | */ |
||
98 | public function getTemplate($entityType) |
||
109 | |||
110 | /** |
||
111 | * Get a single Entity by its id |
||
112 | * |
||
113 | * @param string $entityType |
||
114 | * @param string $id |
||
115 | * @param array $params (optional) |
||
116 | * @param string|null $version |
||
117 | * |
||
118 | * @return Promise of result |
||
119 | * |
||
120 | * @return array entity |
||
121 | * |
||
122 | * @throws Exception |
||
123 | */ |
||
124 | public function getById($entityType, $id, array $params = [], $version = null) |
||
137 | |||
138 | /** |
||
139 | * NOTE not yet async |
||
140 | * |
||
141 | * Get a single Entity by a ref-string |
||
142 | * |
||
143 | * @param string $ref |
||
144 | * @param array $parentEntity (optional) |
||
145 | * |
||
146 | * @return array the referred Entity data |
||
147 | * |
||
148 | * @throws Exception |
||
149 | */ |
||
150 | public function getByRef($ref, array $parentEntity = []) |
||
168 | |||
169 | /** |
||
170 | * Get an array of entities by their ids |
||
171 | * |
||
172 | * @param string $entityType |
||
173 | * @param array $ids |
||
174 | * @param array $params (optional) |
||
175 | * |
||
176 | * @return Promise of result |
||
177 | */ |
||
178 | public function getByIds($entityType, array $ids, array $params = []) |
||
202 | |||
203 | /** |
||
204 | * Get all entities of a certain type |
||
205 | * |
||
206 | * @param string $entityType |
||
207 | * @param array $params (optional) |
||
208 | * |
||
209 | * @return Promise of result |
||
210 | */ |
||
211 | public function getAll($entityType, array $params = []) |
||
215 | |||
216 | /** |
||
217 | * Get result entityIds of a certain search |
||
218 | * |
||
219 | * @param string $entityType |
||
220 | * @param array $selector (optional) |
||
221 | * @param array $params (optional) |
||
222 | * |
||
223 | * @return Promise of result |
||
224 | */ |
||
225 | public function getIds($entityType, array $selector = [], array $params = []) |
||
233 | |||
234 | /** |
||
235 | * Get the id of an entity based on a search |
||
236 | * |
||
237 | * @param string $entityType i.e. Person |
||
238 | * @param array $selector (optional) i.e. ['firstName' => 'Henk'] |
||
239 | * |
||
240 | * @return Promise of result |
||
241 | */ |
||
242 | public function getId($entityType, array $selector = []) |
||
249 | |||
250 | /** |
||
251 | * Returns an array of the history for the entity with the following format: |
||
252 | * |
||
253 | * <code> |
||
254 | * [ |
||
255 | * [ |
||
256 | * 'updatedBy' => '', // name of the user |
||
257 | * 'updatedAt' => '', // a string according to the DateTime::ISO8601 format |
||
258 | * '_id' => '', // the ID of the entity which can ge fetched seperately |
||
259 | * ], |
||
260 | * ... |
||
261 | * ] |
||
262 | * </code> |
||
263 | * |
||
264 | * @param string $entityType |
||
265 | * @param string $id |
||
266 | * |
||
267 | * @return Promise of result |
||
268 | * |
||
269 | * @throws Exception |
||
270 | */ |
||
271 | public function getHistory($entityType, $id) |
||
275 | |||
276 | /** |
||
277 | * Search for the given entity by optional passed selector/params |
||
278 | * |
||
279 | * @param string $entityType |
||
280 | * @param array $querySelector |
||
281 | * @param array $params (optional) |
||
282 | * |
||
283 | * @return Promise of result |
||
284 | * |
||
285 | * @throws Exception |
||
286 | */ |
||
287 | public function search($entityType, array $querySelector, array $params = []) |
||
291 | |||
292 | /** |
||
293 | * This will save an entity in Communibase. When a _id-field is found, this entity will be updated |
||
294 | * |
||
295 | * NOTE: When updating, depending on the Entity, you may need to include all fields. |
||
296 | * |
||
297 | * @param string $entityType |
||
298 | * @param array $properties - the to-be-saved entity data |
||
299 | * |
||
300 | * @returns Promise of result |
||
301 | * |
||
302 | * @throws Exception |
||
303 | */ |
||
304 | public function update($entityType, array $properties) |
||
314 | |||
315 | /** |
||
316 | * Finalize an invoice by adding an invoiceNumber to it. |
||
317 | * Besides, invoice items will receive a "generalLedgerAccountNumber". |
||
318 | * This number will be unique and sequential within the "daybook" of the invoice. |
||
319 | * |
||
320 | * NOTE: this is Invoice specific |
||
321 | * |
||
322 | * @param string $entityType |
||
323 | * @param string $id |
||
324 | * |
||
325 | * @return Promise of result |
||
326 | * |
||
327 | * @throws Exception |
||
328 | */ |
||
329 | public function finalize($entityType, $id) |
||
337 | |||
338 | /** |
||
339 | * Delete something from Communibase |
||
340 | * |
||
341 | * @param string $entityType |
||
342 | * @param string $id |
||
343 | * |
||
344 | * @return Promise of result |
||
345 | */ |
||
346 | public function destroy($entityType, $id) |
||
350 | |||
351 | /** |
||
352 | * Get the binary contents of a file by its ID |
||
353 | * |
||
354 | * NOTE: for meta-data like filesize and mimetype, one can use the getById()-method. |
||
355 | * |
||
356 | * @param string $id id string for the file-entity |
||
357 | * |
||
358 | * @return StreamInterface Binary contents of the file. Since the stream can be made a string this works like a charm! |
||
359 | * |
||
360 | * @throws Exception |
||
361 | */ |
||
362 | public function getBinary($id) |
||
372 | |||
373 | /** |
||
374 | * Uploads the contents of the resource (this could be a file handle) to Communibase |
||
375 | * |
||
376 | * @param StreamInterface $resource |
||
377 | * @param string $name |
||
378 | * @param string $destinationPath |
||
379 | * @param string $id |
||
380 | * |
||
381 | * @return array|mixed |
||
382 | * @throws Exception |
||
383 | */ |
||
384 | public function updateBinary(StreamInterface $resource, $name, $destinationPath, $id = '') |
||
416 | |||
417 | /** |
||
418 | * MAGIC for making sync requests |
||
419 | * |
||
420 | * @param string $name |
||
421 | * @param array $arguments |
||
422 | * |
||
423 | * @return mixed |
||
424 | */ |
||
425 | public function __call($name, $arguments) |
||
437 | |||
438 | /** |
||
439 | * @param string $path |
||
440 | * @param array $params |
||
441 | * @param array $data |
||
442 | * |
||
443 | * @return Promise |
||
444 | * |
||
445 | * @throws Exception |
||
446 | */ |
||
447 | protected function doGet($path, array $params = null, array $data = null) |
||
451 | |||
452 | /** |
||
453 | * @param string $path |
||
454 | * @param array $params |
||
455 | * @param array $data |
||
456 | * |
||
457 | * @return Promise |
||
458 | * |
||
459 | * @throws Exception |
||
460 | */ |
||
461 | protected function doPost($path, array $params = null, array $data = null) |
||
465 | |||
466 | /** |
||
467 | * @param string $path |
||
468 | * @param array $params |
||
469 | * @param array $data |
||
470 | * |
||
471 | * @return Promise |
||
472 | * |
||
473 | * @throws Exception |
||
474 | */ |
||
475 | protected function doPut($path, array $params = null, array $data = null) |
||
479 | |||
480 | /** |
||
481 | * @param string $path |
||
482 | * @param array $params |
||
483 | * @param array $data |
||
484 | * |
||
485 | * @return Promise |
||
486 | * |
||
487 | * @throws Exception |
||
488 | */ |
||
489 | protected function doDelete($path, array $params = null, array $data = null) |
||
493 | |||
494 | /** |
||
495 | * Process the request |
||
496 | * |
||
497 | * @param string $method |
||
498 | * @param string $path |
||
499 | * @param array $params |
||
500 | * @param array $data |
||
501 | * |
||
502 | * @return Promise array i.e. [success => true|false, [errors => ['message' => 'this is broken', ..]]] |
||
503 | * |
||
504 | * @throws Exception |
||
505 | */ |
||
506 | protected function getResult($method, $path, array $params = null, array $data = null) |
||
545 | |||
546 | /** |
||
547 | * @param array $params |
||
548 | * |
||
549 | * @return mixed |
||
550 | */ |
||
551 | private function preParseParams(array $params) |
||
576 | |||
577 | /** |
||
578 | * Parse the Communibase result and if necessary throw an exception |
||
579 | * |
||
580 | * @param string $response |
||
581 | * @param int $httpCode |
||
582 | * |
||
583 | * @return array |
||
584 | * |
||
585 | * @throws Exception |
||
586 | */ |
||
587 | private function parseResult($response, $httpCode) |
||
597 | |||
598 | /** |
||
599 | * Error message based on the most recent JSON error. |
||
600 | * |
||
601 | * @see http://nl1.php.net/manual/en/function.json-last-error.php |
||
602 | * |
||
603 | * @return string |
||
604 | */ |
||
605 | private function getLastJsonError() |
||
618 | |||
619 | /** |
||
620 | * @param string $id |
||
621 | * |
||
622 | * @return bool |
||
623 | */ |
||
624 | public static function isIdValid($id) |
||
636 | |||
637 | /** |
||
638 | * Generate a Communibase compatible ID, that consists of: |
||
639 | * |
||
640 | * a 4-byte timestamp, |
||
641 | * a 3-byte machine identifier, |
||
642 | * a 2-byte process id, and |
||
643 | * a 3-byte counter, starting with a random value. |
||
644 | * |
||
645 | * @return string |
||
646 | */ |
||
647 | public static function generateId() |
||
664 | |||
665 | /** |
||
666 | * Add extra headers to be added to each request |
||
667 | * |
||
668 | * @see http://php.net/manual/en/function.header.php |
||
669 | * |
||
670 | * @param array $extraHeaders |
||
671 | */ |
||
672 | public function addExtraHeaders(array $extraHeaders) |
||
676 | |||
677 | /** |
||
678 | * @param QueryLogger $logger |
||
679 | */ |
||
680 | public function setQueryLogger(QueryLogger $logger) |
||
684 | |||
685 | /** |
||
686 | * @return QueryLogger |
||
687 | */ |
||
688 | public function getQueryLogger() |
||
692 | |||
693 | /** |
||
694 | * @return \GuzzleHttp\Client |
||
695 | * @throws Exception |
||
696 | */ |
||
697 | protected function getClient() |
||
717 | |||
718 | /** |
||
719 | * @param string $method |
||
720 | * @param array $arguments |
||
721 | * |
||
722 | * @return Promise |
||
723 | * |
||
724 | * @throws Exception |
||
725 | */ |
||
726 | private function call($method, array $arguments) |
||
748 | |||
749 | } |
||
750 |
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.