Passed
Push — master ( 97dd58...4109cd )
by Dev
10:22
created

StaticAppGenerator::__construct()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 33
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 17
nc 3
nop 9
dl 0
loc 33
rs 9.7
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace PiedWeb\CMSBundle\StaticGenerator;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use PiedWeb\CMSBundle\Entity\PageInterface;
7
use PiedWeb\CMSBundle\Entity\PageInterface as Page;
8
use PiedWeb\CMSBundle\Repository\PageRepository;
9
use PiedWeb\CMSBundle\Service\AppConfigHelper;
10
use PiedWeb\CMSBundle\Service\AppConfigHelper as App;
11
use PiedWeb\CMSBundle\Service\PageCanonicalService as PageCanonical;
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\HttpFoundation\Response;
17
use Symfony\Component\Routing\Router;
18
use Symfony\Component\Routing\RouterInterface;
19
use Symfony\Contracts\Translation\TranslatorInterface;
20
use Twig\Environment as Twig;
21
use WyriHaximus\HtmlCompress\Factory as HtmlCompressor;
22
use WyriHaximus\HtmlCompress\HtmlCompressorInterface;
23
24
/**
25
 * Generate 1 App.
26
 */
27
class StaticAppGenerator
28
{
29
    /**
30
     * Contain files relative to SEO wich will be hard copied.
31
     *
32
     * @var array
33
     */
34
    protected $robotsFiles = ['robots.txt'];
35
36
    /**
37
     * @var array
38
     */
39
    protected $dontCopy = ['index.php', '.htaccess'];
40
41
    /**
42
     * @var EntityManagerInterface
43
     */
44
    protected $em;
45
46
    /**
47
     * @var Filesystem
48
     */
49
    protected $filesystem;
50
51
    /**
52
     * @var Twig
53
     */
54
    protected $twig;
55
56
    /**
57
     * @var string
58
     */
59
    protected $webDir;
60
61
    /**
62
     * @var array
63
     */
64
    protected $apps;
65
    protected $app;
66
    protected $staticDomain;
67
    protected $mustGetPagesWithoutHost;
68
69
    /** var @string */
70
    protected $staticDir;
71
72
    /**
73
     * @var RequestStack
74
     */
75
    protected $requesStack;
76
77
    /**
78
     * @var \PiedWeb\CMSBundle\Service\PageCanonicalService
79
     */
80
    protected $pageCanonical;
81
82
    /**
83
     * @var TranslatorInterface
84
     */
85
    protected $translator;
86
87
    /**
88
     * @var HtmlCompressorInterface
89
     */
90
    protected $parser;
91
92
    /**
93
     * @var ParameterBagInterface
94
     */
95
    protected $params;
96
    public static $appKernel;
97
98
    /**
99
     * @var Router
100
     */
101
    protected $router;
102
103
    /**
104
     * Used in .htaccess generation.
105
     *
106
     * @var string
107
     */
108
    protected $redirections = '';
109
110
    public function __construct(
111
        EntityManagerInterface $em,
112
        Twig $twig,
113
        ParameterBagInterface $params,
114
        RequestStack $requesStack,
115
        PageCanonical $pageCanonical,
116
        TranslatorInterface $translator,
117
        RouterInterface $router,
118
        string $webDir,
119
        $kernel
120
    ) {
121
        $this->em = $em;
122
        $this->filesystem = new Filesystem();
123
        $this->twig = $twig;
124
        $this->params = $params;
125
        $this->requesStack = $requesStack;
126
        $this->webDir = $webDir;
127
        $this->pageCanonical = $pageCanonical;
128
        $this->translator = $translator;
129
        $this->router = $router;
0 ignored issues
show
Documentation Bug introduced by
$router is of type Symfony\Component\Routing\RouterInterface, but the property $router was declared to be of type Symfony\Component\Routing\Router. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
130
        $this->apps = $this->params->get('pwc.apps');
131
        $this->parser = HtmlCompressor::construct();
132
133
        if (!method_exists($this->filesystem, 'dumpFile')) {
134
            throw new \RuntimeException('Method dumpFile() is not available. Upgrade your Filesystem.');
135
        }
136
137
        if (null === static::$appKernel) {
138
            $kernelClass = \get_class($kernel);
139
            static::$appKernel = new $kernelClass('prod', false);
140
            //static::$appKernel = clone $kernel;
141
            // NOTE: If we clone, it's take too much time in dev mod
142
            static::$appKernel->boot();
143
        }
144
    }
145
146
    public function generateAll($filter = null)
147
    {
148
149
        foreach ($this->apps as $app) {
150
            if ($filter && !in_array($filter, $app->getHost())) {
151
                    continue;
152
            }
153
            $this->generate($app, $this->mustGetPagesWithoutHost);
154
            //$this->generateStaticApp($app);
155
156
            $this->mustGetPagesWithoutHost = false;
157
        }
158
    }
159
160
    public function generateFromHost($host)
161
    {
162
        return $this->generateAll($host);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->generateAll($host) targeting PiedWeb\CMSBundle\Static...enerator::generateAll() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
163
    }
164
165
    /**
166
     * Main Logic is here.
167
     *
168
     * @throws \RuntimeException
169
     * @throws \LogicException
170
     */
171
    public function generate($app, $mustGetPagesWithoutHost = false)
172
    {
173
        $this->app = new App($app['hosts'][0], [$app]);
174
        $this->mustGetPagesWithoutHost = $mustGetPagesWithoutHost;
175
176
        $this->filesystem->remove($this->app->getStaticDir());
177
        $this->generatePages();
178
        $this->generateSitemaps();
179
        $this->generateErrorPages();
180
        $this->copyRobotsFiles();
181
        $this->generateServerManagerFile();
182
        $this->copyAssets();
183
        $this->copyMediaToDownload();
184
    }
185
186
    protected function generateLivePathFor($host, $route = 'piedweb_cms_page', $params = [])
187
    {
188
        if ($host instanceof PageInterface) {
189
            $page = $host;
190
            $host = $page->getHost();
191
        }
192
193
        if (isset($page)) {
194
            $params['slug'] = $page->getRealSlug();
195
        }
196
197
        if ($host) {
198
            $params['host'] = $host;
199
            $route = 'custom_host_'.$route;
200
        }
201
202
        return $this->router->generate($route, $params);
203
    }
204
205
    /**
206
     * Symlink doesn't work on github page, symlink only for apache if conf say OK to symlink.
207
     */
208
    protected function mustSymlink()
209
    {
210
        return $this->app->get('static_generateForApache') ? $this->app->get('static_symlinkMedia') : false;
211
    }
212
213
    /**
214
     * Generate .htaccess for Apache or CNAME for github
215
     * Must be run after generatePages() !!
216
     */
217
    protected function generateServerManagerFile()
218
    {
219
        if ($this->app->get('static_generateForApache')) {
220
            $this->generateHtaccess();
221
        } else { //if ($this->app['static_generateForGithubPages'])) {
222
            $this->generateCname();
223
        }
224
    }
225
226
    /**
227
     * Copy files relative to SEO (robots, sitemaps, etc.).
228
     */
229
    protected function copyRobotsFiles(): void
230
    {
231
        array_map([$this, 'copy'], $this->robotsFiles);
232
    }
233
234
    // todo
235
    // docs
236
    // https://help.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site
237
    protected function generateCname()
238
    {
239
        $this->filesystem->dumpFile($this->app->getStaticDir().'/CNAME', $this->app->getMainHost());
240
    }
241
242
    protected function generateHtaccess()
243
    {
244
        $htaccess = $this->twig->render('@PiedWebCMS/static/htaccess.twig', [
245
            'domain' => $this->app->getMainHost(),
246
            'redirections' => $this->redirections,
247
        ]);
248
        $this->filesystem->dumpFile($this->app->getStaticDir().'/.htaccess', $htaccess);
249
    }
250
251
    protected function copy(string $file): void
252
    {
253
        if (file_exists($file)) {
254
            copy(
255
                str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$file),
256
                $this->app->getStaticDir().'/'.$file
257
            );
258
        }
259
    }
