Completed
Push — master ( e385a5...4af18e )
by Gaetano
20s
created

ContentVersionMatcher::matchAnd()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 22

Duplication

Lines 22
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 22
loc 22
ccs 0
cts 10
cp 0
rs 8.9457
c 0
b 0
f 0
cc 6
nc 5
nop 2
crap 42
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 $conditions
0 ignored issues
show
Documentation introduced by
There is no parameter named $conditions. Did you maybe mean $contentConditions?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

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