Completed
Push — collection-position ( 265073...9f47c3 )
by Arnaud
02:11
created

Taxonomy::createVocabulariesCollection()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
cc 3
nc 3
nop 0
1
<?php
2
/*
3
 * Copyright (c) Arnaud Ligny <[email protected]>
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Cecil\Generator;
10
11
use Cecil\Collection\Page\Collection as PagesCollection;
12
use Cecil\Collection\Page\Page;
13
use Cecil\Collection\Page\Type;
14
use Cecil\Collection\Taxonomy\Collection as VocabulariesCollection;
15
use Cecil\Collection\Taxonomy\Term as Term;
16
use Cecil\Collection\Taxonomy\Vocabulary as Vocabulary;
17
use Cecil\Exception\Exception;
18
19
/**
20
 * Class Taxonomy.
21
 */
22
class Taxonomy extends AbstractGenerator implements GeneratorInterface
23
{
24
    /* @var VocabulariesCollection */
25
    protected $vocabulariesCollection;
26
27
    /**
28
     * {@inheritdoc}
29
     */
30
    public function generate(): void
31
    {
32
        if ($this->config->get('site.taxonomies')) {
33
            $this->createVocabulariesCollection();
34
            $this->collectTermsFromPages();
35
            $this->generateTaxonomiesPages();
36
        }
37
    }
38
39
    /**
40
     * Create a collection from the vocabularies configuration.
41
     */
42
    protected function createVocabulariesCollection()
43
    {
44
        // create an empty "taxonomies" collection
45
        $this->vocabulariesCollection = new VocabulariesCollection('taxonomies');
46
47
        // adds vocabularies to the collection
48
        foreach (array_keys($this->config->get('site.taxonomies')) as $vocabulary) {
49
            /*
50
             * ie:
51
             *   taxonomies:
52
             *     tags: disabled
53
             */
54
            if ($this->config->get("site.taxonomies.$vocabulary") == 'disabled') {
55
                continue;
56
            }
57
58
            $this->vocabulariesCollection->add(new Vocabulary($vocabulary));
59
        }
60
    }
61
62
    /**
63
     * Collects vocabularies/terms from pages frontmatter.
64
     */
65
    protected function collectTermsFromPages()
66
    {
67
        /* @var $page Page */
68
        foreach ($this->pagesCollection as $page) {
69
            foreach ($this->vocabulariesCollection as $vocabulary) {
70
                $plural = $vocabulary->getId();
71
                /*
72
                 * ie:
73
                 *   tags: Tag 1, Tag 2
74
                 */
75
                if ($page->hasVariable($plural)) {
76
                    // converts a string list to an array
77
                    if (!is_array($page->getVariable($plural))) {
78
                        $page->setVariable($plural, [$page->getVariable($plural)]);
79
                    }
80
                    // adds each term to the vocabulary collection...
81
                    foreach ($page->getVariable($plural) as $term) {
82
                        $term = mb_strtolower($term);
83
                        $this->vocabulariesCollection
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cecil\Collection\ItemInterface as the method add() does only exist in the following implementations of said interface: Cecil\Collection\Menu\Menu, Cecil\Collection\Taxonomy\Term, Cecil\Collection\Taxonomy\Vocabulary.

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...
84
                            ->get($plural)
85
                            ->add(new Term($term));
86
                        // ... and adds page to the term collection
87
                        $this->vocabulariesCollection
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Cecil\Collection\ItemInterface as the method get() does only exist in the following implementations of said interface: Cecil\Collection\Menu\Menu, Cecil\Collection\Taxonomy\Term, Cecil\Collection\Taxonomy\Vocabulary.

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...
88
                            ->get($plural)
89
                            ->get($term)
90
                            ->add($page);
91
                    }
92
                }
93
            }
94
        }
95
    }
96
97
    /**
98
     * Generate taxonomies pages.
99
     */
100
    protected function generateTaxonomiesPages()
101
    {
102
        /* @var $vocabulary Vocabulary */
103
        foreach ($this->vocabulariesCollection as $position => $vocabulary) {
104
            $plural = $vocabulary->getId();
105
            if (count($vocabulary) > 0) {
106
                /*
107
                 * Creates $plural/$term pages (list of pages)
108
                 * ie: /tags/tag-1/
109
                 */
110
                /* @var $pages PagesCollection */
111
                foreach ($vocabulary as $position => $term) {
112
                    $pages = $term->sortByDate();
113
                    $pageId = $path = Page::slugify(sprintf('%s/%s', $plural, $term->getId()));
114
                    $page = (new Page($pageId))->setVariable('title', ucfirst($term->getId()));
115
                    if ($this->pagesCollection->has($pageId)) {
116
                        $page = clone $this->pagesCollection->get($pageId);
117
                    }
118
                    $date = $pages->first()->getVariable('date');
119
                    $page->setPath($path)
120
                        ->setType(Type::TAXONOMY_VOCABULARY)
121
                        ->setVariable('pages', $pages)
122
                        ->setVariable('date', $date)
123
                        ->setVariable('singular', $this->config->get("site.taxonomies.$plural"))
124
                        ->setVariable('pagination', ['pages' => $pages]);
125
                    $this->generatedPages->add($page);
126
                }
127
                /*
128
                 * Creates $plural pages (list of terms)
129
                 * ex: /tags/
130
                 */
131
                $page = (new Page(Page::slugify($plural)))
132
                    ->setPath(strtolower($plural))
133
                    ->setVariable('title', $plural)
134
                    ->setType(Type::TAXONOMY_TERMS)
135
                    ->setVariable('plural', $plural)
136
                    ->setVariable('singular', $this->config->get("site.taxonomies.$plural"))
137
                    ->setVariable('terms', $vocabulary)
138
                    ->setVariable('date', $date);
0 ignored issues
show
Bug introduced by
The variable $date does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
139
                // add page only if a template exist
140
                try {
141
                    $this->generatedPages->add($page);
142
                } catch (Exception $e) {
143
                    printf("%s\n", $e->getMessage());
144
                    unset($page); // do not add page
145
                }
146
            }
147
        }
148
    }
149
}
150