260
261
    /**
262
     * Copy (or symlink) for all assets in public
263
     * (and media previously generated by liip in public).
264
     */
265
    protected function copyAssets(): void
266
    {
267
        $symlink = $this->mustSymlink();
268
269
        $dir = dir($this->webDir);
270
        while (false !== $entry = $dir->read()) {
271
            if ('.' == $entry || '..' == $entry) {
272
                continue;
273
            }
274
            if (!\in_array($entry, $this->robotsFiles) && !\in_array($entry, $this->dontCopy)) {
275
                //$this->symlink(
276
                if (true === $symlink) {
277
                    $this->filesystem->symlink(
278
                        str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$entry),
279
                        $this->app->getStaticDir().'/'.$entry
280
                    );
281
                } else {
282
                    $action = is_file($this->webDir.'/'.$entry) ? 'copy' : 'mirror';
283
                    $this->filesystem->$action($this->webDir.'/'.$entry, $this->app->getStaticDir().'/'.$entry);
284
                }
285
            }
286
        }
287
        $dir->close();
288
    }
289
290
    /**
291
     * Copy or Symlink "not image" media to download folder.
292
     *
293
     * @return void
294
     */
295
    protected function copyMediaToDownload()
296
    {
297
        $symlink = $this->mustSymlink();
298
299
        if (!file_exists($this->app->getStaticDir().'/download')) {
300
            $this->filesystem->mkdir($this->app->getStaticDir().'/download/');
301
            $this->filesystem->mkdir($this->app->getStaticDir().'/download/media');
302
        }
303
304
        $dir = dir($this->webDir.'/../media');
305
        while (false !== $entry = $dir->read()) {
306
            if ('.' == $entry || '..' == $entry) {
307
                continue;
308
            }
309
            // if the file is an image, it's ever exist (maybe it's slow to check every files)
310
            if (!file_exists($this->webDir.'/media/default/'.$entry)) {
311
                if (true === $symlink) {
312
                    $this->filesystem->symlink('../../../media/'.$entry, $this->app->getStaticDir().'/download/media/'.$entry);
313
                } else {
314
                    $this->filesystem->copy(
315
                        $this->webDir.'/../media/'.$entry,
316
                        $this->app->getStaticDir().'/download/media/'.$entry
317
                    );
318
                }
319
            }
320
        }
321
322
        //$this->filesystem->$action($this->webDir.'/../media', $this->app->getStaticDir().'/download/media');
323
    }
