Passed
Branch main (a5ccba)
by Gaetano
04:40
created

expectedResultsCount()   C

Complexity

Conditions 16
Paths 13

Size

Total Lines 40
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 19.5389

Importance

Changes 0
Metric Value
cc 16
eloc 26
c 0
b 0
f 0
nc 13
nop 1
dl 0
loc 40
ccs 19
cts 25
cp 0.76
crap 19.5389
rs 5.5666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Kaliop\eZMigrationBundle\Core\Executor;
4
5
use Kaliop\eZMigrationBundle\API\Collection\AbstractCollection;
6
use Kaliop\eZMigrationBundle\API\Exception\InvalidMatchResultsNumberException;
7
use Kaliop\eZMigrationBundle\API\Exception\InvalidStepDefinitionException;
8
use Kaliop\eZMigrationBundle\API\Value\MigrationStep;
9
10
/**
11
 * A trait used by Executors which allow to set non-scalar values to references.
12
 * ATM besides scalars we support arrays and collections as reference values
13
 */
14
trait NonScalarReferenceSetterTrait
15
{
16
    // traits can not have constants...
17
18
    static $RESULT_TYPE_SINGLE = 1;
19
    static $RESULT_TYPE_MULTIPLE = 2;
20
    static $RESULT_TYPE_ANY = 3;
21
22
    static $EXPECT_NONE = 'none';
23
    static $EXPECT_ONE = 'one';
24
    static $EXPECT_ANY = 'any';
25
    static $EXPECT_MANY = 'many';
26
    static $EXPECT_UNSPECIFIED = 'unspecified';
27
28
    /**
29
     * @param mixed $results
30
     * @param MigrationStep $step
31
     * @throws InvalidMatchResultsNumberException
32
     * @throws InvalidStepDefinitionException
33
     */
34 35
    protected function validateResultsCount($results, $step)
35
    {
36
        // q: what if we get a scalar result but we expect an array/collection ?
37
        // q2: why not just check for Countable interface instead of AbstractCollection? Or at least allow ArrayIterators and ObjectIterators
38 35
        if (is_array($results) || $results instanceof AbstractCollection) {
39 35
            $expectedResultsCount = $this->expectedResultsCount($step);
40
            switch ($expectedResultsCount) {
41 33
                case self::$EXPECT_UNSPECIFIED:
42 20
                case self::$EXPECT_ANY:
43 27
                    break;
44 20
                case self::$EXPECT_MANY:
45 4
                    if (count($results) == 0) {
46 2
                        throw new InvalidMatchResultsNumberException($this->getSelfName() . ' found no matching ' . $this->getResultsName($results) . 's but expects at least one');
47
                    }
48 2
                    break;
49
                default:
50 16
                    $count = count($results);
51 16
                    if ($count != $expectedResultsCount) {
52 3
                        throw new InvalidMatchResultsNumberException($this->getSelfName() . " found $count matching " . $this->getResultsName($results) . "s but expects $expectedResultsCount");
53
                    }
54
            }
55
        }
56 28
    }
57
58
    /**
59
     * @param MigrationStep $step
60
     * @return int
61
     * @throws InvalidStepDefinitionException
62
     */
63 30
    protected function expectedResultsType($step)
64
    {
65 30
        switch ($this->expectedResultsCount($step)) {
66 30
            case 1:
67 25
                return self::$RESULT_TYPE_SINGLE;
68 11
            case 0:
69
                // note: we consider '0 results' as a specific case of a multi-valued result set; we might want to
70
                // to give it its own RESULT_TYPE...
71 11
                return self::$RESULT_TYPE_MULTIPLE;
72
            case self::$EXPECT_UNSPECIFIED:
73
                return self::$RESULT_TYPE_ANY;
74
            default: // any, many, 2..N
75
                return self::$RESULT_TYPE_MULTIPLE;
76
        }
77
    }
78
79
    /**
80
     * @param MigrationStep $step
81
     * @return int|string 'unspecified', 'any', 'many' or a number 0..PHP_MAX_INT
82
     * @throws InvalidStepDefinitionException
83
     */
84 42
    protected function expectedResultsCount($step)
85
    {
86 42
        if (isset($step->dsl['expect'])) {
87 8
            switch ($step->dsl['expect']) {
88 8
                case self::$EXPECT_NONE:
89 8
                case '0':
90 1
                    return 0;
91 8
                case self::$EXPECT_ONE:
92 5
                case '1':
93 5
                    return 1;
94 4
                case self::$EXPECT_ANY:
95 4
                case self::$EXPECT_MANY:
96 4
                    return $step->dsl['expect'];
97
                default:
98
                    if ((is_int($step->dsl['expect']) || ctype_digit($step->dsl['expect'])) && $step->dsl['expect'] >= 0) {
99
                        return (int)$step->dsl['expect'];
100
                    }
101
                    throw new InvalidStepDefinitionException("Invalid value for 'expect element: {$step->dsl['expect']}");
102
            }
103
        }
104
105
        // BC
106 36
        if (isset($step->dsl['references_type'])) {
107 1
            switch ($step->dsl['references_type']) {
108 1
                case 'array':
109 1
                    return self::$EXPECT_ANY;
110
                case 'scalar':
111
                    return 1;
112
                default:
113
                    throw new InvalidStepDefinitionException('Unexpected value for references_type element: ' . $step->dsl['references_type']);
114
            }
115
        }
116
117
        // if there are references to set, except the always_scalar ones, then we want a single result
118
        // (unless the user told us so via the 'expect' tag)
119 35
        if (isset($step->dsl['references']) && $this->hasNonScalarReferences($step->dsl['references'])) {
120 25
            return 1;
121
        }
122
123 25
        return self::$EXPECT_UNSPECIFIED;
124
    }
125
126
    /**
127
     * @param array $referencesDefinition
128
     * @return bool
129
     * @throws InvalidStepDefinitionException
130
     */
131 30
    protected function hasNonScalarReferences($referencesDefinition)
132
    {
133 30
        foreach ($referencesDefinition as $key => $referenceDefinition) {
134 30
            $referenceDefinition = $this->parseReferenceDefinition($key, $referenceDefinition);
0 ignored issues
show
Bug introduced by
It seems like parseReferenceDefinition() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

134
            /** @scrutinizer ignore-call */ 
135
            $referenceDefinition = $this->parseReferenceDefinition($key, $referenceDefinition);
Loading history...
135 28
            if (!$this->isScalarReference($referenceDefinition))
136
            {
137 25
                return true;
138
            }
139
        }
140
141 8
        return false;
142
    }
143
144
    /**
145
     * @param array $referenceDefinition
146
     * @return bool
147
     */
148
    protected abstract function isScalarReference($referenceDefinition);
149
150
    /**
151
     * Verifies compatibility between the definition of the references to be set and the data set to extract them from.
152
     * NB: for multivalued/array refs, we assume that the users by default expect at least one value.
153
     * NB: for scalar results we do not validate anything, as they are always valid (we do not coerce them to arrays)
154
     * @param AbstractCollection|array|mixed $results
155
     * @param array $referencesDefinition
156
     * @param MigrationStep $step
157
     * @return void throws when incompatibility is found
158
     * @todo we should encapsulate the whole info about refs to be set in a single data structure, instead of poking inside $step...
159
     * @deprecated
160
     */
161
    /*protected function insureResultsCountCompatibility($results, $referencesDefinition, $step)
162
    {
163
        if (!$this->hasNonScalarReferences($referencesDefinition)) {
164
            return;
165
        }
166
167
        if (is_array($results) || $results instanceof AbstractCollection) {
168
            if (count($results) > 1 && !$this->allowMultipleResults($step)) {
169
                throw new \InvalidArgumentException($this->getSelfName() . ' does not support setting references for multiple ' . $this->getResultsName($results) . 's');
170
            }
171
            if (count($results) == 0 && !$this->allowEmptyResults($step)) {
172
                throw new \InvalidArgumentException($this->getSelfName() . ' does not support setting references for no ' . $this->getResultsName($results) . 's');
173
            }
174
        }
175
    }*/
176
177
    /**
178
     * @return string
179
     */
180 5
    protected function getSelfName()
181
    {
182 5
        $className = get_class($this);
183 5
        $array = explode('\\', $className);
184 5
        $className = end($array);
185
        // CamelCase to Camel Case using negative look-behind in regexp
186 5
        return preg_replace('/(?<!^)[A-Z]/', ' $0', $className);
187
    }
188
189
    /**
190
     * @param array|object $collection
191
     * @return string
192
     */
193 5
    protected function getResultsName($collection)
194
    {
195 5
        if (is_array($collection)) {
196 1
            return 'result';
197
        }
198
199 4
        $className = get_class($collection);
200 4
        $array = explode('\\', $className);
201 4
        $className = str_replace('Collection', '', end($array));
202
        // CamelCase to snake case using negative look-behind in regexp
203 4
        return strtolower(preg_replace('/(?<!^)[A-Z]/', ' $0', $className));
204
    }
205
}
206