Completed
Push — include-lib ( fd24a6...4173d3 )
by Arnaud
13:24
created

Taxonomy::createNodePages()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 50

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 50
rs 8.4686
c 0
b 0
f 0
cc 6
nc 8
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\Collection as PageCollection;
12
use Cecil\Collection\Page\Page;
13
use Cecil\Collection\Taxonomy\Collection as TaxonomyCollection;
14
use Cecil\Collection\Taxonomy\Term as Term;
15
use Cecil\Collection\Taxonomy\Vocabulary as Vocabulary;
16
use Cecil\Exception\Exception;
17
use Cecil\Page\NodeType;
18
19
/**
20
 * Class Taxonomy.
21
 */
22
class Taxonomy extends AbstractGenerator implements GeneratorInterface
23
{
24
    /* @var TaxonomyCollection */
25
    protected $taxonomies;
26
    /* @var PageCollection */
27
    protected $pageCollection;
28
    /* @var PageCollection */
29
    protected $generatedPages;
30
31
    /**
32
     * {@inheritdoc}
33
     */
34
    public function generate(PageCollection $pageCollection, \Closure $messageCallback)
35
    {
36
        $this->pageCollection = $pageCollection;
37
        $this->generatedPages = new PageCollection();
38
39
        if ($this->config->get('site.taxonomies')) {
40
            // is taxonomies disabled
41
            if ($this->config->get('site.taxonomies.disabled')) {
42
                return $this->generatedPages;
43
            }
44
45
            // prepares taxonomies collection
46
            $this->taxonomies = new TaxonomyCollection('taxonomies');
47
            // adds each vocabulary collection to the taxonomies collection
48
            foreach ($this->config->get('site.taxonomies') as $vocabulary) {
49
                if ($vocabulary != 'disable') {
50
                    $this->taxonomies->add(new Vocabulary($vocabulary));
51
                }
52
            }
53
54
            // collects taxonomies from pages
55
            $this->collectTaxonomiesFromPages();
56
57
            // creates node pages
58
            $this->createNodePages();
59
        }
60
61
        return $this->generatedPages;
62
    }
63
64
    /**
65
     * Collects taxonomies from pages.
66
     */
67
    protected function collectTaxonomiesFromPages()
68
    {
69
        /* @var $page Page */
70
        foreach ($this->pageCollection as $page) {
71
            foreach (array_keys($this->config->get('site.taxonomies')) as $plural) {
72
                if (isset($page[$plural])) {
73
                    // converts a list to an array if necessary
74
                    if (!is_array($page[$plural])) {
75
                        $page->setVariable($plural, [$page[$plural]]);
76
                    }
77
                    foreach ($page[$plural] as $term) {
78
                        // adds each terms to the vocabulary collection
79
                        $this->taxonomies->get($plural)
80
                            ->add(new Term($term));
81
                        // adds each pages to the term collection
82
                        $this->taxonomies
83
                            ->get($plural)
84
                            ->get($term)
85
                            ->add($page);
86
                    }
87
                }
88
            }
89
        }
90
    }
91
92
    /**
93
     * Creates node pages.
94
     */
95
    protected function createNodePages()
96
    {
97
        /* @var $terms Vocabulary */
98
        foreach ($this->taxonomies as $plural => $terms) {
99
            if (count($terms) > 0) {
100
                /*
101
                 * Creates $plural/$term pages (list of pages)
102
                 * ex: /tags/tag-1/
103
                 */
104
                /* @var $pages PageCollection */
105
                foreach ($terms as $term => $pages) {
106
                    if (!$this->pageCollection->has($term)) {
107
                        $pages = $pages->sortByDate()->toArray();
108
                        $page = (new Page())
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Cecil\Collection\Item as the method setPathname() does only exist in the following sub-classes of Cecil\Collection\Item: Cecil\Collection\Page\Page. 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...
109
                            ->setId(Page::urlize(sprintf('%s/%s/', $plural, $term)))
110
                            ->setPathname(Page::urlize(sprintf('%s/%s', $plural, $term)))
111
                            ->setTitle(ucfirst($term))
112
                            ->setNodeType(NodeType::TAXONOMY)
113
                            ->setVariable('pages', $pages)
114
                            ->setVariable('date', $date = reset($pages)->getDate())
115
                            ->setVariable('singular', $this->config->get('site.taxonomies')[$plural])
116
                            ->setVariable('pagination', ['pages' => $pages]);
117
                        $this->generatedPages->add($page);
118
                    }
119
                }
120
                /*
121
                 * Creates $plural pages (list of terms)
122
                 * ex: /tags/
123
                 */
124
                $page = (new Page())
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Cecil\Collection\Item as the method setPathname() does only exist in the following sub-classes of Cecil\Collection\Item: Cecil\Collection\Page\Page. 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...
125
                    ->setId(Page::urlize($plural))
126
                    ->setPathname(strtolower($plural))
127
                    ->setTitle($plural)
128
                    ->setNodeType(NodeType::TERMS)
129
                    ->setVariable('plural', $plural)
130
                    ->setVariable('singular', $this->config->get('site.taxonomies')[$plural])
131
                    ->setVariable('terms', $terms)
132
                    ->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...
133
134
                // add page only if a template exist
135
                try {
136
                    $this->generatedPages->add($page);
137
                } catch (Exception $e) {
138
                    echo $e->getMessage()."\n";
139
                    // do not add page
140
                    unset($page);
141
                }
142
            }
143
        }
144
    }
145
}
146