StaticAppGenerator   F
last analyzed

Complexity

Total Complexity 61

Size/Duplication

Total Lines 439
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 164
dl 0
loc 439
rs 3.52
c 1
b 0
f 0
wmc 61

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 29 2
A generateErrorPage() 0 10 3
A generatePages() 0 8 2
A generate() 0 13 1
A copy() 0 6 2
B copyAssets() 0 23 8
A generateFromHost() 0 3 1
A generateCname() 0 3 1
A generateAll() 0 13 4
A compress() 0 3 1
A generatePage() 0 10 2
B copyMediaToDownload() 0 25 7
A mustSymlink() 0 3 2
A generateErrorPages() 0 10 2
A generateFilePath() 0 6 2
A saveAsStatic() 0 21 5
A copyRobotsFiles() 0 3 1
A generateHtaccess() 0 7 1
A getPageRepository() 0 3 1
A generateFeedFor() 0 5 1
A generateSitemap() 0 13 3
A addRedirection() 0 7 1
A generateSitemaps() 0 8 3
A generateFeed() 0 13 3
A generateServerManagerFile() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like StaticAppGenerator 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.

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 StaticAppGenerator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PiedWeb\CMSBundle\Extension\StaticGenerator;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use PiedWeb\CMSBundle\Entity\PageInterface as Page;
7
use PiedWeb\CMSBundle\Repository\PageRepository;
8
use PiedWeb\CMSBundle\Service\App;
9
use PiedWeb\CMSBundle\Service\PageCanonicalService as PageCanonical;
10
use PiedWeb\CMSBundle\Utils\GenerateLivePathForTrait;
11
use PiedWeb\CMSBundle\Utils\KernelTrait;
12
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
13
use Symfony\Component\Filesystem\Filesystem;
14
use Symfony\Component\HttpFoundation\Request;
15
use Symfony\Component\HttpFoundation\RequestStack;
16
use Symfony\Component\HttpKernel\KernelInterface;
17
use Symfony\Component\Routing\RouterInterface;
18
use Symfony\Contracts\Translation\TranslatorInterface;
19
use Twig\Environment as Twig;
20
use WyriHaximus\HtmlCompress\Factory as HtmlCompressor;
21
use WyriHaximus\HtmlCompress\HtmlCompressorInterface;
22
23
/**
24
 * Generate 1 App.
25
 */
