Completed
Push — ezp-30928-as_a_developer_i_wan... ( 4d81da...f06b29 )
by
unknown
14:36
created

Criterion::createFromQueryBuilder()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the eZ\Publish\API\Repository\Values\Content\Query\Criterion class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\API\Repository\Values\Content\Query;
10
11
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value;
12
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications;
13
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator;
14
use InvalidArgumentException;
15
16
abstract class Criterion implements CriterionInterface
17
{
18
    /**
19
     * The operator used by the Criterion.
20
     *
21
     * @var string
22
     */
23
    public $operator;
24
25
    /**
26
     * The value(s) matched by the criteria.
27
     *
28
     * @var array(int|string)
29
     */
30
    public $value;
31
32
    /**
33
     * The target used by the criteria (field, metadata...).
34
     *
35
     * @var string
36
     */
37
    public $target;
38
39
    /**
40
     * Additional value data, required by some criterions, MapLocationDistance for instance.
41
     *
42
     * @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value
43
     */
44
    public $valueData;
45
46
    /**
47
     * Performs operator validation based on the Criterion specifications returned by {@see getSpecifications()}.
48
     *
49
     * @param string|null $target The target the Criterion applies to: metadata identifier, field identifier...
50
     * @param string|null $operator
51
     *        The operator the Criterion uses. If null is given, will default to Operator::IN if $value is an array,
52
     *        Operator::EQ if it is not.
53
     * @param string[]|int[]|int|string $value
54
     * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value $valueData
55
     *
56
     * @todo Add a dedicated exception
57
     *
58
     * @throws \InvalidArgumentException if the provided operator isn't supported
59
     */
60
    public function __construct($target, $operator, $value, Value $valueData = null)
61
    {
62
        if ($operator === null) {
63
            $operator = is_array($value) ? Operator::IN : Operator::EQ;
64
        }
65
66
        $operatorFound = false;
67
68
        // we loop on each specified operator.
69
        // If the provided operator ain't found, an exception will be thrown at the end
70
        foreach ($this->getSpecifications() as $operatorSpecifications) {
71
            if ($operatorSpecifications->operator != $operator) {
72
                continue;
73
            }
74
            $operatorFound = true;
75
76
            // input format check (single/array)
77
            switch ($operatorSpecifications->valueFormat) {
78
                case Specifications::FORMAT_SINGLE:
79
                    if (is_array($value)) {
80
                        throw new InvalidArgumentException('The Criterion expects a single value');
81
                    }
82
                    break;
83
84
                case Specifications::FORMAT_ARRAY:
85
                    if (!is_array($value)) {
86
                        throw new InvalidArgumentException('The criterion expects an array of values');
87
                    }
88
                    break;
89
            }
90
91
            // input value check
92
            if ($operatorSpecifications->valueTypes !== null) {
93
                $callback = $this->getValueTypeCheckCallback($operatorSpecifications->valueTypes);
94
                if (!is_array($value)) {
95
                    $value = [$value];
96
                }
97
                foreach ($value as $item) {
98
                    if ($callback($item) === false) {
99
                        throw new InvalidArgumentException('Unsupported value (' . gettype($item) . ")$item");
100
                    }
101
                }
102
            }
103
        }
104
105
        // Operator wasn't found in the criterion specifications
106
        if ($operatorFound === false) {
107
            throw new InvalidArgumentException("Operator $operator isn't supported by the Criterion " . get_class($this));
108
        }
109
110
        $this->operator = $operator;
111
        $this->value = $value;
112
        $this->target = $target;
113
        $this->valueData = $valueData;
114
    }
115
116
    /**
117
     * Criterion description function.
118
     *
119
     * Returns the combination of the Criterion's supported operator/value,
120
     * as an array of eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications objects
121
     * - Operator is one supported Operator, as an Operator::* constant
122
     * - ValueType is the type of input value this operator requires, either array or single
123
     * - SupportedTypes is an array of types the operator will accept
124
     * - ValueCountLimitation is an integer saying how many values are expected.
125
     *
126
     * <code>
127
     * // IN and EQ are supported
128
     * return [
129
     *     // The EQ operator expects a single value, either as an integer or a string
130
     *     new Specifications(
131
     *         Operator::EQ,
132
     *         Specifications::INPUT_TYPE_SINGLE,
133
     *         [Specifications::INPUT_VALUE_INTEGER, Specifications::INPUT_VALUE_STRING],
134
     *     ),
135
     *     // The IN operator expects an array of values, of either integers or strings
136
     *     new Specifications(
137
     *         Operator::IN,
138
     *         Specifications::INPUT_TYPE_ARRAY,
139
     *         [Specifications::INPUT_VALUE_INTEGER, Specifications::INPUT_VALUE_STRING]
140
     *     )
141
     * ]
142
     * </code>
143
     *
144
     * @return \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications[]
145
     */
146
    abstract public function getSpecifications();
147
148
    /**
149
     * Returns a callback that checks the values types depending on the operator specifications.
150
     *
151
     * @param int $valueTypes The accepted values, as a bit field of Specifications::TYPE_* constants
152
     *
153
     * @return \Closure
154
     */
155
    private function getValueTypeCheckCallback($valueTypes)
156
    {
157
        $callback = function ($value) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
158
            return false;
159
        };
160
161
        // the callback code will return true as soon as an accepted value type is found
162 View Code Duplication
        if ($valueTypes & Specifications::TYPE_INTEGER) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
163
            $callback = function ($value) use ($callback) {
164
                return is_numeric($value) || $callback($value);
165
            };
166
        }
167 View Code Duplication
        if ($valueTypes & Specifications::TYPE_STRING) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
168
            $callback = function ($value) use ($callback) {
169
                return is_string($value) || $callback($value);
170
            };
171
        }
172 View Code Duplication
        if ($valueTypes & Specifications::TYPE_BOOLEAN) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
173
            $callback = function ($value) use ($callback) {
174
                return is_bool($value) || $callback($value);
175
            };
176
        }
177
178
        return $callback;
179
    }
180
181
    /**
182
     * @deprecated since 7.2, will be removed in 8.0. Use the constructor directly instead.
183
     */
184
    public static function createFromQueryBuilder($target, $operator, $value)
185
    {
186
        @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 7.2 and will be removed in 8.0.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
187
188
        return new static($target, $operator, $value);
189
    }
190
}
191