Passed
Push — master ( 77a6e6...0cc661 )
by Gaetano
08:48
created

)   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 1
b 0
f 0
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
    protected function validateResultsCount($results, $step)
35
    {
36
        // q: what if we get a scalar result but we expect an array/collection ?
37
        if (is_array($results) || $results instanceof AbstractCollection) {
38
            $expectedResultsCount = $this->expectedResultsCount($step);
39
            switch($expectedResultsCount) {
40
                case self::$EXPECT_UNSPECIFIED:
41
                case self::$EXPECT_ANY:
42
                    break;
43
                case self::$EXPECT_MANY:
44
                    if (count($results) == 0) {
45
                        throw new InvalidMatchResultsNumberException($this->getSelfName() . ' found no matching ' . $this->getResultsName($results) . 's but expects at least one');
46
                    }
47
                    break;
48
                default:
49
                    $count = count($results);
50
                    if ($count != $expectedResultsCount) {
51
                        throw new InvalidMatchResultsNumberException($this->getSelfName() . " found $count matching " . $this->getResultsName($results) . "s but expects $expectedResultsCount");
52
                    }
53
            }
54
        }
55
    }
56
57
    /**
58
     * @param MigrationStep $step
59
     * @return int
60
     * @throws InvalidStepDefinitionException
61
     */
62
    protected function expectedResultsType($step)
63
    {
64
        switch($this->expectedResultsCount($step)) {
65
            case 1:
66
                return self::$RESULT_TYPE_SINGLE;
67
            case 0:
68
                // note: we consider '0 results' as a specific case of a multi-valued result set; we might want to
69
                // to give it its own RESULT_TYPE...
70
                return self::$RESULT_TYPE_MULTIPLE;
71
            case self::$EXPECT_UNSPECIFIED:
72
                return self::$RESULT_TYPE_ANY;
73
            default: // any, many, 2..N
74
                return self::$RESULT_TYPE_MULTIPLE;
75
        }
76
    }
77
78
    /**
79
     * @param MigrationStep $step
80
     * @return int|string 'unspecified', 'any', 'many' or a number 0..PHP_MAX_INT
81
     * @throws InvalidStepDefinitionException
82
     */
83
    protected function expectedResultsCount($step)
84
    {
85
        if (isset($step->dsl['expect'])) {
86
            switch ($step->dsl['expect']) {
87
                case self::$EXPECT_NONE:
88
                case '0':
89
                    return 0;
90
                case self::$EXPECT_ONE:
91
                case '1':
92
                    return 1;
93
                case self::$EXPECT_ANY:
94
                case self::$EXPECT_MANY:
95
                    return $step->dsl['expect'];
96
                default:
97
                    if ((is_int($step->dsl['expect']) || ctype_digit($step->dsl['expect'])) && $step->dsl['expect'] >= 0) {
98
                        return (int)$step->dsl['expect'];
99
                    }
100
                    throw new InvalidStepDefinitionException("Invalid value for 'expect element: {$step->dsl['expect']}");
101
            }
102
        }
103
104
        // BC
105
        if (isset($step->dsl['references_type'])) {
106
            switch($step->dsl['references_type']) {
107
                case 'array':
108
                    return self::$EXPECT_ANY;
109
                case 'scalar':
110
                    return 1;
111
                default:
112
                    throw new InvalidStepDefinitionException('Unexpected value for references_type element: ' . $step->dsl['references_type']);
113
            }
114
        }
115
116
        // if there are references to set, except the always_scalar ones, then we want a single result
117
        // (unless the user told us so via the 'expect' tag)
118
        if (isset($step->dsl['references']) && $this->hasNonScalarReferences($step->dsl['references'])) {
119
            return 1;
120
        }
121
122
        return self::$EXPECT_UNSPECIFIED;
123
    }
124
125
    /**
126
     * @param array $referencesDefinition
127
     * @return bool
128
     */
129
    protected function hasNonScalarReferences($referencesDefinition)
130
    {
131
        foreach($referencesDefinition as $referenceDefinition) {
132
            if (!$this->isScalarReference($referenceDefinition))
133
            {
134
                return true;
135
            }
136
        }
137
138
        return false;
139
    }
140
141
    /**
142
     * @param array $referenceDefinition
143
     * @return bool
144
     */
145
    protected abstract function isScalarReference($referenceDefinition);
146
147
    /**
148
     * Verifies compatibility between the definition of the references to be set and the data set to extract them from.
149
     * NB: for multivalued/array refs, we assume that the users by default expect at least one value.
150
     * NB: for scalar results we do not validate anything, as they are always valid (we do not coerce them to arrays)
151
     * @param AbstractCollection|array|mixed $results
152
     * @param array $referencesDefinition
153
     * @param MigrationStep $step
154
     * @return void throws when incompatibility is found
155
     * @todo we should encapsulate the whole info about refs to be set in a single data structure, instead of poking inside $step...
156
     * @deprecated
157
     */
158
    /*protected function insureResultsCountCompatibility($results, $referencesDefinition, $step)
159
    {
160
        if (!$this->hasNonScalarReferences($referencesDefinition)) {
161
            return;
162
        }
163
164
        if (is_array($results) || $results instanceof AbstractCollection) {
165
            if (count($results) > 1 && !$this->allowMultipleResults($step)) {
166
                throw new \InvalidArgumentException($this->getSelfName() . ' does not support setting references for multiple ' . $this->getResultsName($results) . 's');
167
            }
168
            if (count($results) == 0 && !$this->allowEmptyResults($step)) {
169
                throw new \InvalidArgumentException($this->getSelfName() . ' does not support setting references for no ' . $this->getResultsName($results) . 's');
170
            }
171
        }
172
    }*/
173
174
    /**
175
     * @return string
176
     */
177
    protected function getSelfName()
178
    {
179
        $className = get_class($this);
180
        $array = explode('\\', $className);
181
        $className = end($array);
182
        // CamelCase to Camel Case using negative look-behind in regexp
183
        return preg_replace('/(?<!^)[A-Z]/', ' $0', $className);
184
    }
185
186
    /**
187
     * @param array|object $collection
188
     * @return string
189
     */
190
    protected function getResultsName($collection)
191
    {
192
        if (is_array($collection)) {
193
            return 'result';
194
        }
195
196
        $className = get_class($collection);
197
        $array = explode('\\', $className);
198
        $className = str_replace('Collection', '', end($array));
199
        // CamelCase to snake case using negative look-behind in regexp
200
        return strtolower(preg_replace('/(?<!^)[A-Z]/', ' $0', $className));
201
    }
202
}
203