Passed
Push — master ( 1ab5cb...bf69e0 )
by Dev
14:46
created

StaticService::generatePages()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
ccs 0
cts 4
cp 0
crap 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PiedWeb\CMSBundle\Service;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use PiedWeb\CMSBundle\Entity\PageInterface as Page;
7
use PiedWeb\CMSBundle\Service\PageCanonicalService as PageCanonical;
8
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
9
use Symfony\Component\Filesystem\Filesystem;
10
use Symfony\Component\HttpFoundation\Request;
11
use Symfony\Component\HttpFoundation\RequestStack;
12
use Symfony\Contracts\Translation\TranslatorInterface;
13
use Twig\Environment as Twig;
14
use WyriHaximus\HtmlCompress\Factory as HtmlCompressor;
15
use WyriHaximus\HtmlCompress\Parser as HtmlCompressorParser;
16
17
class StaticService
18
{
19
    /**
20
     * Contain files relative to SEO wich will be hard copied.
21
     *
22
     * @var array
23
     */
24
    protected $robotsFiles = ['robots.txt', 'feed.xml', 'sitemap.xml', 'sitemap.txt'];
25
26
    /**
27
     * @var array
28
     */
29
    protected $dontCopy = ['index.php', '.htaccess'];
30
31
    /**
32
     * @var EntityManagerInterface
33
     */
34
    protected $em;
35
36
    /**
37
     * @var Filesystem
38
     */
39
    protected $filesystem;
40
41
    /**
42
     * @var Twig
43
     */
44
    protected $twig;
45
46
    /**
47
     * @var string
48
     */
49
    protected $webDir;
50
51
    /**
52
     * @var string
53
     */
54
    protected $staticDir;
55
56
    /**
57
     * @var RequestStack
58
     */
59
    protected $requesStack;
60
61
    /**
62
     * @var \PiedWeb\CMSBundle\Service\PageCanonicalService
63
     */
64
    protected $pageCanonical;
65
66
    /**
67
     * @var TranslatorInterface
68
     */
69
    protected $translator;
70
71
    /**
72
     * @var HtmlCompressorParser
73
     */
74
    protected $parser;
75
76
    /**
77
     * @var ParameterBagInterface
78
     */
79
    protected $params;
80
81
    /**
82
     * Used in .htaccess generation.
83
     *
84
     * @var string
85
     */
86
    protected $redirections = '';
87
88
    public function __construct(
89
        EntityManagerInterface $em,
90
        Twig $twig,
91
        ParameterBagInterface $params,
92
        RequestStack $requesStack,
93
        PageCanonical $pageCanonical,
94
        TranslatorInterface $translator,
95
        string $webDir
96
    ) {
97
        $this->em = $em;
98
        $this->filesystem = new Filesystem();
99
        $this->twig = $twig;
100
        $this->params = $params;
101
        $this->requesStack = $requesStack;
102
        $this->webDir = $webDir;
103
        $this->pageCanonical = $pageCanonical;
104
        $this->translator = $translator;
105
        $this->staticDir = $this->params->get('pwc.static.dir');
106
        $this->parser = HtmlCompressor::construct();
107
    }
108
109
    /**
110
     * Main Logic is here.
111
     *
112
     * @throws \RuntimeException
113
     * @throws \LogicException
114
     */
115
    public function dump()
116
    {
117
        if (!method_exists($this->filesystem, 'dumpFile')) {
118
            throw new \RuntimeException('Method dumpFile() is not available. Upgrade your Filesystem.');
119
        }
120
121
        $this->filesystem->remove($this->staticDir);
122
123
        if ($this->params->get('pwc.static.generateForApache')) {
124
            $this->generateHtaccess();
125
            $symlink = $this->params->get('pwc.static.symlinkMedia');
126
        } else { //if ($this->params->has('pwc.static.generateForGithubPages')) {
127
            // symlink doesn't work on github page.
128
            $symlink = false;
129
            $this->generateCname();
130
        }
131
132
        $this->generatePages();
133
        $this->generateErrorPages();
134
        $this->copyRobotsFiles();
135
        $this->copyAssets($symlink);
136
        $this->copyMediaToDownload($symlink);
137
    }
138
139
    /**
140
     * Copy files relative to SEO (robots, sitemaps, etc.).
141
     */
142
    protected function copyRobotsFiles(): void
143
    {
144
        array_map([$this, 'copy'], $this->robotsFiles);
145
    }
146
147
    // todo
148
    // docs
149
    // https://help.github.com/en/github/working-with-github-pages/managing-a-custom-domain-for-your-github-pages-site
150
    protected function generateCname()
151
    {
152
        $this->filesystem->dumpFile($this->staticDir.'/CNAME', $this->params->get('pwc.static.domain'));
153
    }
154
155
    protected function generateHtaccess()
156
    {
157
        if (!$this->params->has('pwc.static.domain')) {
158
            throw new \Exception('Before, you need to configure (in config/packages/piedweb_cms.yaml) static_domain.');
159
        }
160
161
        $htaccess = $this->twig->render('@PiedWebCMS/static/htaccess.twig', [
162
            'domain' => $this->params->get('pwc.static.domain'),
163
            'redirections' => $this->redirections,
164
        ]);
165
        $this->filesystem->dumpFile($this->staticDir.'/.htaccess', $htaccess);
166
    }
167
168
    protected function copy(string $file): void
169
    {
170
        if (file_exists($file)) {
171
            copy(
172
                str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$file),
173
                $this->staticDir.'/'.$file
174
            );
175
        }
176
    }
