Completed
Push — master ( 397eee...f113dc )
by Gaetano
22:13 queued 10:37
created

ContentVersionMatcher   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 212
Duplicated Lines 23.58 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 4.6%

Importance

Changes 0
Metric Value
wmc 38
lcom 1
cbo 8
dl 50
loc 212
ccs 4
cts 87
cp 0.046
rs 9.36
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A match() 0 11 2
B matchAnd() 22 22 6
A matchOne() 9 9 2
B matchContentVersions() 0 32 10
A matchOr() 19 19 5
A findContentVersionsByStatus() 0 13 4
B findContentVersionsByVersionNo() 0 26 6
A findAllContentVersions() 0 12 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\Matcher;
4
5
use eZ\Publish\API\Repository\Repository;
6
use eZ\Publish\API\Repository\Values\Content\Content;
7
use \eZ\Publish\API\Repository\Values\Content\VersionInfo;
8
use eZ\Publish\Core\Base\Exceptions\NotFoundException;
9
use Kaliop\eZMigrationBundle\API\MatcherInterface;
10
use Kaliop\eZMigrationBundle\API\Collection\VersionInfoCollection;
11
12
class ContentVersionMatcher extends RepositoryMatcher implements MatcherInterface
13
{
14
    const MATCH_STATUS_DRAFT = 'draft';
15
    const MATCH_STATUS_PUBLISHED = 'published';
16
    const MATCH_STATUS_ARCHIVED = 'archived';
17
18
    const MATCH_STATUS = 'version_status';
19
    const MATCH_VERSION = 'version';
20
21
    const STATUS_MAP = array(
22
        self::MATCH_STATUS_DRAFT => VersionInfo::STATUS_DRAFT,
23
        self::MATCH_STATUS_PUBLISHED => VersionInfo::STATUS_PUBLISHED,
24
        self::MATCH_STATUS_ARCHIVED => VersionInfo::STATUS_ARCHIVED
25
    );
26
27
    protected $allowedConditions = array(
28
        self::MATCH_ALL, self::MATCH_AND, self::MATCH_OR, self::MATCH_NOT,
29
        self::MATCH_STATUS, self::MATCH_VERSION,
30
        // aliases
31
        'status'
32
    );
33
    protected $returns = 'VersionInfo';
34
35
    protected $contentMatcher;
36
37 80
    public function __construct(Repository $repository, MatcherInterface $contentMatcher)
38
    {
39 80
        $this->repository = $repository;
40 80
        $this->contentMatcher = $contentMatcher;
41 80
    }
42
43
    public function match(array $contentConditions, array $versionConditions = array(), $sort = array(), $offset = 0, $limit = 0)
44
    {
45
        $versions = array();
46
47
        $contentCollection = $this->contentMatcher->match($contentConditions, $sort, $offset, $limit);
0 ignored issues
show
Unused Code introduced by
The call to MatcherInterface::match() has too many arguments starting with $sort.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
48
        foreach($contentCollection as $content) {
49
            $versions = array_merge($versions, $this->matchContentVersions($versionConditions, $content));
50
        }
51
52
        return new VersionInfoCollection($versions);
53
    }
54
55
    /**
56
     * Like match, but will throw an exception if there are 0 or more than 1 items matching
57
     *
58
     * @param array $contentConditions
59
     * @param array $versionConditions
60
     * @param array $sort
61
     * @param int $offset
62
     * @return mixed
63
     * @throws \Exception
64
     */
65 View Code Duplication
    public function matchOne(array $contentConditions, array $versionConditions = array(), $sort = array(), $offset = 0)
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...
66
    {
67
        $results = $this->match($contentConditions, $versionConditions, $sort, $offset, 2);
68
        $count = count($results);
69
        if ($count !== 1) {
70
            throw new \Exception("Found $count " . $this->returns . " when expected exactly only one to match the conditions");
71
        }
72
        return reset($results);
73
    }
74
75
    /**
76
     * @param array $versionConditions
77
     * @param Content $content
78
     * @return VersionInfo[] key: obj_id/version_no
79
     */
80
    public function matchContentVersions(array $versionConditions, Content $content)
81
    {
82
        $this->validateConditions($versionConditions);
83
84
        foreach ($versionConditions as $key => $values) {
85
86
            if (!is_array($values)) {
87
                $values = array($values);
88
            }
89
90
            switch ($key) {
91
                case 'status':
92
                case self::MATCH_STATUS:
93
                    return $this->findContentVersionsByStatus($content, $values);
94
95
                case self::MATCH_VERSION:
96
                    return $this->findContentVersionsByVersionNo($content, $values);
97
98
                case self::MATCH_ALL:
99
                    return $this->findAllContentVersions($content);
100
101
                case self::MATCH_AND:
102
                    return $this->matchAnd($values, $content);
103
104
                case self::MATCH_OR:
105
                    return $this->matchOr($values, $content);
106
107
                case self::MATCH_NOT:
108
                    return array_diff_key($this->findAllContentVersions($content), $this->matchContentVersions($values, $content));
109
            }
110
        }
111
    }
112
113 View Code Duplication
    protected function matchAnd($conditionsArray, $content = null)
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...
114
    {
115
        /// @todo introduce proper re-validation of all child conditions
116
        if (!is_array($conditionsArray) || !count($conditionsArray)) {
117
            throw new \Exception($this->returns . " can not be matched because no matching conditions found for 'and' clause.");
118
        }
119
120
        if (is_null($content)) {
121
            throw new \Exception($this->returns . " can not be matched because there was no content to match for 'and' clause.");
122
        }
123
124
        foreach ($conditionsArray as $conditions) {
125
            $out = $this->matchContentVersions($conditions, $content);
126
            if (!isset($results)) {
127
                $results = $out;
128
            } else {
129
                $results = array_intersect_key($results, $out);
130
            }
131
        }
132
133
        return $results;
0 ignored issues
show
Bug introduced by
The variable $results 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...
134
    }
135
136 View Code Duplication
    protected function matchOr($conditionsArray, $content = null)
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...
137
    {
138
        /// @todo introduce proper re-validation of all child conditions
139
        if (!is_array($conditionsArray) || !count($conditionsArray)) {
140
            throw new \Exception($this->returns . " can not be matched because no matching conditions found for 'or' clause.");
141
        }
142
143
        if (is_null($content)) {
144
            throw new \Exception($this->returns . " can not be matched because there was no content to match for 'or' clause.");
145
        }
146
147
        $results = array();
148
        foreach ($conditionsArray as $conditions) {
149
            $out = $this->matchContentVersions($conditions, $content);
150
            $results = array_replace($results, $out);
151
        }
152
153
        return $results;
154
    }
155
156
    /**
157
     * @param Content $content
158
     * @param string[] $values
159
     * @return VersionInfo[] key: obj_id/version_no, sorted in increasing version no.
160
     */
161
    protected function findContentVersionsByStatus(Content $content, array $values)
162
    {
163
        $versions = array();
164
        foreach ($this->findAllContentVersions($content) as $versionKey => $versionInfo) {
165
            foreach($values as $acceptedStatus) {
166
                if ($versionInfo->status == self::STATUS_MAP[$acceptedStatus]) {
167
                    $versions[$versionKey] = $versionInfo;
168
                    break;
169
                }
170
            }
171
        }
172
        return $versions;
173
    }
174
175
    /**
176
     * @param Content $content
177
     * @param int[] $values
178
     * @return VersionInfo[] key: obj_id/version_no, sorted in increasing version no.
179
     */
180
    protected function findContentVersionsByVersionNo(Content $content, array $values)
181
    {
182
        $versions = array();
183
        $contentVersions = $this->findAllContentVersions($content);
184
        $contentVersionsCount = count($contentVersions);
185
        $i = 0;
186
        foreach ($contentVersions as $versionKey => $versionInfo) {
187
            foreach($values as $acceptedVersionNo) {
188
                if ($acceptedVersionNo > 0 ) {
189
                    if ($acceptedVersionNo == $versionInfo->versionNo) {
190
                        $versions[$versionKey] = $versionInfo;
191
                        break;
192
                    }
193
                } else {
194
                    // negative $acceptedVersionNo means 'leave the last X versions', eg: -1 = leave the last version
195
                    if ($i < $contentVersionsCount + $acceptedVersionNo)  {
196
                        $versions[$versionKey] = $versionInfo;
197
                        break;
198
199
                    }
200
                }
201
            }
202
            $i++;
203
        }
204
        return $versions;
205
    }
206
207
    /**
208
     * @param Content $content
209
     * @return VersionInfo[] key: obj_id/version_no, sorted in increasing version no.
210
     */
211
    protected function findAllContentVersions(Content $content)
212
    {
213
        $contentVersions = $this->repository->getContentService()->loadVersions($content->contentInfo);
214
        // different eZ kernels apparently sort versions in different order...
215
        $sortedVersions = array();
216
        foreach($contentVersions as $versionInfo) {
217
            $sortedVersions[$content->contentInfo->id . '/' . $versionInfo->versionNo] = $versionInfo;
218
        }
219
        ksort($sortedVersions);
220
221
        return $sortedVersions;
222
    }
223
}
224