324
325
    protected function generatePages(): void
326
    {
327
        $qb = $this->getPageRepository()->getQueryToFindPublished('p');
328
        $qb = $this->getPageRepository()->andHost($qb, $this->app->getMainHost(), $this->mustGetPagesWithoutHost);
329
        $pages = $qb->getQuery()->getResult();
330
331
        foreach ($pages as $page) {
332
            $this->generatePage($page);
333
            $this->generateFeedFor($page);
334
        }
335
    }
336
337
    protected function generateSitemaps(): void
338
    {
339
        foreach (explode('|', $this->params->get('pwc.locales')) as $locale) {
340
341
            foreach (['txt', 'xml'] as $format) {
342
                $this->generateSitemap($locale, $format);
343
            }
344
345
            $this->generateFeed($locale);
346
        }
347
    }
348
349
    protected function generateSitemap($locale, $format)
350
    {
351
        $liveUri = $this->generateLivePathFor($this->app->getMainHost(), 'piedweb_cms_page_sitemap', ['locale'=>$locale, '_format'=>$format]);
352
        $staticFile = $this->app->getStaticDir().'/sitemap'.$locale.'.'.$format; // todo get it from URI removing host
353
        $this->saveAsStatic($liveUri, $staticFile);
354
355
        if ($this->params->get('locale') == $locale ? '' : '.'.$locale) {
356
            $staticFile = $this->app->getStaticDir().'/sitemap.'.$format;
357
            $this->saveAsStatic($liveUri, $staticFile);
358
        }
359
    }
360
361
    protected function generateFeed($locale)
