Completed
Push — master ( 814bfa...dac46c )
by Vladimir
02:01
created

Compiler::compilePageView()   B

Complexity

Conditions 5
Paths 11

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 6.4222

Importance

Changes 0
Metric Value
dl 0
loc 39
c 0
b 0
f 0
rs 8.9848
ccs 16
cts 26
cp 0.6153
cc 5
nc 11
nop 1
crap 6.4222
1
<?php
2
3
/**
4
 * @copyright 2018 Vladimir Jimenez
5
 * @license   https://github.com/stakx-io/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx;
9
10
use allejo\stakx\Document\BasePageView;
11
use allejo\stakx\Document\CollectableItem;
12
use allejo\stakx\Document\DynamicPageView;
13
use allejo\stakx\Document\PermalinkDocument;
14
use allejo\stakx\Document\RepeaterPageView;
15
use allejo\stakx\Document\StaticPageView;
16
use allejo\stakx\Document\TemplateReadyDocument;
17
use allejo\stakx\Event\CompilerPostRenderDynamicPageView;
18
use allejo\stakx\Event\CompilerPostRenderRepeaterPageView;
19
use allejo\stakx\Event\CompilerPostRenderStaticPageView;
20
use allejo\stakx\Event\CompilerPreRenderDynamicPageView;
21
use allejo\stakx\Event\CompilerPreRenderRepeaterPageView;
22
use allejo\stakx\Event\CompilerPreRenderStaticPageView;
23
use allejo\stakx\Event\CompilerTemplateCreation;
24
use allejo\stakx\Exception\FileAwareException;
25
use allejo\stakx\Filesystem\Folder;
26
use allejo\stakx\FrontMatter\ExpandedValue;
27
use allejo\stakx\Manager\CollectionManager;
28
use allejo\stakx\Manager\DataManager;
29
use allejo\stakx\Manager\MenuManager;
30
use allejo\stakx\Manager\PageManager;
31
use allejo\stakx\Templating\TemplateBridgeInterface;
32
use allejo\stakx\Templating\TemplateErrorInterface;
33
use allejo\stakx\Templating\TemplateInterface;
34
use Psr\Log\LoggerInterface;
35
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
36
37
/**
38
 * This class takes care of rendering the Twig body of PageViews with the respective information and it also takes care
39
 * of writing the rendered Twig to the filesystem.
40
 *
41
 * @since 0.1.1
42
 */
