-
Notifications
You must be signed in to change notification settings - Fork 690
Expand file tree
/
Copy pathConditions.php
More file actions
133 lines (119 loc) · 4.87 KB
/
Conditions.php
File metadata and controls
133 lines (119 loc) · 4.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/
namespace craft\services;
use Craft;
use craft\base\conditions\ConditionInterface;
use craft\base\conditions\ConditionRuleInterface;
use craft\helpers\ArrayHelper;
use craft\helpers\Json;
use ReflectionException;
use ReflectionProperty;
use yii\base\Component;
use yii\base\InvalidArgumentException;
use yii\base\InvalidConfigException;
/**
* The Conditions service provides APIs for managing conditions.
*
* An instance of the Conditions service is globally accessible in Craft via [[\craft\base\ApplicationTrait::getConditions()|`Craft::$app->getConditions()`]].
*
* @author Pixel & Tonic, Inc. <[email protected]>
* @since 4.0.0
*/
class Conditions extends Component
{
/**
* Creates a condition instance.
*
* @template T of ConditionInterface
* @param array|class-string<T> $config The condition class or configuration array
* @phpstan-param array{class:class-string<T>}|class-string<T> $config
* @return T
* @throws InvalidArgumentException if the condition does not implement [[ConditionInterface]]
* @throws InvalidConfigException
*/
public function createCondition(array|string $config): ConditionInterface
{
if (is_string($config)) {
$class = $config;
$config = [];
} else {
$class = ArrayHelper::remove($config, 'class');
}
if (!is_subclass_of($class, ConditionInterface::class)) {
throw new InvalidArgumentException("Invalid condition class: $class");
}
// The base config will be JSON-encoded within a `config` key if this came from a condition builder
if (isset($config['config']) && Json::isJsonObject($config['config'])) {
$config = array_merge(
Json::decode(ArrayHelper::remove($config, 'config')),
$config
);
}
// Set condition rules last, in case any available rules are dependent on the condition config
$rules = ArrayHelper::remove($config, 'conditionRules', []);
/** @var ConditionInterface */
return Craft::createObject([
'class' => $class,
'attributes' => $config,
'conditionRules' => $rules,
]);
}
/**
* Creates a condition rule instance.
*
* @param array|string $config The condition class or configuration array
* @phpstan-param array{class: string}|array{type:string}|string $config The condition class or configuration array
* @return ConditionRuleInterface
* @throws InvalidArgumentException if the condition rule does not implement [[ConditionRuleInterface]]
*/
public function createConditionRule(array|string $config): ConditionRuleInterface
{
if (is_string($config)) {
$class = $config;
$config = [];
} else {
$class = ArrayHelper::remove($config, 'class');
// Merge `type` in, if this is coming from a condition builder
if (isset($config['type'])) {
$newConfig = Json::decodeIfJson(ArrayHelper::remove($config, 'type'));
if (is_string($newConfig)) {
$newClass = $newConfig;
$newConfig = [];
} else {
$newClass = ArrayHelper::remove($newConfig, 'class');
}
// Make sure the condition is being passed to the condition rule when it's being constructed
if (isset($config['condition'])) {
$newConfig['condition'] = $config['condition'];
}
// Is the type changing?
if ($class !== null && $newClass !== $class) {
// Remove any config attributes that aren't defined by the same class between both types
$config = array_filter($config, function($attribute) use ($class, $newClass) {
try {
$r1 = new ReflectionProperty($class, $attribute);
$r2 = new ReflectionProperty($newClass, $attribute);
return $r1->getDeclaringClass()->name === $r2->getDeclaringClass()->name;
} catch (ReflectionException) {
return false;
}
}, ARRAY_FILTER_USE_KEY);
}
$class = $newClass;
$config += $newConfig;
}
}
if (!is_subclass_of($class, ConditionRuleInterface::class)) {
throw new InvalidArgumentException("Invalid condition rule class: $class");
}
/** @var ConditionRuleInterface */
return Craft::createObject([
'class' => $class,
'attributes' => $config,
]);
}
}