26
class StaticAppGenerator
27
{
28
    use GenerateLivePathForTrait;
29
    use KernelTrait;
30
31
    /**
32
     * Contain files relative to SEO wich will be hard copied.
33
     *
34
     * @var array
35
     */
36
    protected $robotsFiles = ['robots.txt'];
37
38
    /**
39
     * @var array
40
     */
41
    protected $dontCopy = ['index.php', '.htaccess'];
42
43
    /**
44
     * @var EntityManagerInterface
45
     */
46
    protected $em;
47
48
    /**
49
     * @var Filesystem
50
     */
51
    protected $filesystem;
52
53
    /**
54
     * @var Twig
55
     */
56
    protected $twig;
57
58
    /**
59
     * @var string
60
     */
61
    protected $webDir;
62
63
    /**
64
     * @var array
65
     */
66
    protected $apps;
67
    protected $app;
68
    protected $staticDomain;
69
    protected $mustGetPagesWithoutHost = true;
70
71
    /** var @string */
72
    protected $staticDir;
73
74
    /**
75
     * @var RequestStack
76
     */
77
    protected $requesStack;
78
79
    /**
80
     * @var \PiedWeb\CMSBundle\Service\PageCanonicalService
81
     */
82
    protected $pageCanonical;
83
84
    /**
85
     * @var TranslatorInterface
86
     */
87
    protected $translator;
88
89
    /**
90
     * @var HtmlCompressorInterface
91
     */
92
    protected $parser;
93
94
    /**
95
     * @var ParameterBagInterface
96
     */
97
    protected $params;
98
99
    /**
100
     * @var RouterInterface
101
     */
102
    protected $router;
103
104
    /**
105
     * Used in .htaccess generation.
106
     *
107
     * @var string
108
     */
109
    protected $redirections = '';
110
111
    public function __construct(
112
        EntityManagerInterface $em,
113
        Twig $twig,
114
        ParameterBagInterface $params,
115
        RequestStack $requesStack,
116
        PageCanonical $pageCanonical,
117
        TranslatorInterface $translator,
118
        RouterInterface $router,
119
        string $webDir,
120
        KernelInterface $kernel
121
    ) {
122
        $this->em = $em;
123
        $this->filesystem = new Filesystem();
124
        $this->twig = $twig;
125
        $this->params = $params;
126
        $this->requesStack = $requesStack;
127
        $this->webDir = $webDir;
128
        $this->pageCanonical = $pageCanonical;
129
        $this->translator = $translator;
130
        $this->router = $router;
131
        $this->apps = $this->params->get('pwc.apps');
132
        $this->parser = HtmlCompressor::construct();
133
134
        if (! method_exists($this->filesystem, 'dumpFile')) {
135
            throw new \RuntimeException('Method dumpFile() is not available. Upgrade your Filesystem.');
136
        }
137
138
        static::loadKernel($kernel);
139
        $this->kernel = $kernel;
140
    }
141
142
    public function generateAll($filter = null)
143
    {
144
        foreach ($this->apps as $app) {
145
            if ($filter && ! \in_array($filter, $app->getHost())) {
146
                continue;
147
            }
148
            $this->generate($app, $this->mustGetPagesWithoutHost);
149
            //$this->generateStaticApp($app);
150
151
            $this->mustGetPagesWithoutHost = false;
152
        }
153
154
        return true;
155
    }
156
157
    public function generateFromHost($host)
158
    {
159
        return $this->generateAll($host);
160
    }
161
162
    /**
163
     * Main Logic is here.
164
     *
165
     * @throws \RuntimeException
166
     * @throws \LogicException
167
     */
168
    public function generate($app, $mustGetPagesWithoutHost = false)
169
    {
170
        $this->app = new App($app['hosts'][0], [$app]);
171
        $this->mustGetPagesWithoutHost = $mustGetPagesWithoutHost;
172
173
        $this->filesystem->remove($this->app->getStaticDir());
174
        $this->generatePages();
175
        $this->generateSitemaps();
176
        $this->generateErrorPages();
177
        $this->copyRobotsFiles();
178
        $this->generateServerManagerFile();
179
        $this->copyAssets();
180
        $this->copyMediaToDownload();
181
    }
182
183
    /**
184
     * Symlink doesn't work on github page, symlink only for apache if conf say OK to symlink.
185
     */
186
    protected function mustSymlink()
187
    {
188
        return $this->app->get('static_generateForApache') ? $this->app->get('static_symlinkMedia') : false;
189
    }
190
191
    /**
192
     * Generate .htaccess for Apache or CNAME for github
193
     * Must be run after generatePages() !!
194
     */
195
    protected function generateServerManagerFile()
196
    {
197
        if ($this->app->get('static_generateForApache')) {
198
            $this->generateHtaccess();
199
        } else { //if ($this->app['static_generateForGithubPages'])) {
200
            $this->generateCname();
201
        }
202
    }
203
204
    /**
205
     * Copy files relative to SEO (robots, sitemaps, etc.).
206
     */
207
    protected function copyRobotsFiles(): void
208
    {
209
        array_map([$this, 'copy'], $this->robotsFiles);
210
    }
211
212
    // todo
213
    // docs
214
    // https://help.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site
215
    protected function generateCname()
216
    {
217
        $this->filesystem->dumpFile($this->app->getStaticDir().'/CNAME', $this->app->getMainHost());
218
    }
219
220
    protected function generateHtaccess()
221
    {
222
        $htaccess = $this->twig->render('@PiedWebCMS/static/htaccess.twig', [
223
            'domain' => $this->app->getMainHost(),
224
            'redirections' => $this->redirections,
225
        ]);
226
        $this->filesystem->dumpFile($this->app->getStaticDir().'/.htaccess', $htaccess);
227
    }
228
229
    protected function copy(string $file): void
230
    {
231
        if (file_exists($file)) {
232
            copy(
233
                str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$file),
234
                $this->app->getStaticDir().'/'.$file
235
            );
236
        }
237
    }