177
178
    /**
179
     * Copy (or symlink) for all assets in public
180
     * (and media previously generated by liip in public).
181
     */
182
    protected function copyAssets(bool $symlink = true): void
183
    {
184
        $dir = dir($this->webDir);
185
        while (false !== $entry = $dir->read()) {
186
            if ('.' == $entry || '..' == $entry) {
187
                continue;
188
            }
189
            if (!in_array($entry, $this->robotsFiles) && !in_array($entry, $this->dontCopy)) {
190
                //$this->symlink(
191
                if (true === $symlink) {
192
                    $this->filesystem->symlink(
193
                        str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$entry),
194
                        $this->staticDir.'/'.$entry
195
                    );
196
                } else {
197
                    $action = is_file($this->webDir.'/'.$entry) ? 'copy' : 'mirror';
198
                    $this->filesystem->$action($this->webDir.'/'.$entry, $this->staticDir.'/'.$entry);
199
                }
200
            }
201
        }
202
        $dir->close();
203
    }
204
205
    /**
206
     * Copy or Symlink "not image" media to download folder.
207
     *
208
     * @return void
209
     */
210
    protected function copyMediaToDownload(bool $symlink = true)
211
    {
212
        if (!file_exists($this->staticDir.'/download')) {
213
            $this->filesystem->mkdir($this->staticDir.'/download/');
214
            $this->filesystem->mkdir($this->staticDir.'/download/media');
215
        }
216
217
        $dir = dir($this->webDir.'/../media');
218
        while (false !== $entry = $dir->read()) {
219
            if ('.' == $entry || '..' == $entry) {
220
                continue;
221
            }
222
            // if the file is an image, it's ever exist (maybe it's slow to check every files)
223
            if (!file_exists($this->webDir.'/media/default/'.$entry)) {
224
                if (true === $symlink) {
225
                    $this->filesystem->symlink('../../../media/'.$entry, $this->staticDir.'/download/media/'.$entry);
226
                } else {
227
                    $this->filesystem->copy(
228
                        $this->webDir.'/../media/'.$entry,
229
                        $this->staticDir.'/download/media/'.$entry
230
                    );
231
                }
232
            }
233
        }
234
235
        //$this->filesystem->$action($this->webDir.'/../media', $this->staticDir.'/download/media');
236
    }
237
238
    protected function generatePages(): void
239
    {
240
        $pages = $this->getPages();
241
242
        foreach ($pages as $page) {
243
            $this->generatePage($page);
244
            $this->generateFeedFor($page);
245
        }
246
    }
247
248
    /**
249
     * The function cache redirection found during generatePages and
250
     * format in self::$redirection the content for the .htaccess.
251
     *
252
     * @return void
253
     */
254
    protected function addRedirection(Page $page)
255
    {
256
        $this->redirections .= 'Redirect ';
257
        $this->redirections .= $page->getRedirectionCode().' '.$route.' '.$page->getRedirection();
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $route seems to be never defined.
Loading history...
Bug introduced by
The method getRedirection() does not exist on PiedWeb\CMSBundle\Entity\PageInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to PiedWeb\CMSBundle\Entity\PageInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

257
        $this->redirections .= $page->getRedirectionCode().' '.$route.' '.$page->/** @scrutinizer ignore-call */ getRedirection();
Loading history...
Bug introduced by
The method getRedirectionCode() does not exist on PiedWeb\CMSBundle\Entity\PageInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to PiedWeb\CMSBundle\Entity\PageInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

257
        $this->redirections .= $page->/** @scrutinizer ignore-call */ getRedirectionCode().' '.$route.' '.$page->getRedirection();
Loading history...
258
        $this->redirections .= PHP_EOL;
259
    }
