Complex classes like Collection 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 Collection, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
31 | class Collection extends AbstractNode implements IQuota |
||
32 | { |
||
33 | /** |
||
34 | * Root folder. |
||
35 | */ |
||
36 | const ROOT_FOLDER = '/'; |
||
37 | |||
38 | /** |
||
39 | * Share acl. |
||
40 | * |
||
41 | * @var array |
||
42 | */ |
||
43 | protected $acl = []; |
||
44 | |||
45 | /** |
||
46 | * Share name. |
||
47 | * |
||
48 | * @var string |
||
49 | */ |
||
50 | protected $share_name; |
||
51 | |||
52 | /** |
||
53 | * filter. |
||
54 | * |
||
55 | * @var string |
||
56 | */ |
||
57 | protected $filter; |
||
58 | |||
59 | /** |
||
60 | * Storage. |
||
61 | * |
||
62 | * @var StorageAdapterInterface |
||
63 | */ |
||
64 | protected $_storage; |
||
65 | |||
66 | /** |
||
67 | * Initialize. |
||
68 | */ |
||
69 | public function __construct(array $attributes, Filesystem $fs, LoggerInterface $logger, Hook $hook, Acl $acl, ?Collection $parent, StorageAdapterInterface $storage) |
||
70 | { |
||
71 | $this->_fs = $fs; |
||
72 | $this->_server = $fs->getServer(); |
||
73 | $this->_db = $fs->getDatabase(); |
||
74 | $this->_user = $fs->getUser(); |
||
75 | $this->_logger = $logger; |
||
76 | $this->_hook = $hook; |
||
77 | $this->_acl = $acl; |
||
78 | $this->_storage = $storage; |
||
79 | $this->_parent = $parent; |
||
80 | |||
81 | foreach ($attributes as $attr => $value) { |
||
82 | $this->{$attr} = $value; |
||
83 | } |
||
84 | |||
85 | $this->mime = 'inode/directory'; |
||
86 | $this->raw_attributes = $attributes; |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * Set storage adapter. |
||
91 | */ |
||
92 | public function setStorage(StorageAdapterInterface $adapter): self |
||
93 | { |
||
94 | $this->_storage = $adapter; |
||
95 | |||
96 | return $this; |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * Get storage adapter. |
||
101 | */ |
||
102 | public function getStorage(): StorageAdapterInterface |
||
103 | { |
||
104 | return $this->_storage; |
||
105 | } |
||
106 | |||
107 | /** |
||
108 | * Copy node with children. |
||
109 | */ |
||
110 | public function copyTo(self $parent, int $conflict = NodeInterface::CONFLICT_NOACTION, ?string $recursion = null, bool $recursion_first = true, int $deleted = NodeInterface::DELETED_EXCLUDE): NodeInterface |
||
111 | { |
||
112 | if (null === $recursion) { |
||
113 | $recursion_first = true; |
||
114 | $recursion = uniqid(); |
||
115 | } else { |
||
116 | $recursion_first = false; |
||
117 | } |
||
118 | |||
119 | $this->_hook->run( |
||
120 | 'preCopyCollection', |
||
121 | [$this, $parent, &$conflict, &$recursion, &$recursion_first] |
||
122 | ); |
||
123 | |||
124 | if (NodeInterface::CONFLICT_RENAME === $conflict && $parent->childExists($this->name)) { |
||
125 | $name = $this->getDuplicateName(); |
||
126 | } else { |
||
127 | $name = $this->name; |
||
128 | } |
||
129 | |||
130 | if ($this->_id === $parent->getId()) { |
||
131 | throw new Exception\Conflict( |
||
132 | 'can not copy node into itself', |
||
133 | Exception\Conflict::CANT_COPY_INTO_ITSELF |
||
134 | ); |
||
135 | } |
||
136 | |||
137 | if (NodeInterface::CONFLICT_MERGE === $conflict && $parent->childExists($this->name)) { |
||
138 | $new_parent = $parent->getChild($this->name); |
||
139 | |||
140 | if ($new_parent instanceof File) { |
||
141 | $new_parent = $this; |
||
142 | } |
||
143 | } else { |
||
144 | $new_parent = $parent->addDirectory($name, [ |
||
145 | 'created' => $this->created, |
||
146 | 'changed' => $this->changed, |
||
147 | 'filter' => $this->filter, |
||
148 | 'meta' => $this->meta, |
||
149 | ], NodeInterface::CONFLICT_NOACTION, true); |
||
150 | } |
||
151 | |||
152 | foreach ($this->getChildNodes($deleted) as $child) { |
||
153 | $child->copyTo($new_parent, $conflict, $recursion, false, $deleted); |
||
154 | } |
||
155 | |||
156 | $this->_hook->run( |
||
157 | 'postCopyCollection', |
||
158 | [$this, $parent, $new_parent, $conflict, $recursion, $recursion_first] |
||
159 | ); |
||
160 | |||
161 | return $new_parent; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Is mount. |
||
166 | */ |
||
167 | public function isMounted(): bool |
||
168 | { |
||
169 | return count($this->mount) > 0; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Get Share name. |
||
174 | */ |
||
175 | public function getShareName(): string |
||
176 | { |
||
177 | if ($this->isShare()) { |
||
178 | return $this->share_name; |
||
179 | } |
||
180 | |||
181 | return $this->_fs->findRawNode($this->getShareId())['share_name']; |
||
|
|||
182 | } |
||
183 | |||
184 | /** |
||
185 | * Get Attributes. |
||
186 | */ |
||
187 | public function getAttributes(): array |
||
188 | { |
||
189 | return [ |
||
190 | '_id' => $this->_id, |
||
191 | 'name' => $this->name, |
||
192 | 'shared' => $this->shared, |
||
193 | 'share_name' => $this->share_name, |
||
194 | 'acl' => $this->acl, |
||
195 | 'directory' => true, |
||
196 | 'reference' => $this->reference, |
||
197 | 'parent' => $this->parent, |
||
198 | 'app' => $this->app, |
||
199 | 'owner' => $this->owner, |
||
200 | 'meta' => $this->meta, |
||
201 | 'mime' => $this->mime, |
||
202 | 'filter' => $this->filter, |
||
203 | 'deleted' => $this->deleted, |
||
204 | 'changed' => $this->changed, |
||
205 | 'created' => $this->created, |
||
206 | 'destroy' => $this->destroy, |
||
207 | 'readonly' => $this->readonly, |
||
208 | 'mount' => $this->mount, |
||
209 | 'storage_reference' => $this->storage_reference, |
||
210 | 'storage' => $this->storage, |
||
211 | ]; |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * Set collection filter. |
||
216 | */ |
||
217 | public function setFilter(?array $filter = null): bool |
||
223 | |||
224 | /** |
||
225 | * Get collection. |
||
226 | */ |
||
227 | public function get(): void |
||
231 | |||
232 | /** |
||
233 | * Fetch children items of this collection. |
||
234 | * |
||
235 | * Deleted: |
||
236 | * 0 - Exclude deleted |
||
237 | * 1 - Only deleted |
||
238 | * 2 - Include deleted |
||
239 | * |
||
240 | * @param int $offset |
||
241 | * @param int $limit |
||
242 | */ |
||
243 | public function getChildNodes(int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = [], ?int $offset = null, ?int $limit = null): Generator |
||
249 | |||
250 | /** |
||
251 | * Fetch children items of this collection (as array). |
||
252 | * |
||
253 | * Deleted: |
||
254 | * 0 - Exclude deleted |
||
255 | * 1 - Only deleted |
||
256 | * 2 - Include deleted |
||
257 | */ |
||
258 | public function getChildren(int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): array |
||
262 | |||
263 | /** |
||
264 | * Is custom filter node. |
||
265 | */ |
||
266 | public function isFiltered(): bool |
||
270 | |||
271 | /** |
||
272 | * Get number of children. |
||
273 | */ |
||
274 | public function getSize(): int |
||
278 | |||
279 | /** |
||
280 | * Get real id (reference). |
||
281 | * |
||
282 | * @return ObjectId |
||
283 | */ |
||
284 | public function getRealId(): ?ObjectId |
||
292 | |||
293 | /** |
||
294 | * Get user quota information. |
||
295 | */ |
||
296 | public function getQuotaInfo(): array |
||
305 | |||
306 | /** |
||
307 | * Fetch children items of this collection. |
||
308 | */ |
||
309 | public function getChild($name, int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): NodeInterface |
||
329 | |||
330 | /** |
||
331 | * Delete node. |
||
332 | * |
||
333 | * Actually the node will not be deleted (Just set a delete flag), set $force=true to |
||
334 | * delete finally |
||
335 | */ |
||
336 | public function delete(bool $force = false, ?string $recursion = null, bool $recursion_first = true): bool |
||
385 | |||
386 | /** |
||
387 | * Check if this collection has child named $name. |
||
388 | * |
||
389 | * deleted: |
||
390 | * |
||
391 | * 0 - Exclude deleted |
||
392 | * 1 - Only deleted |
||
393 | * 2 - Include deleted |
||
394 | * |
||
395 | * @param string $name |
||
396 | * @param int $deleted |
||
397 | */ |
||
398 | public function childExists($name, $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): bool |
||
432 | |||
433 | /** |
||
434 | * Share collection. |
||
435 | */ |
||
436 | public function share(array $acl, string $name): bool |
||
486 | |||
487 | /** |
||
488 | * Unshare collection. |
||
489 | */ |
||
490 | public function unshare(): bool |
||
491 | { |
||
492 | if (!$this->_acl->isAllowed($this, 'm')) { |
||
493 | throw new ForbiddenException( |
||
528 | |||
529 | /** |
||
530 | * Get children. |
||
531 | */ |
||
532 | public function getChildrenRecursive(?ObjectId $id = null, ?array &$shares = []): array |
||
560 | |||
561 | /** |
||
562 | * Create new directory. |
||
563 | */ |
||
564 | public function addDirectory($name, array $attributes = [], int $conflict = NodeInterface::CONFLICT_NOACTION, bool $clone = false): self |
||
656 | |||
657 | /** |
||
658 | * Create new file as a child from this collection. |
||
659 | */ |
||
660 | public function addFile($name, ?ObjectId $session = null, array $attributes = [], int $conflict = NodeInterface::CONFLICT_NOACTION, bool $clone = false): File |
||
755 | |||
756 | /** |
||
757 | * Create new file wrapper |
||
758 | * (Sabe\DAV compatible method, elsewhere use addFile(). |
||
759 | * |
||
760 | * Sabre\DAV requires that createFile() returns the ETag instead the newly created file instance |
||
761 | * |
||
762 | * @param string $name |
||
763 | * @param string $data |
||
764 | */ |
||
765 | public function createFile($name, $data = null): string |
||
778 | |||
779 | /** |
||
780 | * Create new directory wrapper |
||
781 | * (Sabe\DAV compatible method, elsewhere use addDirectory(). |
||
782 | * |
||
783 | * Sabre\DAV requires that createDirectory() returns void |
||
784 | * |
||
785 | * @param string $name |
||
786 | */ |
||
787 | public function createDirectory($name): void |
||
791 | |||
792 | /** |
||
793 | * Do recursive Action. |
||
794 | */ |
||
795 | public function doRecursiveAction(callable $callable, int $deleted = NodeInterface::DELETED_EXCLUDE): bool |
||
805 | |||
806 | /** |
||
807 | * Validate filtered collection query. |
||
808 | */ |
||
809 | protected function validateFilter(string $filter): bool |
||
821 | |||
822 | /** |
||
823 | * Validate acl. |
||
824 | */ |
||
825 | protected function validateAcl(array $acl): bool |
||
842 | |||
843 | /** |
||
844 | * Get children query filter. |
||
845 | * |
||
846 | * Deleted: |
||
847 | * 0 - Exclude deleted |
||
848 | * 1 - Only deleted |
||
849 | * 2 - Include deleted |
||
850 | */ |
||
851 | protected function getChildrenFilter(int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): array |
||
913 | |||
914 | /** |
||
915 | * Completely remove node. |
||
916 | */ |
||
917 | protected function _forceDelete(?string $recursion = null, bool $recursion_first = true): bool |
||
952 | } |
||
953 |
This check looks at variables that 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.