Complex classes like PassFactory 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 PassFactory, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
30 | class PassFactory |
||
31 | { |
||
32 | /** |
||
33 | * Output path for generated pass files |
||
34 | * |
||
35 | * @var string |
||
36 | */ |
||
37 | protected $outputPath = ''; |
||
38 | |||
39 | /** |
||
40 | * Overwrite if pass exists |
||
41 | * |
||
42 | * @var bool |
||
43 | */ |
||
44 | protected $overwrite = false; |
||
45 | |||
46 | /** |
||
47 | * Pass type identifier |
||
48 | * |
||
49 | * @var string |
||
50 | */ |
||
51 | protected $passTypeIdentifier; |
||
52 | |||
53 | /** |
||
54 | * Team identifier |
||
55 | * |
||
56 | * @var string |
||
57 | */ |
||
58 | protected $teamIdentifier; |
||
59 | |||
60 | /** |
||
61 | * Organization name |
||
62 | * |
||
63 | * @var string |
||
64 | */ |
||
65 | protected $organizationName; |
||
66 | |||
67 | /** |
||
68 | * P12 file |
||
69 | * |
||
70 | * @var \Passbook\Certificate\P12Interface |
||
71 | */ |
||
72 | protected $p12; |
||
73 | |||
74 | /** |
||
75 | * WWDR file |
||
76 | * |
||
77 | * @var \Passbook\Certificate\WWDRInterface |
||
78 | */ |
||
79 | protected $wwdr; |
||
80 | |||
81 | /** |
||
82 | * @var bool - skip signing the pass; should only be used for testing |
||
83 | */ |
||
84 | protected $skipSignature; |
||
85 | |||
86 | /** |
||
87 | * Pass file extension |
||
88 | * |
||
89 | * @var string |
||
90 | */ |
||
91 | const PASS_EXTENSION = '.pkpass'; |
||
92 | |||
93 | public function __construct($passTypeIdentifier, $teamIdentifier, $organizationName, $p12File, $p12Pass, $wwdrFile) |
||
103 | |||
104 | /** |
||
105 | * Set outputPath |
||
106 | * |
||
107 | * @param string |
||
108 | * |
||
109 | * @return $this |
||
110 | */ |
||
111 | public function setOutputPath($outputPath) |
||
117 | |||
118 | /** |
||
119 | * Get outputPath |
||
120 | * |
||
121 | * @return string |
||
122 | */ |
||
123 | public function getOutputPath() |
||
127 | |||
128 | /** |
||
129 | * Set overwrite |
||
130 | * |
||
131 | * @param boolean |
||
132 | * |
||
133 | * @return $this |
||
134 | */ |
||
135 | public function setOverwrite($overwrite) |
||
141 | |||
142 | /** |
||
143 | * Get overwrite |
||
144 | * |
||
145 | * @return boolean |
||
146 | */ |
||
147 | public function isOverwrite() |
||
151 | |||
152 | /** |
||
153 | * Set skip signature |
||
154 | * |
||
155 | * When set, the pass will not be signed when packaged. This should only |
||
156 | * be used for testing. |
||
157 | * |
||
158 | * @param boolean |
||
159 | * @return $this |
||
160 | */ |
||
161 | public function setSkipSignature($skipSignature) |
||
167 | |||
168 | /** |
||
169 | * Get overwrite |
||
170 | * @return boolean |
||
171 | */ |
||
172 | public function getSkipSignature() |
||
176 | |||
177 | /** |
||
178 | * Serialize pass |
||
179 | * |
||
180 | * @param PassInterface $pass |
||
181 | * |
||
182 | * @return string |
||
183 | */ |
||
184 | public static function serialize(PassInterface $pass) |
||
188 | |||
189 | /** |
||
190 | * Creates a pkpass file |
||
191 | * |
||
192 | * @param PassInterface $pass |
||
193 | * |
||
194 | * @throws FileException If an IO error occurred |
||
195 | * @return SplFileObject |
||
196 | */ |
||
197 | public function package(PassInterface $pass, $passName = '') |
||
198 | { |
||
199 | if ($pass->getSerialNumber() == '') { |
||
200 | throw new \InvalidArgumentException('Pass must have a serial number to be packaged'); |
||
201 | } |
||
202 | |||
203 | $this->populateRequiredInformation($pass); |
||
204 | |||
205 | // Serialize pass |
||
206 | $json = self::serialize($pass); |
||
207 | |||
208 | $outputPath = rtrim($this->getOutputPath(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
||
209 | $passDir = $outputPath . $this->getPassName($passName, $pass) . DIRECTORY_SEPARATOR; |
||
210 | $passDirExists = file_exists($passDir); |
||
211 | if ($passDirExists && !$this->isOverwrite()) { |
||
212 | throw new FileException("Temporary pass directory already exists"); |
||
213 | } elseif (!$passDirExists && !mkdir($passDir, 0777, true)) { |
||
214 | throw new FileException("Couldn't create temporary pass directory"); |
||
215 | } |
||
216 | |||
217 | // Pass.json |
||
218 | $passJSONFile = $passDir . 'pass.json'; |
||
219 | file_put_contents($passJSONFile, $json); |
||
220 | |||
221 | // Images |
||
222 | /** @var Image $image */ |
||
223 | foreach ($pass->getImages() as $image) { |
||
224 | $fileName = $passDir . $image->getContext(); |
||
225 | if ($image->isRetina()) { |
||
226 | $fileName .= '@2x'; |
||
227 | } |
||
228 | $fileName .= '.' . $image->getExtension(); |
||
229 | copy($image->getPathname(), $fileName); |
||
230 | } |
||
231 | |||
232 | // Localizations |
||
233 | foreach ($pass->getLocalizations() as $localization) { |
||
234 | // Create dir (LANGUAGE.lproj) |
||
235 | $localizationDir = $passDir . $localization->getLanguage() . '.lproj' . DIRECTORY_SEPARATOR; |
||
236 | mkdir($localizationDir, 0777, true); |
||
237 | |||
238 | // pass.strings File (Format: "token" = "value") |
||
239 | $localizationStringsFile = $localizationDir . 'pass.strings'; |
||
240 | file_put_contents($localizationStringsFile, $localization->getStringsFileOutput()); |
||
241 | |||
242 | // Localization images |
||
243 | foreach ($localization->getImages() as $image) { |
||
244 | $fileName = $localizationDir . $image->getContext(); |
||
245 | if ($image->isRetina()) { |
||
246 | $fileName .= '@2x'; |
||
247 | } |
||
248 | $fileName .= '.' . $image->getExtension(); |
||
249 | copy($image->getPathname(), $fileName); |
||
250 | } |
||
251 | } |
||
252 | |||
253 | // Manifest.json - recursive, also add files in sub directories |
||
254 | $manifestJSONFile = $passDir . 'manifest.json'; |
||
255 | $manifest = array(); |
||
256 | $files = new RecursiveIteratorIterator( |
||
257 | new RecursiveDirectoryIterator($passDir), |
||
258 | RecursiveIteratorIterator::SELF_FIRST |
||
259 | ); |
||
260 | foreach ($files as $file) { |
||
261 | // Ignore "." and ".." folders |
||
262 | if (in_array(substr($file, strrpos($file, '/') + 1), array('.', '..'))) { |
||
263 | continue; |
||
264 | } |
||
265 | // |
||
266 | $filePath = realpath($file); |
||
267 | if (is_file($filePath) === true) { |
||
268 | $relativePathName = str_replace($passDir, '', $file->getPathname()); |
||
269 | $manifest[$relativePathName] = sha1_file($filePath); |
||
270 | } |
||
271 | } |
||
272 | file_put_contents($manifestJSONFile, $this->jsonEncode($manifest)); |
||
273 | |||
274 | // Signature |
||
275 | $this->sign($passDir, $manifestJSONFile); |
||
276 | |||
277 | // Zip pass |
||
278 | $zipFile = $outputPath . $this->getPassName($passName, $pass) . self::PASS_EXTENSION; |
||
279 | $this->zip($passDir, $zipFile); |
||
280 | |||
281 | // Remove temporary pass directory |
||
282 | $this->rrmdir($passDir); |
||
283 | |||
284 | return new SplFileObject($zipFile); |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * @param $passDir |
||
289 | * @param $manifestJSONFile |
||
290 | */ |
||
291 | private function sign($passDir, $manifestJSONFile) |
||
333 | |||
334 | /** |
||
335 | * Creates a zip of a directory including all sub directories (recursive) |
||
336 | * |
||
337 | * @param $source - path to the source directory |
||
338 | * @param $destination - output directory |
||
339 | * |
||
340 | * @return bool |
||
341 | * @throws Exception |
||
342 | */ |
||
343 | private function zip($source, $destination) |
||
374 | |||
375 | /** |
||
376 | * Recursive folder remove |
||
377 | * |
||
378 | * @param string $dir |
||
379 | * |
||
380 | * @return bool |
||
381 | */ |
||
382 | private function rrmdir($dir) |
||
391 | |||
392 | /** |
||
393 | * @param PassInterface $pass |
||
394 | */ |
||
395 | private function populateRequiredInformation(PassInterface $pass) |
||
409 | |||
410 | /** |
||
411 | * @param $array |
||
412 | * |
||
413 | * @return string |
||
414 | */ |
||
415 | private static function jsonEncode($array) |
||
421 | |||
422 | /** |
||
423 | * @param $passName |
||
424 | * @param PassInterface $pass |
||
425 | * |
||
426 | * @return string |
||
427 | */ |
||
428 | public function getPassName($passName, PassInterface $pass) |
||
433 | |||
434 | } |
||
435 |
This check examines a number of code elements and verifies that they conform to the given naming conventions.
You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.