238
239
    /**
240
     * Copy (or symlink) for all assets in public
241
     * (and media previously generated by liip in public).
242
     */
243
    protected function copyAssets(): void
244
    {
245
        $symlink = $this->mustSymlink();
246
247
        $dir = dir($this->webDir);
248
        while (false !== $entry = $dir->read()) {
249
            if ('.' == $entry || '..' == $entry) {
250
                continue;
251
            }
252
            if (! \in_array($entry, $this->robotsFiles) && ! \in_array($entry, $this->dontCopy)) {
253
                //$this->symlink(
254
                if (true === $symlink) {
255
                    $this->filesystem->symlink(
256
                        str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$entry),
257
                        $this->app->getStaticDir().'/'.$entry
258
                    );
259
                } else {
260
                    $action = is_file($this->webDir.'/'.$entry) ? 'copy' : 'mirror';
261
                    $this->filesystem->$action($this->webDir.'/'.$entry, $this->app->getStaticDir().'/'.$entry);
262
                }
263
            }
264
        }
265
        $dir->close();
266
    }
267
268
    /**
269
     * Copy or Symlink "not image" media to download folder.
270
     *
271
     * @return void
272
     */
273
    protected function copyMediaToDownload()
274
    {
275
        $symlink = $this->mustSymlink();
276
277
        if (! file_exists($this->app->getStaticDir().'/download')) {
278
            $this->filesystem->mkdir($this->app->getStaticDir().'/download/');
279
            $this->filesystem->mkdir($this->app->getStaticDir().'/download/media');
280
        }
281
282
        $dir = dir($this->webDir.'/../media');
283
        while (false !== $entry = $dir->read()) {
284
            if ('.' == $entry || '..' == $entry) {
285
                continue;
286
            }
287
            // if the file is an image, it's ever exist (maybe it's slow to check every files)
288
            if (! file_exists($this->webDir.'/media/default/'.$entry)) {
289
                if (true === $symlink) {
290
                    $this->filesystem->symlink(
291
                        '../../../media/'.$entry,
292
                        $this->app->getStaticDir().'/download/media/'.$entry
293
                    );
294
                } else {
295
                    $this->filesystem->copy(
296
                        $this->webDir.'/../media/'.$entry,
297
                        $this->app->getStaticDir().'/download/media/'.$entry
298
                    );
299
                }
300
            }
301
        }
302
303
        //$this->filesystem->$action($this->webDir.'/../media', $this->app->getStaticDir().'/download/media');
304
    }
305
306
    protected function generateSitemaps(): void
307
    {
308
        foreach (explode('|', $this->params->get('pwc.locales')) as $locale) {
309
            foreach (['txt', 'xml'] as $format) {
310
                $this->generateSitemap($locale, $format);
311
            }
312
313
            $this->generateFeed($locale);
314
        }
315
    }
316
317
    protected function generateSitemap($locale, $format)
318
    {
319
        $liveUri = $this->generateLivePathFor(
320
            $this->app->getMainHost(),
321
            'piedweb_cms_page_sitemap',
322
            ['locale' => $locale, '_format' => $format]
323
        );
324
        $staticFile = $this->app->getStaticDir().'/sitemap'.$locale.'.'.$format; // todo get it from URI removing host
325
        $this->saveAsStatic($liveUri, $staticFile);
326
327
        if ($this->params->get('locale') == $locale ? '' : '.'.$locale) {
328
            $staticFile = $this->app->getStaticDir().'/sitemap.'.$format;
329
            $this->saveAsStatic($liveUri, $staticFile);
330
        }
331
    }
332
333
    protected function generateFeed($locale)
334
    {
335
        $liveUri = $this->generateLivePathFor(
336
            $this->app->getMainHost(),
337
            'piedweb_cms_page_main_feed',
338
            ['locale' => $locale]
339
        );
340
        $staticFile = $this->app->getStaticDir().'/feed'.$locale.'.xml';
341
        $this->saveAsStatic($liveUri, $staticFile);
342
343
        if ($this->params->get('locale') == $locale ? '' : '.'.$locale) {
344
            $staticFile = $this->app->getStaticDir().'/feed.xml';
345
            $this->saveAsStatic($liveUri, $staticFile);
346
        }
347
    }
