Passed
Push — master ( 9f96f6...0f86e3 )
by Dev
12:11 queued 07:09
created

StaticService::symlink()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 10
ccs 0
cts 8
cp 0
crap 6
rs 10
1
<?php
2
3
namespace PiedWeb\CMSBundle\Service;
4
5
use Doctrine\ORM\EntityManagerInterface;
6
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
7
use Liip\ImagineBundle\Imagine\Data\DataManager;
8
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
9
use PiedWeb\CMSBundle\Entity\PageInterface as Page;
10
use PiedWeb\CMSBundle\EventListener\MediaCacheGeneratorTrait;
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\Contracts\Translation\TranslatorInterface;
17
use Twig\Environment as Twig;
18
use \WyriHaximus\HtmlCompress\Factory as HtmlCompressor;
19
20
class StaticService
21
{
22
    //use MediaCacheGeneratorTrait;
23
24
    /**
25
     * @var EntityManagerInterface
26
     */
27
    private $em;
28
29
    /**
30
     * @var Filesystem
31
     */
32
    private $filesystem;
33
34
    /**
35
     * @var \Twig_environement
0 ignored issues
show
Bug introduced by
The type Twig_environement was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
36
     */
37
    private $twig;
38
39
    /**
40
     * @var string
41
     */
42
    private $webDir;
43
44
    /**
45
     * @var string
46
     */
47
    private $staticDir;
48
49
    /**
50
     * @var RequestStack
51
     */
52
    private $requesStack;
53
54
    /**
55
     * @var \PiedWeb\CMSBundle\Service\PageCanonicalService
56
     */
57
    private $pageCanonical;
58
59
    /**
60
     * @var TranslatorInterface
61
     */
62
    private $translator;
63
64
    private $parser;
65
66
    private $params;
67
68
    protected $redirections = '';
69
70
    private $cacheManager;
71
    private $dataManager;
72
    private $filterManager;
73
74
    public function __construct(
75
        EntityManagerInterface $em,
76
        Twig $twig,
77
        ParameterBagInterface $params,
78
        RequestStack $requesStack,
79
        PageCanonical $pageCanonical,
80
        TranslatorInterface $translator,
81
        CacheManager $cacheManager,
82
        DataManager $dataManager,
83
        FilterManager $filterManager,
84
        string $webDir
85
    ) {
86
        $this->em = $em;
87
        $this->filesystem = new Filesystem();
88
        $this->twig = $twig;
0 ignored issues
show
Documentation Bug introduced by
It seems like $twig of type Twig\Environment is incompatible with the declared type Twig_environement of property $twig.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
89
        $this->params = $params;
90
        $this->requesStack = $requesStack;
91
        $this->webDir = $webDir;
92
        $this->pageCanonical = $pageCanonical;
93
        $this->translator = $translator;
94
        $this->cacheManager = $cacheManager;
95
        $this->dataManager = $dataManager;
96
        $this->filterManager = $filterManager;
97
        $this->staticDir = $this->webDir.'/../static';
98
        $this->parser = HtmlCompressor::construct();
99
    }
100
101
    /**
102
     * @throws \RuntimeException
103
     * @throws \LogicException
104
     */
105
    public function dump()
106
    {
107
        if (!method_exists($this->filesystem, 'dumpFile')) {
108
            throw new \RuntimeException('Method dumpFile() is not available. Upgrade your Filesystem.');
109
        }
110
111
        $this->rmdir($this->staticDir);
112
        $this->pageToStatic();
113
        $this->assetsToStatic();
114
        $this->htaccessToStatic();
115
        $this->mediaToDownload();
116
    }
117
118
    protected function htaccessToStatic()
119
    {
120
        if (!$this->params->has('app.static_domain')) {
121
            throw new \Exception('Before, you need to configure (in config/services.yaml) app.static_domain.');
122
        }
123
124
        $htaccess = $this->twig->render('@PiedWebCMS/static/htaccess.twig', [
125
            'domain' => $this->params->get('app.static_domain'),
126
            'redirections' => $this->redirections,
127
        ]);
128
        $this->filesystem->dumpFile($this->staticDir.'/.htaccess', $htaccess);
129
    }
130
131
    protected static function rmdir($dir)
132
    {
133
        if (is_link($dir)) {
134
            unlink($dir);
135
        }
136
137
        if (!is_dir($dir)) {
138
            return false;
139
        }
140
141
        $dir_handle = opendir($dir);
142
        if (!$dir_handle) {
0 ignored issues
show
introduced by
$dir_handle is of type false|resource, thus it always evaluated to false.
Loading history...
143
            return false;
144
        }
145
146
        while ($file = readdir($dir_handle)) {
147
            if ('.' != $file && '..' != $file) {
148
                if (!is_dir($dir.'/'.$file)) {
149
                    unlink($dir.'/'.$file);
150
                } else {
151
                    self::rmdir($dir.'/'.$file);
152
                }
153
            }
154
        }
155
        closedir($dir_handle);
156
        rmdir($dir);
157
158
        return true;
159
    }
160
161
    protected function symlink(string $target, string $link): bool
162
    {
163
        // Check for symlinks
164
        if (is_link($target)) {
165
            return symlink(readlink($target), $link);
166
        }
167
168
        return symlink(
169
            $target,
170
            $link
171
        );
172
    }
173
174
    /**
175
     * We create symlink for all assets
176
     * and we copy feed.xml, sitemap.xml, robots.txt (avoid new page not yet generated try to be indexed by bots).
177
     */
178
    protected function assetsToStatic(): self
179
    {
180
        $dir = dir($this->webDir);
181
        while (false !== $entry = $dir->read()) {
182
            if ('.' == $entry || '..' == $entry) {
183
                continue;
184
            }
185
            if (in_array($entry, ['robots.txt', 'feed.xml', 'sitemap.xml', 'sitemap.txt'])) {
186
                copy(
187
                    str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$entry),
188
                    $this->staticDir.'/'.$entry
189
                );
190
            } elseif ('index.php' != $entry) {
191
                $this->symlink(
192
                    str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/'.$entry),
193
                    $this->staticDir.'/'.$entry
194
                );
195
            }
196
        }
197
        $dir->close();
198
199
        return $this;
200
    }
