Complex classes like Module 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 Module, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 31 | class Module implements PdfInterface, ResolverInterface, ServiceManagerAwareInterface |
||
| 32 | { |
||
| 33 | const RENDER_FULL = 0; |
||
| 34 | const RENDER_WITHOUT_PDF = 1; |
||
| 35 | const RENDER_WITHOUT_ATTACHMENTS = 2; |
||
| 36 | |||
| 37 | protected $serviceManager; |
||
| 38 | |||
| 39 | protected $viewResolverAttached = false; |
||
| 40 | |||
| 41 | protected $appendPDF = array(); |
||
| 42 | protected $appendImage = array(); |
||
| 43 | |||
| 44 | |||
| 45 | /** |
||
| 46 | * Loads module specific configuration. |
||
| 47 | * |
||
| 48 | * @return array |
||
| 49 | */ |
||
| 50 | public function getConfig() |
||
| 51 | { |
||
| 52 | return ModuleConfigLoader::load(__DIR__ . '/config'); |
||
| 53 | } |
||
| 54 | |||
| 55 | public function setServiceManager(ServiceManager $serviceManager) |
||
| 56 | { |
||
| 57 | $this->serviceManager = $serviceManager; |
||
| 58 | return $this; |
||
| 59 | } |
||
| 60 | |||
| 61 | public function onBootstrap(MvcEvent $e) |
||
| 62 | { |
||
| 63 | $eventManager = $e->getApplication()->getEventManager(); |
||
| 64 | $eventManager->getSharedManager()->attach( |
||
| 65 | 'Applications', |
||
| 66 | 'application.detail.actionbuttons', |
||
| 67 | function ($event) { |
||
| 68 | return 'pdf/application/details/button'; |
||
| 69 | } |
||
| 70 | ); |
||
| 71 | } |
||
| 72 | |||
| 73 | /** |
||
| 74 | * hook into the rendering for transformation of HTML to PDF |
||
| 75 | * @param \Zend\EventManager\EventManagerInterface $events |
||
| 76 | */ |
||
| 77 | public function attach(EventManagerInterface $events) |
||
| 78 | { |
||
| 79 | $events->attach(ViewEvent::EVENT_RENDERER_POST, array($this, 'cleanLayout'), 1); |
||
| 80 | $events->attach(ViewEvent::EVENT_RESPONSE, array($this, 'attachPDFtransformer'), 10); |
||
| 81 | } |
||
| 82 | |||
| 83 | /** |
||
| 84 | * hook into the MVC |
||
| 85 | * in here you could still decide, if you want to hook into the Rendering |
||
| 86 | * @param \Zend\EventManager\EventManagerInterface $events |
||
| 87 | */ |
||
| 88 | public function attachMvc(EventManagerInterface $events) |
||
| 89 | { |
||
| 90 | $events->attach(MvcEvent::EVENT_RENDER, array($this, 'initializeViewHelper'), 100); |
||
| 91 | } |
||
| 92 | |||
| 93 | /** |
||
| 94 | * hook into the Rendering of files |
||
| 95 | * the manager to hook in is the viewhelper 'insertfiles' |
||
| 96 | * |
||
| 97 | * @param \Zend\Mvc\MvcEvent $e |
||
| 98 | */ |
||
| 99 | public function initializeViewHelper(MvcEvent $e) |
||
| 100 | { |
||
| 101 | $viewhelperManager = $this->serviceManager->get('ViewhelperManager'); |
||
| 102 | if ($viewhelperManager->has('insertFile')) { |
||
| 103 | $insertFile = $viewhelperManager->get('insertFile'); |
||
| 104 | $insertFile->attach(FileEvent::GETFILE, array($this, 'getFile')); |
||
| 105 | $insertFile->attach(FileEvent::RENDERFILE, array($this, 'renderFile')); |
||
| 106 | $insertFile->attach(FileEvent::INSERTFILE, array($this, 'collectFiles')); |
||
| 107 | } |
||
| 108 | } |
||
| 109 | |||
| 110 | /** |
||
| 111 | * proxy, in case that you just got a name and have to find the associated file-entity |
||
| 112 | * maybe this is redundant and can be deprecated |
||
| 113 | * |
||
| 114 | * @param \Core\View\Helper\InsertFile\FileEvent $e |
||
| 115 | * @return null |
||
| 116 | */ |
||
| 117 | public function getFile(FileEvent $e) |
||
| 118 | { |
||
| 119 | $lastFileName = $e->getLastFileName(); |
||
| 120 | if (is_string($lastFileName)) { |
||
| 121 | $repository = $this->serviceManager->get('repositories')->get('Applications/Attachment'); |
||
| 122 | $file = $repository->find($lastFileName); |
||
| 123 | if (isset($file)) { |
||
| 124 | $e->setFileObject($lastFileName, $file); |
||
| 125 | $e->stopPropagation(); |
||
| 126 | return $file; |
||
| 127 | } |
||
| 128 | return null; |
||
| 129 | } |
||
| 130 | // if it is not a string i do presume it is already a file-Object |
||
| 131 | return $lastFileName; |
||
| 132 | } |
||
| 133 | |||
| 134 | /** |
||
| 135 | * here the inserted File is rendered, |
||
| 136 | * there is a lot which still can be done like outsorcing the HTML to a template, |
||
| 137 | * or distinguish between different File Types, |
||
| 138 | * at the moment we assume the $file is always an (sub-)instance of \Core\File\Entity |
||
| 139 | * |
||
| 140 | * @param \Core\View\Helper\InsertFile\FileEvent $e |
||
| 141 | * @return string |
||
| 142 | */ |
||
| 143 | public function renderFile(FileEvent $e) |
||
| 144 | { |
||
| 145 | $file = $e->getLastFileObject(); |
||
| 146 | // assume it is of the class Core\Entity\FileEntity |
||
| 147 | $return = '<div class="col-md-3"><a href="#attachment_' . $file->getId() . '">' . $file->getName() . '</a></div>' . PHP_EOL |
||
| 148 | . '<div class="col-md-3">' . $file->getType() . '</div>' |
||
| 149 | . '<div class="col-md-3">' . $file->prettySize . '</div>'; |
||
| 150 | /* |
||
| 151 | * this snippet was for direct inserting an image into the PDF |
||
| 152 | if ($file && $file instanceOf FileEntity && 0 === strpos($file->getType(), 'image')) { |
||
| 153 | //$content = $file->getContent(); |
||
| 154 | //$url = 'data:image/' . $file->getType() . ';base64,' . base64_encode ($content); |
||
| 155 | //$html = '<img src="' . $url . '" >'; |
||
| 156 | $html = '<a href="#1">' . $file->getName() . '</a>'; |
||
| 157 | $e->stopPropagation(); |
||
| 158 | return $html; |
||
| 159 | } |
||
| 160 | */ |
||
| 161 | return $return; |
||
| 162 | } |
||
| 163 | |||
| 164 | /** |
||
| 165 | * give a summary of all inserted Files, |
||
| 166 | * this is for having access to those files in the post-process |
||
| 167 | * @param \Core\View\Helper\InsertFile\FileEvent|\Zend\View\ViewEvent $e |
||
| 168 | * @return NULL |
||
| 169 | */ |
||
| 170 | public function collectFiles(FileEvent $e) |
||
| 171 | { |
||
| 172 | $this->appendPDF = array(); |
||
| 173 | $files = $e->getAllFiles(); |
||
| 174 | foreach ($files as $name => $file) { |
||
| 175 | if (!empty($file) && $file instanceof FileEntity) { |
||
| 176 | if (0 === strpos($file->getType(), 'image')) { |
||
| 177 | $this->appendImage[] = $file; |
||
| 178 | } |
||
| 179 | if (strtolower($file->getType()) == 'application/pdf') { |
||
| 180 | $this->appendPDF[] = $file; |
||
| 181 | } |
||
| 182 | } |
||
| 183 | } |
||
| 184 | return null; |
||
| 185 | } |
||
| 186 | |||
| 187 | /** |
||
| 188 | * remove unwanted or layout related data |
||
| 189 | * |
||
| 190 | * basically you rake through the viewmodel for the data you want to use for your template, |
||
| 191 | * this may not be optimal because you have to rely on the correct naming of the viewmodels |
||
| 192 | * |
||
| 193 | * if you get the data you want, you switch to the specific template by adding the conforming resolver |
||
| 194 | * |
||
| 195 | * @param \Zend\View\ViewEvent $e |
||
| 196 | */ |
||
| 197 | public function cleanLayout(ViewEvent $e) |
||
| 198 | { |
||
| 199 | $result = $e->getResult(); |
||
| 200 | $response = $e->getResponse(); |
||
| 201 | $model = $e->getModel(); |
||
| 202 | if ($model->hasChildren()) { |
||
| 203 | $children = $model->getChildren(); |
||
| 204 | $content = null; |
||
| 205 | foreach ($children as $child) { |
||
| 206 | if ($child->captureTo() == 'content') { |
||
| 207 | $content = $child; |
||
| 208 | $this->attachViewResolver(); |
||
| 209 | } |
||
| 210 | } |
||
| 211 | if (!empty($content)) { |
||
| 212 | $e->setModel($content); |
||
| 213 | } |
||
| 214 | } else { |
||
| 215 | // attach the own resolver here too ? |
||
| 216 | // ... |
||
| 217 | } |
||
| 218 | } |
||
| 219 | |||
| 220 | /** |
||
| 221 | * Attach an own ViewResolver |
||
| 222 | */ |
||
| 223 | public function attachViewResolver() |
||
| 224 | { |
||
| 225 | if (!$this->viewResolverAttached) { |
||
| 226 | $this->viewResolverAttached = true; |
||
| 227 | $resolver = $this->serviceManager->get('ViewResolver'); |
||
| 228 | $resolver->attach($this, 100); |
||
| 229 | } |
||
| 230 | } |
||
| 231 | |||
| 232 | /** |
||
| 233 | * Transform the HTML to PDF, |
||
| 234 | * this is a post-rendering-process |
||
| 235 | * |
||
| 236 | * put in here everything related to the transforming-process like options |
||
| 237 | * |
||
| 238 | * @param \Zend\View\ViewEvent $e |
||
| 239 | */ |
||
| 240 | public function attachPDFtransformer(ViewEvent $e) |
||
| 241 | { |
||
| 242 | |||
| 243 | //$renderer = $e->getRenderer(); |
||
| 244 | $result = $e->getResult(); |
||
| 245 | $response = $e->getResponse(); |
||
| 246 | |||
| 247 | // the handles are for temporary files |
||
| 248 | error_reporting(0); |
||
| 249 | foreach (array(self::RENDER_FULL, self::RENDER_WITHOUT_PDF, self::RENDER_WITHOUT_ATTACHMENTS ) as $render) { |
||
| 250 | $handles = array(); |
||
| 251 | try { |
||
| 252 | $pdf = new extern\mPDFderive(); |
||
| 253 | $pdf->SetImportUse(); |
||
| 254 | // create bookmark list in Acrobat Reader |
||
| 255 | $pdf->h2bookmarks = array('H1' => 0, 'H2' => 1, 'H3' => 2); |
||
| 256 | $pdf->WriteHTML($result); |
||
| 257 | |||
| 258 | // Output of the Images |
||
| 259 | if (self::RENDER_FULL == $render || self::RENDER_WITHOUT_PDF == $render) { |
||
| 260 | if (is_array($this->appendImage) && !empty($this->appendImage)) { |
||
| 261 | foreach ($this->appendImage as $imageAttachment) { |
||
| 262 | $content = $imageAttachment->getContent(); |
||
| 263 | $url = 'data:image/' . $imageAttachment->getType() . ';base64,' . base64_encode($content); |
||
| 264 | $html = '<a name="attachment_' . $imageAttachment->getId() . '"><img src="' . $url . '" /><br /></a>'; |
||
| 265 | $pdf->WriteHTML($html); |
||
| 266 | } |
||
| 267 | } |
||
| 268 | } |
||
| 269 | |||
| 270 | // Temp Files PDF |
||
| 271 | if (self::RENDER_FULL == $render) { |
||
| 272 | if (is_array($this->appendPDF) && !empty($this->appendPDF)) { |
||
| 273 | foreach ($this->appendPDF as $pdfAttachment) { |
||
| 274 | $content = $pdfAttachment->getContent(); |
||
| 275 | $tmpHandle = tmpfile(); |
||
| 276 | $handles[] = $tmpHandle; |
||
| 277 | fwrite($tmpHandle, $content); |
||
| 278 | fseek($tmpHandle, 0); |
||
| 279 | } |
||
| 280 | } |
||
| 281 | } |
||
| 282 | |||
| 283 | // Output of the PDF |
||
| 284 | foreach ($handles as $handle) { |
||
| 285 | $meta_data = stream_get_meta_data($handle); |
||
| 286 | $filename = $meta_data["uri"]; |
||
| 287 | $pdf->WriteHTML($filename); |
||
| 288 | $pagecount = $pdf->SetSourceFile($filename); |
||
| 289 | for ($pages = 0; $pages < $pagecount; $pages++) { |
||
| 290 | $pdf->AddPage(); |
||
| 291 | $pdf->WriteHTML(' pages: ' . $pagecount); |
||
| 292 | $tx = $pdf->ImportPage($pages + 1); |
||
| 293 | $pdf->UseTemplate($tx); |
||
| 294 | } |
||
| 295 | } |
||
| 296 | |||
| 297 | $pdf_result = $pdf->Output(); |
||
| 298 | $e->setResult($pdf_result); |
||
| 299 | |||
| 300 | // delete all temporary Files again |
||
| 301 | foreach ($handles as $handle) { |
||
| 302 | fclose($handle); |
||
| 303 | } |
||
| 304 | break; |
||
| 305 | } catch (\Exception $e) { |
||
| 306 | } |
||
| 307 | } |
||
| 308 | error_reporting(E_ALL); |
||
| 309 | } |
||
| 310 | |||
| 311 | /** |
||
| 312 | * Look for a template with the Suffix ".pdf.phtml" |
||
| 313 | * |
||
| 314 | * @param string $name |
||
| 315 | * @param \Zend\View\Renderer\RendererInterface $renderer |
||
| 316 | * @return string|boolean |
||
| 317 | */ |
||
| 318 | public function resolve($name, Renderer $renderer = null) |
||
| 319 | { |
||
| 320 | if ($this->serviceManager->has('ViewTemplatePathStack')) { |
||
| 321 | // get all the Pases made up for the zend-provided resolver |
||
| 322 | // we won't get any closer to ALL than that |
||
| 323 | $viewTemplatePathStack = $this->serviceManager->get('ViewTemplatePathStack'); |
||
| 324 | $paths = $viewTemplatePathStack->getPaths(); |
||
| 325 | $defaultSuffix = $viewTemplatePathStack->getDefaultSuffix(); |
||
| 326 | if (pathinfo($name, PATHINFO_EXTENSION) != $defaultSuffix) { |
||
| 327 | ; |
||
| 328 | $name .= '.pdf.' . $defaultSuffix; |
||
| 329 | } else { |
||
| 330 | // TODO: replace Filename by Filename for PDF |
||
| 331 | } |
||
| 332 | |||
| 333 | foreach ($paths as $path) { |
||
| 334 | $file = new SplFileInfo($path . $name); |
||
| 335 | if ($file->isReadable()) { |
||
| 336 | // Found! Return it. |
||
| 337 | if (($filePath = $file->getRealPath()) === false && substr($path, 0, 7) === 'phar://') { |
||
| 338 | // Do not try to expand phar paths (realpath + phars == fail) |
||
| 339 | $filePath = $path . $name; |
||
| 340 | if (!file_exists($filePath)) { |
||
| 341 | break; |
||
| 342 | } |
||
| 343 | } |
||
| 344 | //if ($this->useStreamWrapper()) { |
||
| 345 | // // If using a stream wrapper, prepend the spec to the path |
||
| 346 | // $filePath = 'zend.view://' . $filePath; |
||
| 347 | //} |
||
| 348 | return $filePath; |
||
| 349 | } |
||
| 350 | } |
||
| 351 | } |
||
| 352 | // TODO: Resolving to an PDF has failed, this could have implications for the transformer |
||
| 353 | return false; |
||
| 354 | } |
||
| 355 | } |
||
| 356 |