362
    {
363
        $liveUri = $this->generateLivePathFor($this->app->getMainHost(), 'piedweb_cms_page_main_feed', ['locale'=>$locale]);
364
        $staticFile = $this->app->getStaticDir().'/feed'.$locale.'.xml';
365
        $this->saveAsStatic($liveUri, $staticFile);
366
367
        if ($this->params->get('locale') == $locale ? '' : '.'.$locale) {
368
            $staticFile = $this->app->getStaticDir().'/feed.xml';
369
            $this->saveAsStatic($liveUri, $staticFile);
370
        }
371
    }
372
373
    /**
374
     * The function cache redirection found during generatePages and
375
     * format in self::$redirection the content for the .htaccess.
376
     *
377
     * @return void
378
     */
379
    protected function addRedirection(Page $page)
380
    {
381
        $this->redirections .= 'Redirect ';
382
        $this->redirections .= $page->getRedirectionCode().' ';
383
        $this->redirections .= $this->pageCanonical->generatePathForPage($page->getRealSlug());
384
        $this->redirections .= ' '.$page->getRedirection();
385
        $this->redirections .= PHP_EOL;
386
    }
387
388
    protected function generatePage(Page $page)
389
    {
390
        /**/
391
        // check if it's a redirection
392
        if (false !== $page->getRedirection()) {
393
            $this->addRedirection($page);
394
395
            return;
396
        }
397
        /**/
398
399
        $this->saveAsStatic($this->generateLivePathFor($page), $this->generateFilePath($page));
400
    }
401
402
    protected function saveAsStatic($liveUri, $destination)
403
    {
404
        $request = Request::create($liveUri);
405
406
        $response = static::$appKernel->handle($request);
407
408
        if ($response->isRedirect()) {
409
            // todo
410
            //$this->addRedirection($liveUri, getRedirectUri)
411
            return;
412
        }
413
        elseif (200 != $response->getStatusCode()) {
414
            // todo log context
415
            return;
416
        }
417
418
        $content = $this->compress($response->getContent());
419
        $this->filesystem->dumpFile($destination, $content);
420
    }
421
422
    protected function compress($html)
423
    {
424
        return $this->parser->compress($html);
425
    }
426
427
    protected function generateFilePath(Page $page)
428
    {
429
        $slug = '' == $page->getRealSlug() ? 'index' : $page->getRealSlug();
430
        $route = $this->pageCanonical->generatePathForPage($slug);
431
432
        return $this->app->getStaticDir().$route.'.html';
433
    }
434
435
    /**
436
     * Generate static file for feed indexing children pages
437
     * (only if children pages exists).
438
     *
439
     * @return void
440
     */
441
    protected function generateFeedFor(Page $page)
442
    {
443
        $liveUri = $this->generateLivePathFor($page, 'piedweb_cms_page_feed');
444
        $staticFile = preg_replace('/.html$/', '.xml', $this->generateFilePath($page));
445
        $this->saveAsStatic($liveUri, $staticFile);
446
    }
447
448
    protected function generateErrorPages(): void
449
    {
450
        $this->generateErrorPage();
451
452
        // todo i18n error in .htaccess
453
        $locales = explode('|', $this->params->get('pwc.locales'));
454
455
        foreach ($locales as $locale) {
456
            $this->filesystem->mkdir($this->app->getStaticDir().'/'.$locale);
457
            $this->generateErrorPage($locale);
458
        }
459
    }
460
461
    protected function generateErrorPage($locale = null, $uri = '404.html')
462
    {
463
        if (null !== $locale) {
464
            $request = new Request();
465
            $request->setLocale($locale);
466
            $this->requesStack->push($request);
467
        }
468
469
        $dump = $this->parser->compress($this->twig->render('@Twig/Exception/error.html.twig'));
470
        $this->filesystem->dumpFile($this->app->getStaticDir().(null !== $locale ? '/'.$locale : '').'/'.$uri, $dump);
471
    }
472
473
    protected function getPageRepository(): PageRepository
474
    {
475
        return $this->em->getRepository($this->params->get('pwc.entity_page'));
476
    }
477
}
478