43
class Compiler
44
{
45
    /** @var string|false */
46
    private $redirectTemplate;
47
48
    /**
49
     * All of the PageViews handled by this Compiler instance indexed by their file paths relative to the site root.
50
     *
51
     * ```
52
     * array['_pages/index.html.twig'] = &PageView;
53
     * ```
54
     *
55
     * @var BasePageView[]
56
     */
57
    private $pageViewsFlattened;
58
59
    /** @var string[] */
60
    private $templateMapping;
61
62
    /** @var Folder */
63
    private $folder;
64
65
    /** @var string */
66
    private $theme;
67
68
    private $templateBridge;
69
    private $pageManager;
70
    private $eventDispatcher;
71
    private $configuration;
72
    private $logger;
73
74 12
    public function __construct(
75
        TemplateBridgeInterface $templateBridge,
76
        Configuration $configuration,
77
        CollectionManager $collectionManager,
78
        DataManager $dataManager,
79
        MenuManager $menuManager,
80
        PageManager $pageManager,
81
        EventDispatcherInterface $eventDispatcher,
82
        LoggerInterface $logger
83
    ) {
84 12
        $this->templateBridge = $templateBridge;
85 12
        $this->theme = '';
86 12
        $this->pageManager = $pageManager;
87 12
        $this->eventDispatcher = $eventDispatcher;
88 12
        $this->logger = $logger;
89 12
        $this->configuration = $configuration;
90
91 12
        $this->pageViewsFlattened = &$pageManager->getPageViewsFlattened();
92 12
        $this->redirectTemplate = $this->configuration->getRedirectTemplate();
93
94
        // Global variables maintained by stakx
95 12
        $this->templateBridge->setGlobalVariable('site', $configuration->getConfiguration());
96 12
        $this->templateBridge->setGlobalVariable('data', $dataManager->getJailedDataItems());
97 12
        $this->templateBridge->setGlobalVariable('collections', $collectionManager->getJailedCollections());
98 12
        $this->templateBridge->setGlobalVariable('menu', $menuManager->getSiteMenu());
99 12
        $this->templateBridge->setGlobalVariable('pages', $pageManager->getJailedStaticPageViews());
100 12
        $this->templateBridge->setGlobalVariable('repeaters', $pageManager->getJailedRepeaterPageViews());
101 12
    }
102
103
    /**
104
     * @param Folder $folder
105
     */
106 12
    public function setTargetFolder(Folder $folder)
107
    {
108 12
        $this->folder = $folder;
109 12
    }
110
111
    /**
112
     * @param string $themeName
113
     */
114
    public function setThemeName($themeName)
115
    {
116
        $this->theme = $themeName;
117
    }
118
119
    ///
120
    // Twig parent templates
121
    ///
122
123
    public function getTemplateBridge()
124
    {
125
        return $this->templateBridge;
126
    }
127
128
    public function getTemplateMappings()
129
    {
130
        return $this->templateMapping;
131
    }
132
133
    ///
134
    // Rendering HTML Functionality
135
    ///
136
137
    /**
138
     * Get the HTML for a Static PageView.
139
     *
140
     * This function just **renders** the HTML but does not write it to the filesystem. Use `compilePageView()` for that
141
     * instead.
142
     *
143
     * @param StaticPageView $pageView
144
     *
145
     * @throws TemplateErrorInterface
146
     *
147
     * @return string the HTML for a Static PageView
148
     */
149 10
    public function renderStaticPageView(StaticPageView $pageView)
150
    {
151 10
        $pageView->compile();
152
153 10
        return $this->buildStaticPageViewHTML($pageView);
154
    }
155
156
    /**
157
     * Get the HTML for a Dynamic PageView and ContentItem.
158
     *
159
     * This function just **renders** the HTML but does not write it to the filesystem. Use `compileDynamicPageView()`
160
     * for that instead.
161
     *
162
     * @param DynamicPageView       $pageView
163
     * @param TemplateReadyDocument $contentItem
164
     *
165
     * @throws TemplateErrorInterface
166
     *
167
     * @return string
168
     */
169
    public function renderDynamicPageView(DynamicPageView $pageView, TemplateReadyDocument $contentItem)
170
    {
171
        $template = $this->createTwigTemplate($pageView);
172
173
        return $this->buildDynamicPageViewHTML($template, $contentItem);
174
    }
175
176
    /**
177
     * Get the HTML for a Repeater PageView.
178
     *
179
     * @param RepeaterPageView $pageView
180
     * @param ExpandedValue    $expandedValue
181
     *
182
     * @throws TemplateErrorInterface
183
     *
184
     * @return string
185
     */
186
    public function renderRepeaterPageView(RepeaterPageView $pageView, ExpandedValue $expandedValue)
187
    {
188
        $template = $this->createTwigTemplate($pageView);
189
190
        return $this->buildRepeaterPageViewHTML($template, $pageView, $expandedValue);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\RepeaterPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
191
    }
192
193
    ///
194
    // IO Functionality
195
    ///
196
197
    /**
198
     * Compile all of the PageViews registered with the compiler.
199
     *
200
     * @since 0.1.0
201
     */
202 12
    public function compileAll()
203
    {
204 12
        foreach ($this->pageViewsFlattened as &$pageView)
205
        {
206 12
            $this->compilePageView($pageView);
207
        }
208 12
    }
209
210
    /**
211
     * Compile an individual PageView item.
212
     *
213
     * This function will take care of determining *how* to treat the PageView and write the compiled output to a the
214
     * respective target file.
215
     *
216
     * @param DynamicPageView|RepeaterPageView|StaticPageView $pageView The PageView that needs to be compiled
0 ignored issues
show
Documentation introduced by
Should the type for parameter $pageView not be BasePageView?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
217
     *
218
     * @since 0.1.1
219
     */
220 12
    public function compilePageView(BasePageView &$pageView)
221
    {
222 12
        Service::setOption('currentTemplate', $pageView->getAbsoluteFilePath());
223 12
        $this->logger->debug('Compiling {type} PageView: {pageview}', [
224 12
            'pageview' => $pageView->getRelativeFilePath(),
225 12
            'type' => $pageView->getType(),
226
        ]);
227
228
        try
229
        {
230 12
            switch ($pageView->getType())
231
            {
232 12
                case BasePageView::STATIC_TYPE:
233 10
                    $this->compileStaticPageView($pageView);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\StaticPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
234 10
                    $this->compileStandardRedirects($pageView);
235 10
                    break;
236
237 3
                case BasePageView::DYNAMIC_TYPE:
238
                    $this->compileDynamicPageView($pageView);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\DynamicPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
239
                    $this->compileStandardRedirects($pageView);
240
                    break;
241
242 3
                case BasePageView::REPEATER_TYPE:
243 3
                    $this->compileRepeaterPageView($pageView);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\RepeaterPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
244 3
                    $this->compileExpandedRedirects($pageView);
245 12
                    break;
246
            }
247
        }
248
        catch (TemplateErrorInterface $e)
249
        {
250
            throw new FileAwareException(
251
                $e->getMessage(),
252
                $e->getCode(),
253
                $e,
254
                $pageView->getRelativeFilePath(),
255
                $e->getTemplateLine() + $pageView->getLineOffset()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface allejo\stakx\Document\PermalinkDocument as the method getLineOffset() does only exist in the following implementations of said interface: allejo\stakx\Document\BasePageView, allejo\stakx\Document\ContentItem, allejo\stakx\Document\DynamicPageView, allejo\stakx\Document\PermalinkFrontMatterDocument, allejo\stakx\Document\RepeaterPageView, allejo\stakx\Document\StaticPageView.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
256
            );
257
        }
258 12
    }
259
260
    /**
261
     * Write the compiled output for a static PageView.
262
     *
263
     * @since 0.1.1
264
     *
265
     * @throws TemplateErrorInterface
266
     */
267 10
    private function compileStaticPageView(StaticPageView &$pageView)
268
    {
269 10
        $this->writeToFilesystem(
270 10
            $pageView->getTargetFile(),
271 10
            $this->renderStaticPageView($pageView),
272 10
            BasePageView::STATIC_TYPE
273
        );
274 10
    }
275
276
    /**
277
     * Write the compiled output for a dynamic PageView.
278
     *
279
     * @param DynamicPageView $pageView
280
     *
281
     * @since 0.1.1
282
     *
283
     * @throws TemplateErrorInterface
284
     */
285
    private function compileDynamicPageView(DynamicPageView &$pageView)
286
    {
287
        $contentItems = $pageView->getCollectableItems();
288
        $template = $this->createTwigTemplate($pageView);
289
290
        foreach ($contentItems as &$contentItem)
291
        {
292
            if ($contentItem->isDraft() && !Service::hasRunTimeFlag(RuntimeStatus::USING_DRAFTS))
293
            {
294
                $this->logger->debug('{file}: marked as a draft', [
295
                    'file' => $contentItem->getRelativeFilePath(),
296
                ]);
297
298
                continue;
299
            }
300
301
            $this->writeToFilesystem(
302
                $contentItem->getTargetFile(),
303
                $this->buildDynamicPageViewHTML($template, $contentItem),
304
                BasePageView::DYNAMIC_TYPE
305
            );
306
307
            $this->compileStandardRedirects($contentItem);
0 ignored issues
show
Documentation introduced by
$contentItem is of type object<allejo\stakx\Docu...\TemplateReadyDocument>, but the function expects a object<allejo\stakx\Document\PermalinkDocument>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
308
        }
309
    }
310
311
    /**
312
     * Write the compiled output for a repeater PageView.
313
     *
314
     * @param RepeaterPageView $pageView
315
     *
316
     * @since 0.1.1
317
     *
318
     * @throws TemplateErrorInterface
319
     */
320 3
    private function compileRepeaterPageView(RepeaterPageView &$pageView)
321
    {
322 3
        $template = $this->createTwigTemplate($pageView);
323 3
        $permalinks = $pageView->getRepeaterPermalinks();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class allejo\stakx\Document\BasePageView as the method getRepeaterPermalinks() does only exist in the following sub-classes of allejo\stakx\Document\BasePageView: allejo\stakx\Document\RepeaterPageView. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
324
325 3
        foreach ($permalinks as $permalink)
326
        {
327 3
            $this->writeToFilesystem(
328 3
                $pageView->getTargetFile($permalink->getEvaluated()),
329 3
                $this->buildRepeaterPageViewHTML($template, $pageView, $permalink),
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\RepeaterPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
330 3
                BasePageView::REPEATER_TYPE
331
            );
332
        }
333 3
    }
334
335
    /**
336
     * Write the given $output to the $targetFile as a $fileType PageView.
337
     *
338
     * @param string $targetFile
339
     * @param string $output
340
     * @param string $fileType
341
     */
342 12
    private function writeToFilesystem($targetFile, $output, $fileType)
343
    {
344 12
        $this->logger->notice('Writing {type} PageView file: {file}', [
345 12
            'type' => $fileType,
346 12
            'file' => $targetFile,
347
        ]);
348 12
        $this->folder->writeFile($targetFile, $output);
349 12
    }
350
351
    ///
352
    // Redirect handling
353
    ///
354
355
    /**
356
     * Write redirects for standard redirects.
357
     *
358
     * @throws TemplateErrorInterface
359
     *
360
     * @since 0.1.1
361
     */
362 10
    private function compileStandardRedirects(PermalinkDocument &$pageView)
363
    {
364 10
        $redirects = $pageView->getRedirects();
365
366 10 View Code Duplication
        foreach ($redirects as $redirect)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
367
        {
368 1
            $redirectPageView = BasePageView::createRedirect(
369 1
                $redirect,
370 1
                $pageView->getPermalink(),
371 1
                $this->redirectTemplate
372
            );
373 1
            $redirectPageView->evaluateFrontMatter([], [
374 1
                'site' => $this->configuration->getConfiguration(),
375
            ]);
376
377 1
            $this->compileStaticPageView($redirectPageView);
378
        }
379 10
    }
380
381
    /**
382
     * Write redirects for expanded redirects.
383
     *
384
     * @param RepeaterPageView $pageView
385
     *
386
     * @since 0.1.1
387
     */
388 3
    private function compileExpandedRedirects(RepeaterPageView &$pageView)
389
    {
390 3
        $permalinks = $pageView->getRepeaterPermalinks();
391
392
        /** @var ExpandedValue[] $repeaterRedirect */
393 3
        foreach ($pageView->getRepeaterRedirects() as $repeaterRedirect)
394
        {
395
            /**
396
             * @var int
397
             * @var ExpandedValue $redirect
398
             */
399 1 View Code Duplication
            foreach ($repeaterRedirect as $index => $redirect)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
400
            {
401 1
                $redirectPageView = BasePageView::createRedirect(
402 1
                    $redirect->getEvaluated(),
403 1
                    $permalinks[$index]->getEvaluated(),
404 1
                    $this->redirectTemplate
405
                );
406 1
                $redirectPageView->evaluateFrontMatter([], [
407 1
                    'site' => $this->configuration->getConfiguration(),
408
                ]);
409
410 1
                $this->compilePageView($redirectPageView);
411
            }
412
        }
413 3
    }
414
415
    ///
416
    // Twig Functionality
417
    ///
418
419
    /**
420
     * Get the compiled HTML for a specific iteration of a repeater PageView.
421
     *
422
     * @param TemplateInterface $template
423
     * @param RepeaterPageView  $pageView
424
     * @param ExpandedValue     $expandedValue
425
     *
426
     * @since  0.1.1
427
     *
428
     * @return string
429
     */
430 3
    private function buildRepeaterPageViewHTML(TemplateInterface &$template, RepeaterPageView &$pageView, ExpandedValue &$expandedValue)
431
    {
432
        $defaultContext = [
433 3
            'this' => $pageView->createJail(),
434
        ];
435
436 3
        $pageView->evaluateFrontMatter([
437 3
            'permalink' => $expandedValue->getEvaluated(),
438 3
            'iterators' => $expandedValue->getIterators(),
439
        ]);
440
441 3
        $preEvent = new CompilerPreRenderRepeaterPageView($pageView, $expandedValue);
442 3
        $this->eventDispatcher->dispatch(CompilerPreRenderRepeaterPageView::NAME, $preEvent);
443
444 3
        $context = array_merge($preEvent->getCustomVariables(), $defaultContext);
445
        $output = $template
446 3
            ->render($context)
447
        ;
448
449 3
        $postEvent = new CompilerPostRenderRepeaterPageView($pageView, $expandedValue, $output);
450 3
        $this->eventDispatcher->dispatch(CompilerPostRenderRepeaterPageView::NAME, $postEvent);
451
452 3
        return $postEvent->getCompiledOutput();
453
    }
454
455
    /**
456
     * Get the compiled HTML for a specific ContentItem.
457
     *
458
     * @param CollectableItem|TemplateReadyDocument $twigItem
459
     *
460
     * @since  0.1.1
461
     *
462
     * @return string
463
     */
464 View Code Duplication
    private function buildDynamicPageViewHTML(TemplateInterface &$template, &$twigItem)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
465
    {
466
        $defaultContext = [
467
            'this' => $twigItem->createJail(),
0 ignored issues
show
Bug introduced by
The method createJail does only exist in allejo\stakx\Document\TemplateReadyDocument, but not in allejo\stakx\Document\CollectableItem.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
468
        ];
469
470
        $preEvent = new CompilerPreRenderDynamicPageView($twigItem);
0 ignored issues
show
Bug introduced by
It seems like $twigItem defined by parameter $twigItem on line 464 can also be of type object<allejo\stakx\Docu...\TemplateReadyDocument>; however, allejo\stakx\Event\Compi...PageView::__construct() does only seem to accept object<allejo\stakx\Document\CollectableItem>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
471
        $this->eventDispatcher->dispatch(CompilerPreRenderDynamicPageView::NAME, $preEvent);
472
473
        $context = array_merge($preEvent->getCustomVariables(), $defaultContext);
474
        $output = $template
475
            ->render($context)
476
        ;
477
478
        $postEvent = new CompilerPostRenderDynamicPageView($twigItem, $output);
0 ignored issues
show
Bug introduced by
It seems like $twigItem defined by parameter $twigItem on line 464 can also be of type object<allejo\stakx\Docu...\TemplateReadyDocument>; however, allejo\stakx\Event\Compi...PageView::__construct() does only seem to accept object<allejo\stakx\Document\CollectableItem>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
479
        $this->eventDispatcher->dispatch(CompilerPostRenderDynamicPageView::NAME, $postEvent);
480
481
        return $postEvent->getCompiledOutput();
482
    }
483
484
    /**
485
     * Get the compiled HTML for a static PageView.
486
     *
487
     * @since  0.1.1
488
     *
489
     * @throws TemplateErrorInterface
490
     *
491
     * @return string
492
     */
493 10 View Code Duplication
    private function buildStaticPageViewHTML(StaticPageView &$pageView)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
494
    {
495
        $defaultContext = [
496 10
            'this' => $pageView->createJail(),
497
        ];
498
499 10
        $preEvent = new CompilerPreRenderStaticPageView($pageView);
500 10
        $this->eventDispatcher->dispatch(CompilerPreRenderStaticPageView::NAME, $preEvent);
501
502 10
        $context = array_merge($preEvent->getCustomVariables(), $defaultContext);
503
        $output = $this
504 10
            ->createTwigTemplate($pageView)
505 10
            ->render($context)
506
        ;
507
508 10
        $postEvent = new CompilerPostRenderStaticPageView($pageView, $output);
0 ignored issues
show
Compatibility introduced by
$pageView of type object<allejo\stakx\Document\BasePageView> is not a sub-type of object<allejo\stakx\Document\StaticPageView>. It seems like you assume a child class of the class allejo\stakx\Document\BasePageView to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
509 10
        $this->eventDispatcher->dispatch(CompilerPostRenderStaticPageView::NAME, $postEvent);
510
511 10
        return $postEvent->getCompiledOutput();
512
    }
513
514
    /**
515
     * Create a Twig template that just needs an array to render.
516
     *
517
     * @since  0.1.1
518
     *
519
     * @throws TemplateErrorInterface
520
     *
521
     * @return TemplateInterface
522
     */
523 12
    private function createTwigTemplate(BasePageView &$pageView)
524
    {
525
        try
526
        {
527 12
            $template = $this->templateBridge->createTemplate($pageView->getContent());
528
529 12
            $this->templateMapping[$template->getTemplateName()] = $pageView->getRelativeFilePath();
530
531 12
            $event = new CompilerTemplateCreation($pageView, $template, $this->theme);
532 12
            $this->eventDispatcher->dispatch(CompilerTemplateCreation::NAME, $event);
533
534 12
            return $template;
535
        }
536
        catch (TemplateErrorInterface $e)
537
        {
538
            $e
539
                ->setTemplateLine($e->getTemplateLine() + $pageView->getLineOffset())
540
                ->setContent($pageView->getContent())
541
                ->setName($pageView->getRelativeFilePath())
542
                ->setRelativeFilePath($pageView->getRelativeFilePath())
543
                ->buildException()
544
            ;
545
546
            throw $e;
547
        }
548
    }
549
}
550