201
202
    protected function mediaToDownload()
203
    {
204
        $this->filesystem->mkdir($this->staticDir.'/download/');
205
        symlink($this->webDir.'/../media', $this->staticDir.'/download/media');
206
207
        /* create download symlink *
208
        if (!file_exists($this->staticDir.'/download')) {
209
            mkdir($this->staticDir.'/download');
210
        }
211
        $this->symlink(
212
            str_replace($this->params->get('kernel.project_dir').'/', '../', $this->webDir.'/../media'),
213
            $this->staticDir.'/download/media'
214
        );
215
        /**/
216
    }
217
218
    protected function pageToStatic(): self
219
    {
220
        $pages = $this->getPages();
221
222
        // todo i18n error
223
        $locales = explode('|', $this->params->get('app.locales'));
224
225
        foreach ($locales as $locale) {
226
            $this->filesystem->mkdir($this->staticDir.'/'.$locale);
227
            $this->generateErrorPage($locale);
228
        }
229
230
        foreach ($pages as $page) {
231
            // set current locale to avoid twig error
232
            $request = new Request();
233
            $request->setLocale($page->getLocale());
234
            $this->requesStack->push($request);
235
236
            $page->setTranslatableLocale($page->getLocale());
237
            $this->em->refresh($page);
238
239
            $this->translator->setLocale($page->getLocale());
240
241
            $slug = '' == $page->getRealSlug() ? 'index' : $page->getRealSlug();
242
            $route = $this->pageCanonical->generatePathForPage($slug);
243
            $filepath = $this->staticDir.$route.'.html';
244
245
            // check if it's a redirection
246
            if (false !== $page->getRedirection()) {
247
                $this->redirections .= 'Redirect ';
248
                $this->redirections .= $page->getRedirectionCode().' '.$route.' '.$page->getRedirection();
249
                $this->redirections .= PHP_EOL;
250
                continue;
251
            }
252
253
            $dump = $this->render($page);
254
            $this->filesystem->dumpFile($filepath, $dump);
255
256
            if ($page->getChildrenPages()->count() > 0) {
257
                $dump = $this->renderFeed($page);
258
                $this->filesystem->dumpFile(preg_replace('/.html$/', '.xml', $filepath), $dump);
259
            }
260
        }
261
262
        return $this;
263
    }
264
265
    protected function generateErrorPage($locale = null)
266
    {
267
        if (null !== $locale) {
268
            $request = new Request();
269
            $request->setLocale($locale);
270
            $this->requesStack->push($request);
271
        }
272
273
        $dump = $this->parser->compress($this->twig->render('@Twig/Exception/error.html.twig'));
274
        $this->filesystem->dumpFile($this->staticDir.(null !== $locale ? '/'.$locale : '').'/_error.html', $dump);
275
    }
276
277
    protected function getPages()
278
    {
279
        $qb = $this->em->getRepository($this->params->get('app.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

279
        $qb = $this->em->getRepository($this->params->get('app.entity_page'))->/** @scrutinizer ignore-call */ getQueryToFindPublished('p');
Loading history...
280
281
        return $qb->getQuery()->getResult();
282
    }
283
284
    protected function render(Page $page)
285
    {
286
        $template = $this->params->get('app.default_page_template');
287
288
        return $this->parser->compress($this->twig->render($template, ['page' => $page]));
289
    }
290
291
    // todo i18n feed ...
292
    protected function renderFeed(Page $page)
293
    {
294
        $template = '@PiedWebCMS/page/rss.xml.twig';
295
296
        return $this->parser->compress($this->twig->render($template, ['page' => $page]));
297
    }
298
}
299