260
261
    public function generatePage(Page $page)
262
    {
263
        // set current locale to avoid twig error
264
        $request = new Request();
265
        $request->setLocale($page->getLocale());
0 ignored issues
show
Bug introduced by
The method getLocale() does not exist on PiedWeb\CMSBundle\Entity\PageInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to PiedWeb\CMSBundle\Entity\PageInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

265
        $request->setLocale($page->/** @scrutinizer ignore-call */ getLocale());
Loading history...
266
        $this->requesStack->push($request);
267
268
        $this->translator->setLocale($page->getLocale());
269
270
        // check if it's a redirection
271
        if (false !== $page->getRedirection()) {
272
            return $this->addRedirection($page);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->addRedirection($page) targeting PiedWeb\CMSBundle\Servic...rvice::addRedirection() 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...
273
        }
274
275
        $slug = '' == $page->getRealSlug() ? 'index' : $page->getRealSlug();
0 ignored issues
show
Bug introduced by
The method getRealSlug() does not exist on PiedWeb\CMSBundle\Entity\PageInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to PiedWeb\CMSBundle\Entity\PageInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

275
        $slug = '' == $page->/** @scrutinizer ignore-call */ getRealSlug() ? 'index' : $page->getRealSlug();
Loading history...
276
        $route = $this->pageCanonical->generatePathForPage($slug);
277
        $filepath = $this->staticDir.$route.'.html';
278
279
        $dump = $this->render($page);
280
        $this->filesystem->dumpFile($filepath, $dump);
281
    }
282
283
    /**
284
     * Generate static file for feed indexing children pages
285
     * (only if children pages exists).
286
     *
287
     * @return void
288
     */
289
    protected function generateFeedFor(Page $page)
290
    {
291
        if ($page->getChildrenPages()->count() > 0) {
0 ignored issues
show
Bug introduced by
The method getChildrenPages() does not exist on PiedWeb\CMSBundle\Entity\PageInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to PiedWeb\CMSBundle\Entity\PageInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

291
        if ($page->/** @scrutinizer ignore-call */ getChildrenPages()->count() > 0) {
Loading history...
292
            $dump = $this->renderFeed($page);
293
            $this->filesystem->dumpFile(preg_replace('/.html$/', '.xml', $filepath), $dump);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $filepath seems to be never defined.
Loading history...
294
        }
295
    }
296
297
    protected function generateErrorPages(): void
298
    {
299
        $this->generateErrorPage();
300
301
        // todo i18n error in .htaccess
302
        $locales = explode('|', $this->params->get('pwc.locales'));
303
304
        foreach ($locales as $locale) {
305
            $this->filesystem->mkdir($this->staticDir.'/'.$locale);
306
            $this->generateErrorPage($locale);
307
        }
308
    }
309
310
    protected function generateErrorPage($locale = null, $uri = '404.html')
311
    {
312
        if (null !== $locale) {
313
            $request = new Request();
314
            $request->setLocale($locale);
315
            $this->requesStack->push($request);
316
        }
317
318
        $dump = $this->parser->compress($this->twig->render('@Twig/Exception/error.html.twig'));
319
        $this->filesystem->dumpFile($this->staticDir.(null !== $locale ? '/'.$locale : '').'/'.$uri, $dump);
320
    }
321
322
    protected function getPages()
323
    {
324
        $qb = $this->em->getRepository($this->params->get('pwc.entity_page'))->getQueryToFindPublished('p');
0 ignored issues
show
Bug introduced by
The method getQueryToFindPublished() does not exist on Doctrine\Persistence\ObjectRepository. It seems like you code against a sub-type of Doctrine\Persistence\ObjectRepository such as Doctrine\ORM\EntityRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

324
        $qb = $this->em->getRepository($this->params->get('pwc.entity_page'))->/** @scrutinizer ignore-call */ getQueryToFindPublished('p');
Loading history...
325
326
        return $qb->getQuery()->getResult();
327
    }
328
329
    protected function render(Page $page): string
330
    {
331
        $template = $this->params->get('pwc.default_page_template');
332
333
        return $this->parser->compress($this->twig->render($template, ['page' => $page]));
334
    }
335
336
    // todo i18n feed ...
337
    protected function renderFeed(Page $page): string
338
    {
339
        $template = '@PiedWebCMS/page/rss.xml.twig';
340
341
        return $this->parser->compress($this->twig->render($template, ['page' => $page]));
342
    }
343
}
344