Completed
Push — collection-position ( 391291...eb797c )
by Arnaud
02:14
created

Taxonomy::createTaxonomiesCollection()   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 Collection;
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 Collection */
25
    protected $taxonomyCollection;
26
27
    /**
28
     * {@inheritdoc}
29
     */
30
    public function generate(): void
31
    {
32
        if ($this->config->get('site.taxonomies')) {
33
            $this->createCollection();
34
            $this->collectTermsFromPages();
35
            $this->createTaxonomiesPages();
36
        }
37
    }
38
39
    /**
40
     * Create a collection from the vocabularies configuration.
41
     */
42
    protected function createCollection()
43
    {
44
        // create an empty "taxonomies" collection
45
        $this->taxonomyCollection = new Collection('taxonomies');
46
47
        // adds vocabularies collections
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->taxonomyCollection->add(new Vocabulary($vocabulary));
59
        }
60
    }
61
62
    /**
63
     * Collects taxonomies's terms from pages frontmatter.
64
     */
65
    protected function collectTermsFromPages()
66
    {
67
        /* @var $page Page */
68
        foreach ($this->pagesCollection as $page) {
69
            //foreach (array_keys($this->config->get('site.taxonomies')) as $plural) {
70
            foreach ($this->taxonomyCollection as $vocabularyCollection) {
71
                $plural = $vocabularyCollection->getId();
72
                /*
73
                 * ie:
74
                 *   tags: Tag 1, Tag 2
75
                 */
76
                if ($page->hasVariable($plural)) {
77
                    // converts a string list to an array
78
                    if (!is_array($page->getVariable($plural))) {
79
                        $page->setVariable($plural, [$page->getVariable($plural)]);
80
                    }
81
                    // adds each term to the vocabulary collection...
82
                    foreach ($page->getVariable($plural) as $term) {
83
                        $term = mb_strtolower($term);
84
                        $this->taxonomyCollection
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...
85
                            ->get($plural)
86
                            ->add(new Term($term));
87
                        // ... and adds page to the term collection
88
                        $this->taxonomyCollection
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...
89
                            ->get($plural)
90
                            ->get($term)
91
                            ->add($page);
92
                    }
93
                }
94
            }
95
        }
96
    }
97
98
    /**
99
     * Creates taxonomies pages.
100
     */
101
    protected function createTaxonomiesPages()
102
    {
103
        /* @var $vocabulary Vocabulary */
104
        foreach ($this->taxonomyCollection as $position => $vocabulary) {
105
            $plural = $vocabulary->getId();
106
            if (count($vocabulary) > 0) {
107
                /*
108
                 * Creates $plural/$term pages (list of pages)
109
                 * ie: /tags/tag-1/
110
                 */
111
                /* @var $pages PagesCollection */
112
                foreach ($vocabulary as $position => $term) {
113
                    $pages = $term;
114
                    $term = $term->getId();
115
                    $pages = $pages->sortByDate();
116
                    $pageId = $path = Page::slugify(sprintf('%s/%s', $plural, $term));
117
                    $page = (new Page($pageId))->setVariable('title', ucfirst($term));
118
                    if ($this->pagesCollection->has($pageId)) {
119
                        $page = clone $this->pagesCollection->get($pageId);
120
                    }
121
                    $date = $pages->first()->getVariable('date');
122
                    $page->setPath($path)
123
                        ->setType(Type::TAXONOMY)
124
                        ->setVariable('pages', $pages)
125
                        ->setVariable('date', $date)
126
                        ->setVariable('singular', $this->config->get('site.taxonomies')[$plural])
127
                        ->setVariable('pagination', ['pages' => $pages]);
128
                    $this->generatedPages->add($page);
129
                }
130
                /*
131
                 * Creates $plural pages (list of terms)
132
                 * ex: /tags/
133
                 */
134
                $page = (new Page(Page::slugify($plural)))
135
                    ->setPath(strtolower($plural))
136
                    ->setVariable('title', $plural)
137
                    ->setType(Type::TERMS)
138
                    ->setVariable('plural', $plural)
139
                    ->setVariable('singular', $this->config->get('site.taxonomies')[$plural])
140
                    ->setVariable('terms', $vocabulary)
141
                    ->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...
142
                // add page only if a template exist
143
                try {
144
                    $this->generatedPages->add($page);
145
                } catch (Exception $e) {
146
                    printf("%s\n", $e->getMessage());
147
                    // do not add page
148
                    unset($page);
149
                }
150
            }
151
        }
152
    }
153
}
154