Completed
Push — master ( 1ee05e...99144a )
by Gaetano
05:13
created

ContentVersionMatcher::matchOne()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 9
c 0
b 0
f 0
ccs 0
cts 6
cp 0
rs 9.9666
cc 2
nc 2
nop 4
crap 6
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, ContentMatcher $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);
48
        foreach($contentCollection as $content) {
0 ignored issues
show
Bug introduced by
The expression $contentCollection of type object<Kaliop\eZMigratio...ContentCollection>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
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)
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
        foreach ($conditionsArray as $conditions) {
118
            $out = $this->matchContentVersions($conditions, $content);
119
            if (!isset($results)) {
120
                $results = $out;
121
            } else {
122
                $results = array_intersect_key($results, $out);
123
            }
124
        }
125
126
        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...
127
    }
128
129 View Code Duplication
    protected function matchOr(array $conditionsArray, $content)
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...
130
    {
131
        /// @todo introduce proper re-validation of all child conditions
132
        if (!is_array($conditionsArray) || !count($conditionsArray)) {
133
            throw new \Exception($this->returns . " can not be matched because no matching conditions found for 'or' clause.");
134
        }
135
136
        $results = array();
137
        foreach ($conditionsArray as $conditions) {
138
            $out = $this->matchContentVersions($conditions, $content);
139
            $results = array_replace($results, $out);
140
        }
141
142
        return $results;
143
    }
144
145
    /**
146
     * @param Content $content
147
     * @param string[] $values
148
     * @return VersionInfo[] key: obj_id/version_no, sorted in increasing version no.
149
     */
150
    protected function findContentVersionsByStatus(Content $content, array $values)
151
    {
152
        $versions = array();
153
        foreach ($this->findAllContentVersions($content) as $versionKey => $versionInfo) {
154
            foreach($values as $acceptedStatus) {
155
                if ($versionInfo->status == self::STATUS_MAP[$acceptedStatus]) {
156
                    $versions[$versionKey] = $versionInfo;
157
                    break;
158
                }
159
            }
160
        }
161
        return $versions;
162
    }
163
164
    /**
165
     * @param Content $content
166
     * @param int[] $values
167
     * @return VersionInfo[] key: obj_id/version_no, sorted in increasing version no.
168
     */
169
    protected function findContentVersionsByVersionNo(Content $content, array $values)
170
    {
171
        $versions = array();
172
        $contentVersions = $this->findAllContentVersions($content);
173
        $contentVersionsCount = count($contentVersions);
174
        $i = 0;
175
        foreach ($contentVersions as $versionKey => $versionInfo) {
176
            foreach($values as $acceptedVersionNo) {
177
                if ($acceptedVersionNo > 0 ) {
178
                    if ($acceptedVersionNr == $versionInfo->versionNo) {
0 ignored issues
show
Bug introduced by
The variable $acceptedVersionNr does not exist. Did you mean $acceptedVersionNo?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
179
                        $versions[$versionKey] = $versionInfo;
180
                        break;
181
                    }
182
                } else {
183
                    // negative $acceptedVersionNo means 'leave the last X versions', eg: -1 = leave the last version
184
                    if ($i < $contentVersionsCount + $acceptedVersionNo)  {
185
                        $versions[$versionKey] = $versionInfo;
186
                        break;
187
188
                    }
189
                }
190
            }
191
            $i++;
192
        }
193
        return $versions;
194
    }
195
196
    /**
197
     * @param Content $content
198
     * @return VersionInfo[] key: obj_id/version_no, sorted in increasing version no.
199
     */
200
    protected function findAllContentVersions(Content $content)
201
    {
202
        $contentVersions = $this->repository->getContentService()->loadVersions($content->contentInfo);
203
        // different eZ kernels apparently sort versions in different order...
204
        $sortedVersions = array();
205
        foreach($contentVersions as $versionInfo) {
206
            $sortedVersions[$content->contentInfo->id . '/' . $versionInfo->versionNo] = $versionInfo;
207
        }
208
        ksort($sortedVersions);
209
210
        return $sortedVersions;
211
    }
212
}
213