348
349
    /**
350
     * The function cache redirection found during generatePages and
351
     * format in self::$redirection the content for the .htaccess.
352
     *
353
     * @return void
354
     */
355
    protected function addRedirection(Page $page)
356
    {
357
        $this->redirections .= 'Redirect ';
358
        $this->redirections .= $page->getRedirectionCode().' ';
359
        $this->redirections .= $this->pageCanonical->generatePathForPage($page->getRealSlug());
360
        $this->redirections .= ' '.$page->getRedirection();
361
        $this->redirections .= PHP_EOL;
362
    }
363
364
    protected function generatePages(): void
365
    {
366
        $qb = $this->getPageRepository()->getQueryToFindPublished('p');
367
        $qb = $this->getPageRepository()->andHost($qb, $this->app->getMainHost(), $this->mustGetPagesWithoutHost);
368
        $pages = $qb->getQuery()->getResult();
369
370
        foreach ($pages as $page) {
371
            $this->generatePage($page);
372
            //if ($page->getRealSlug()) $this->generateFeedFor($page);
373
        }
374
    }
375
376
    protected function generatePage(Page $page)
377
    {
378
        // check if it's a redirection
379
        if (false !== $page->getRedirection()) {
380
            $this->addRedirection($page);
381
382
            return;
383
        }
384
385
        $this->saveAsStatic($this->generateLivePathFor($page), $this->generateFilePath($page));
386
    }
387
388
    protected function saveAsStatic($liveUri, $destination)
389
    {
390
        $request = Request::create($liveUri);
391
392
        $response = static::$appKernel->handle($request);
393
394
        if ($response->isRedirect()) {
395
            // todo
396
            //$this->addRedirection($liveUri, getRedirectUri)
397
            return;
398
        } elseif (200 != $response->getStatusCode()) {
399
            //$this->kernel = static::$appKernel;
400
            if (500 === $response->getStatusCode() && 'dev' == $this->kernel->getEnvironment()) {
401
                exit($this->kernel->handle($request));
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
402
            }
403
404
            return;
405
        }
406
407
        $content = $this->compress($response->getContent());
408
        $this->filesystem->dumpFile($destination, $content);
409
    }
410
411
    protected function compress($html)
412
    {
413
        return $this->parser->compress($html);
414
    }
415
416
    protected function generateFilePath(Page $page)
417
    {
418
        $slug = '' == $page->getRealSlug() ? 'index' : $page->getRealSlug();
419
        $route = $this->pageCanonical->generatePathForPage($slug);
420
421
        return $this->app->getStaticDir().$route.'.html';
422
    }
423
424
    /**
425
     * Generate static file for feed indexing children pages
426
     * (only if children pages exists).
427
     *
428
     * @return void
429
     */
430
    protected function generateFeedFor(Page $page)
431
    {
432
        $liveUri = $this->generateLivePathFor($page, 'piedweb_cms_page_feed');
433
        $staticFile = preg_replace('/.html$/', '.xml', $this->generateFilePath($page));
434
        $this->saveAsStatic($liveUri, $staticFile);
435
    }
436
437
    protected function generateErrorPages(): void
438
    {
439
        $this->generateErrorPage();
440
441
        // todo i18n error in .htaccess
442
        $locales = explode('|', $this->params->get('pwc.locales'));
443
444
        foreach ($locales as $locale) {
445
            $this->filesystem->mkdir($this->app->getStaticDir().'/'.$locale);
446
            $this->generateErrorPage($locale);
447
        }
448
    }
449
450
    protected function generateErrorPage($locale = null, $uri = '404.html')
451
    {
452
        if (null !== $locale) {
453
            $request = new Request();
454
            $request->setLocale($locale);
455
            $this->requesStack->push($request);
456
        }
457
458
        $dump = $this->parser->compress($this->twig->render('@Twig/Exception/error.html.twig'));
459
        $this->filesystem->dumpFile($this->app->getStaticDir().(null !== $locale ? '/'.$locale : '').'/'.$uri, $dump);
460
    }
461
462
    protected function getPageRepository(): PageRepository
463
    {
464
        return $this->em->getRepository($this->params->get('pwc.entity_page'));
465
    }
466
}
467