Test Failed
Pull Request — master (#1676)
by Arnaud
04:53
created

Render::process()   F

Complexity

Conditions 17
Paths 849

Size

Total Lines 127
Code Lines 71

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 67
CRAP Score 17.6334

Importance

Changes 0
Metric Value
cc 17
eloc 71
c 0
b 0
f 0
nc 849
nop 0
dl 0
loc 127
ccs 67
cts 77
cp 0.8701
crap 17.6334
rs 1.2597

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Cecil.
7
 *
8
 * Copyright (c) Arnaud Ligny <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Cecil\Step\Pages;
15
16
use Cecil\Builder;
17
use Cecil\Collection\Page\Collection;
18
use Cecil\Collection\Page\Page;
19
use Cecil\Exception\RuntimeException;
20
use Cecil\Renderer\Config;
21
use Cecil\Renderer\Layout;
22
use Cecil\Renderer\Site;
23
use Cecil\Renderer\Twig;
24
use Cecil\Step\AbstractStep;
25
use Cecil\Util;
26
27
/**
28
 * Pages rendering.
29
 */
30
class Render extends AbstractStep
31
{
32
    /**
33
     * {@inheritdoc}
34
     */
35 1
    public function getName(): string
36
    {
37 1
        return 'Rendering pages';
38
    }
39
40
    /**
41
     * {@inheritdoc}
42
     */
43 1
    public function init(array $options): void
44
    {
45 1
        if (!is_dir($this->config->getLayoutsPath()) && !$this->config->hasTheme()) {
46
            $message = sprintf("'%s' is not a valid layouts directory", $this->config->getLayoutsPath());
47
            $this->builder->getLogger()->debug($message);
48
        }
49
50 1
        $this->canProcess = true;
51
    }
52
53
    /**
54
     * {@inheritdoc}
55
     *
56
     * @throws RuntimeException
57
     */
58 1
    public function process(): void
59
    {
60
        // prepares renderer
61 1
        $this->builder->setRenderer(new Twig($this->builder, $this->getAllLayoutsPaths()));
62
63
        // adds global variables
64 1
        $this->addGlobals();
65
66
        /** @var Collection $pages */
67 1
        $pages = $this->builder->getPages()
68 1
            // published only
69 1
            ->filter(function (Page $page) {
70 1
                return (bool) $page->getVariable('published');
71 1
            })
72 1
            // enrichs some variables
73 1
            ->map(function (Page $page) {
74 1
                $formats = $this->getOutputFormats($page);
75
                // output formats
76 1
                $page->setVariable('output', $formats);
77
                // alternates formats
78 1
                $page->setVariable('alternates', $this->getAlternates($formats));
79
                // translations
80 1
                $page->setVariable('translations', $this->getTranslations($page));
81
82 1
                return $page;
83 1
            });
84 1
        $total = \count($pages);
85
86
        // renders each page
87 1
        $count = 0;
88
        Util::autoload($this->builder, 'postprocessors');
89 1
        /** @var Page $page */
90 1
        foreach ($pages as $page) {
91 1
            $count++;
92
            $rendered = [];
93
94 1
            // l10n
95 1
            $language = $page->getVariable('language', $this->config->getLanguageDefault());
96 1
            $locale = $this->config->getLanguageProperty('locale', $language);
97
            $this->builder->getRenderer()->setLocale($locale);
98
99 1
            // global site variables
100
            $this->builder->getRenderer()->addGlobal('site', new Site($this->builder, $language));
101
102 1
            // global config raw variables
103 1
            $this->builder->getRenderer()->addGlobal('config', new Config($this->builder, $language));
104 1
105
            // excluded format(s)?
106
            $formats = (array) $page->getVariable('output');
107
            foreach ($formats as $key => $format) {
108
                if ($exclude = $this->config->getOutputFormatProperty($format, 'exclude')) {
109
                    // ie:
110 1
                    //   formats:
111
                    //     atom:
112
                    //       [...]
113 1
                    //       exclude: [paginated]
114 1
                    if (!\is_array($exclude)) {
115 1
                        $exclude = [$exclude];
116
                    }
117
                    foreach ($exclude as $variable) {
118
                        if ($page->hasVariable($variable)) {
119
                            unset($formats[$key]);
120
                        }
121
                    }
122 1
                }
123
            }
124 1
125
            // renders each output format
126
            foreach ($formats as $format) {
127 1
                // search for the template
128 1
                $layout = Layout::finder($page, $format, $this->config);
129 1
                // renders with Twig
130 1
                try {
131
                    $deprecations = [];
132 1
                    set_error_handler(function ($type, $msg) use (&$deprecations) {
133 1
                        if (E_USER_DEPRECATED === $type) {
134 1
                            $deprecations[] = $msg;
135 1
                        }
136
                    });
137 1
                    $output = $this->builder->getRenderer()->render($layout['file'], ['page' => $page]);
138 1
                    foreach ($deprecations as $value) {
139 1
                        $this->builder->getLogger()->warning($value);
140 1
                    }
141 1
                    foreach ($this->config->get('output.postprocessors') as $processor) {
142 1
                        if (!class_exists($processor)) {
143 1
                            $this->builder->getLogger()->error(sprintf('Can\'t load output post processor "%s"', $processor));
144 1
                            break;
145 1
                        }
146
                        $output = (new $processor($this->builder))->process($page, $output, $format);
147 1
                    }
148 1
                    $rendered[$format] = [
149 1
                        'output'   => $output,
150 1
                        'template' => [
151 1
                            'scope' => $layout['scope'],
152 1
                            'file'  => $layout['file'],
153
                        ],
154
                    ];
155
                    $page->addRendered($rendered);
156
                    // profiler
157
                    if ($this->builder->isDebug()) {
158
                        $dumper = new \Twig\Profiler\Dumper\HtmlDumper();
159
                        file_put_contents(
160
                            Util::joinFile($this->config->getOutputPath(), '_debug_twig_profile.html'),
161
                            $dumper->dump($this->builder->getRenderer()->getDebugProfile())
162
                        );
163
                    }
164
                } catch (\Twig\Error\Error $e) {
165
                    $template = !empty($e->getSourceContext()->getPath()) ? $e->getSourceContext()->getPath() : $e->getSourceContext()->getName();
166 1
167
                    throw new RuntimeException(sprintf(
168 1
                        'Template "%s%s" (page: %s): %s',
169 1
                        $template,
170 1
                        $e->getTemplateLine() >= 0 ? sprintf(':%s', $e->getTemplateLine()) : '',
171 1
                        $page->getId(),
172 1
                        $e->getMessage()
173 1
                    ));
174 1
                }
175
            }
176
            $this->builder->getPages()->replace($page->getId(), $page);
177
178
            $templates = array_column($rendered, 'template');
179
            $message = sprintf(
180
                'Page "%s" rendered with [%s]',
181 1
                $page->getId() ?: 'index',
182
                Util\Str::combineArrayToString($templates, 'scope', 'file')
183 1
            );
184
            $this->builder->getLogger()->info($message, ['progress' => [$count, $total]]);
185
        }
186 1
    }
187 1
188
    /**
189
     * Returns an array of layouts directories.
190 1
     */
191 1
    protected function getAllLayoutsPaths(): array
192 1
    {
193 1
        $paths = [];
194
195
        // layouts/
196
        if (is_dir($this->config->getLayoutsPath())) {
197 1
            $paths[] = $this->config->getLayoutsPath();
198 1
        }
199
        // <theme>/layouts/
200
        if ($this->config->hasTheme()) {
201 1
            $themes = $this->config->getTheme();
202
            foreach ($themes as $theme) {
203
                $paths[] = $this->config->getThemeDirPath($theme);
204
            }
205
        }
206
        // resources/layouts/
207 1
        if (is_dir($this->config->getLayoutsInternalPath())) {
208
            $paths[] = $this->config->getLayoutsInternalPath();
209 1
        }
210 1
211 1
        return $paths;
212 1
    }
213 1
214
    /**
215
     * Adds global variables.
216
     */
217
    protected function addGlobals()
218
    {
219
        $this->builder->getRenderer()->addGlobal('cecil', [
220
            'url'       => sprintf('https://cecil.app/#%s', Builder::getVersion()),
221 1
            'version'   => Builder::getVersion(),
222
            'poweredby' => sprintf('Cecil v%s', Builder::getVersion()),
223
        ]);
224
    }
225
226
    /**
227
     * Get available output formats.
228 1
     *
229 1
     * @throws RuntimeException
230 1
     */
231 1
    protected function getOutputFormats(Page $page): array
232
    {
233
        // Get page output format(s) if defined.
234 1
        // ie:
235
        // ```yaml
236
        // output: txt
237
        // ```
238
        if ($page->getVariable('output')) {
239
            $formats = $page->getVariable('output');
240
            if (!\is_array($formats)) {
241
                $formats = [$formats];
242 1
            }
243 1
244
            return $formats;
245
        }
246 1
247
        // Get available output formats for the page type.
248
        // ie:
249
        // ```yaml
250 1
        // page: [html, json]
251
        // ```
252
        $formats = $this->config->get('output.pagetypeformats.' . $page->getType());
253
        if (empty($formats)) {
254
            throw new RuntimeException('Configuration key "pagetypeformats" can\'t be empty.');
255
        }
256 1
        if (!\is_array($formats)) {
257
            $formats = [$formats];
258 1
        }
259
260 1
        return $formats;
261 1
    }
262 1
263 1
    /**
264 1
     * Get alternates.
265 1
     */
266 1
    protected function getAlternates(array $formats): array
267 1
    {
268 1
        $alternates = [];
269
270
        if (\count($formats) > 1 || \in_array('html', $formats)) {
271
            foreach ($formats as $format) {
272 1
                $format == 'html' ? $rel = 'canonical' : $rel = 'alternate';
273
                $alternates[] = [
274
                    'rel'    => $rel,
275
                    'type'   => $this->config->getOutputFormatProperty($format, 'mediatype'),
276
                    'title'  => strtoupper($format),
277
                    'format' => $format,
278 1
                ];
279
            }
280 1
        }
281 1
282 1
        return $alternates;
283 1
    }
284 1
285 1
    /**
286 1
     * Returns the collection of translated pages for a given page.
287
     */
288 1
    protected function getTranslations(Page $refPage): \Cecil\Collection\Page\Collection
289
    {
290
        $pages = $this->builder->getPages()->filter(function (Page $page) use ($refPage) {
291
            return $page->getId() !== $refPage->getId()
292
                && $page->getVariable('langref') == $refPage->getVariable('langref')
293
                && $page->getType() == $refPage->getType()
294 1
                && !empty($page->getVariable('published'))
295
                && !$page->getVariable('paginated');
296
        });
297 1
298
        return $pages;
299 1
    }
300
}
301