Complex classes like AssetPackage 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 AssetPackage, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
27 | class AssetPackage extends Object |
||
28 | { |
||
29 | protected $_type; |
||
30 | protected $_name; |
||
31 | protected $_hash; |
||
32 | /** |
||
33 | * @var array |
||
34 | */ |
||
35 | protected $_releases = []; |
||
36 | protected $_saved; |
||
37 | /** |
||
38 | * @var AssetVcsRepository|BowerRegistry|NpmRegistry |
||
39 | */ |
||
40 | protected $_registry; |
||
41 | |||
42 | /** |
||
43 | * @var integer UNIX Epoch timestamp of the latest package update |
||
44 | */ |
||
45 | protected $_updateTime; |
||
46 | |||
47 | /** |
||
48 | * @var NullIO |
||
49 | */ |
||
50 | protected $_io; |
||
51 | /** |
||
52 | * @var Composer |
||
53 | */ |
||
54 | protected $_composer; |
||
55 | /** |
||
56 | * @var Composer |
||
57 | */ |
||
58 | protected static $_commonComposer; |
||
59 | |||
60 | public static function normalizeName($name) |
||
61 | { |
||
62 | 1 | return strtolower($name); |
|
63 | } |
||
64 | 1 | ||
65 | public function __construct($type, $name, $config = []) |
||
66 | 1 | { |
|
67 | parent::__construct($config); |
||
68 | |||
69 | 1 | if (!$this->checkType($type)) { |
|
70 | throw new Exception('wrong type'); |
||
71 | } |
||
72 | 1 | if (!$this->checkName($name)) { |
|
73 | 1 | throw new Exception('wrong name'); |
|
74 | 1 | } |
|
75 | $this->_type = $type; |
||
76 | $this->_name = $name; |
||
77 | } |
||
78 | |||
79 | public function getRegistry() |
||
80 | { |
||
81 | if ($this->_registry === null) { |
||
82 | $this->_registry = RegistryFactory::getRegistry($this->getType(), $this->getComposer()->getRepositoryManager()); |
||
83 | } |
||
84 | |||
85 | 1 | return $this->_registry; |
|
86 | } |
||
87 | 1 | ||
88 | public function checkType($type) |
||
89 | { |
||
90 | 1 | return $type === 'bower' || $type === 'npm'; |
|
91 | } |
||
92 | 1 | ||
93 | public function checkName($name) |
||
94 | { |
||
95 | 1 | return strlen($name) > 0; |
|
96 | } |
||
97 | 1 | ||
98 | public function getFullName() |
||
99 | { |
||
100 | 1 | return static::buildFullName($this->_type, $this->_name); |
|
101 | } |
||
102 | 1 | ||
103 | public static function buildFullName($type, $name) |
||
104 | { |
||
105 | return $type . '-asset/' . $name; |
||
106 | } |
||
107 | |||
108 | public static function splitFullName($full) |
||
109 | { |
||
110 | list($temp, $name) = explode('/', $full); |
||
111 | list($type) = explode('-', $temp); |
||
112 | |||
113 | return [$type, $name]; |
||
114 | } |
||
115 | |||
116 | /** |
||
117 | * @param string $full package name |
||
118 | * @return static |
||
119 | */ |
||
120 | public static function fromFullName($full) |
||
121 | { |
||
122 | list($type, $name) = static::splitFullName($full); |
||
123 | return new static($type, $name); |
||
124 | } |
||
125 | |||
126 | public function getType() |
||
127 | { |
||
128 | return $this->_type; |
||
129 | } |
||
130 | |||
131 | public function getNormalName() |
||
132 | { |
||
133 | return static::buildFullName($this->_type, static::normalizeName($this->_name)); |
||
134 | } |
||
135 | |||
136 | public function getName() |
||
137 | { |
||
138 | return $this->_name; |
||
139 | } |
||
140 | |||
141 | public function getHash() |
||
142 | { |
||
143 | return $this->_hash; |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * @return Composer |
||
148 | */ |
||
149 | public static function getCommonComposer() |
||
150 | { |
||
151 | if (static::$_commonComposer === null) { |
||
152 | static::$_commonComposer = (new Factory())->createComposer( |
||
153 | new NullIO(), |
||
154 | Yii::getAlias('@composer/composer.json'), |
||
155 | false, |
||
156 | Yii::getAlias('@composer') |
||
157 | ); |
||
158 | } |
||
159 | |||
160 | return static::$_commonComposer; |
||
161 | } |
||
162 | |||
163 | public function setComposer($value) |
||
164 | { |
||
165 | $this->_composer = $value; |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * @return Composer |
||
170 | */ |
||
171 | public function getComposer() |
||
172 | { |
||
173 | if ($this->_composer === null) { |
||
174 | $this->_composer = static::getCommonComposer(); |
||
175 | } |
||
176 | |||
177 | return $this->_composer; |
||
178 | } |
||
179 | |||
180 | public function getIO() |
||
181 | { |
||
182 | if ($this->_io === null) { |
||
183 | $this->_io = new NullIO(); |
||
184 | } |
||
185 | |||
186 | return $this->_io; |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * findOne. |
||
191 | * |
||
192 | * @param string $type |
||
193 | * @param string $name |
||
194 | * @return static|null |
||
195 | */ |
||
196 | public static function findOne($type, $name) |
||
197 | { |
||
198 | $package = new static($type, $name); |
||
199 | $package->load(); |
||
200 | |||
201 | return $package; |
||
202 | } |
||
203 | |||
204 | public function load() |
||
205 | { |
||
206 | $data = $this->getStorage()->readPackage($this); |
||
207 | if ($data !== null) { |
||
208 | $this->_hash = $data['hash']; |
||
209 | $this->_releases = $data['releases']; |
||
210 | $this->_updateTime = $data['updateTime']; |
||
211 | } |
||
212 | } |
||
213 | |||
214 | public function update() |
||
215 | { |
||
216 | $repo = $this->getRegistry()->buildVcsRepository($this->getName()); |
||
217 | $this->_releases = $this->prepareReleases($repo); |
||
218 | $this->getStorage()->writePackage($this); |
||
219 | $this->load(); |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * @param AssetVcsRepository $repository |
||
224 | * @return array |
||
225 | */ |
||
226 | public function prepareReleases($repository) |
||
227 | { |
||
228 | $releases = []; |
||
229 | |||
230 | foreach ($repository->getPackages() as $package) { |
||
231 | $version = $package->getVersion(); |
||
232 | $require = $this->prepareRequire($package->getRequires()); |
||
233 | $release = [ |
||
234 | 'uid' => $this->prepareUid($version), |
||
235 | 'name' => $this->getNormalName(), |
||
236 | 'version' => $version, |
||
237 | 'type' => $this->getType() . '-asset, |
||
238 | ]; |
||
239 | if ($require) { |
||
240 | $release['require'] = $require; |
||
241 | } |
||
242 | if ($package->getDistUrl()) { |
||
243 | $release['dist'] = [ |
||
244 | 'type' => $package->getDistType(), |
||
245 | 'url' => $package->getDistUrl(), |
||
246 | 'reference' => $package->getDistReference(), |
||
247 | ]; |
||
248 | } |
||
249 | if ($package->getSourceUrl()) { |
||
250 | $release['source'] = [ |
||
251 | 'type' => $package->getSourceType(), |
||
252 | 'url' => $package->getSourceUrl(), |
||
253 | 'reference' => $package->getSourceReference(), |
||
254 | ]; |
||
255 | } |
||
256 | if ((isset($release['dist']) && $release['dist']) || (isset($release['source']) && $release['source'])) { |
||
257 | $releases[$version] = $release; |
||
258 | } |
||
259 | } |
||
260 | |||
261 | return $releases; |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * Prepares array of requires: name => constraint. |
||
266 | * @param Link[] array of package requires. |
||
267 | * @return array |
||
268 | */ |
||
269 | public function prepareRequire(array $links) |
||
270 | { |
||
271 | $requires = []; |
||
272 | foreach ($links as $name => $link) { |
||
273 | /** @var Link $link */ |
||
274 | $requires[$name] = $link->getPrettyConstraint(); |
||
275 | } |
||
276 | |||
277 | return $requires; |
||
278 | } |
||
279 | |||
280 | public function prepareUid($version) |
||
281 | { |
||
282 | $known = $this->getSaved()->getRelease($version); |
||
283 | |||
284 | return isset($known['uid']) ? $known['uid'] : $this->getStorage()->getNextId(); |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * @return array |
||
289 | */ |
||
290 | public function getReleases() |
||
291 | { |
||
292 | return $this->_releases; |
||
293 | } |
||
294 | |||
295 | /** |
||
296 | * @param $version |
||
297 | * @return array |
||
298 | */ |
||
299 | public function getRelease($version) |
||
300 | { |
||
301 | return isset($this->_releases[$version]) ? $this->_releases[$version] : []; |
||
302 | } |
||
303 | |||
304 | public function getSaved() |
||
305 | { |
||
306 | if ($this->_saved === null) { |
||
307 | $this->_saved = static::findOne($this->getType(), $this->getName()); |
||
308 | } |
||
309 | |||
310 | return $this->_saved; |
||
311 | } |
||
312 | |||
313 | /** |
||
314 | * @return Storage |
||
315 | */ |
||
316 | public function getStorage() |
||
317 | { |
||
318 | return Yii::$app->get('packageStorage'); |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Returns the latest update time (UNIX Epoch). |
||
323 | * @return int|null |
||
324 | */ |
||
325 | public function getUpdateTime() |
||
326 | { |
||
327 | return $this->_updateTime; |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * Package can be updated not more often than once in 10 min. |
||
332 | * @return bool |
||
333 | */ |
||
334 | public function canBeUpdated() |
||
335 | { |
||
336 | return time() - $this->getUpdateTime() > 60 * 10; // 10 min |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * Whether tha package should be auth-updated (if it is older than 1 day). |
||
341 | * @return bool |
||
342 | */ |
||
343 | public function canAutoUpdate() |
||
344 | { |
||
345 | return time() - $this->getUpdateTime() > 60 * 60 * 24; // 1 day |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * @return array |
||
350 | */ |
||
351 | public function __sleep() |
||
352 | { |
||
353 | return ['_type', '_name', '_hash']; |
||
354 | } |
||
355 | } |
||
356 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.