-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathDataItemFactory.php
More file actions
251 lines (216 loc) · 7.3 KB
/
Copy pathDataItemFactory.php
File metadata and controls
251 lines (216 loc) · 7.3 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
<?php
namespace MutableTypedData;
use MutableTypedData\Definition\DefinitionProviderInterface;
use MutableTypedData\Definition\DataDefinition;
use MutableTypedData\Data\DataItem;
use MutableTypedData\Data\StringData;
use MutableTypedData\Data\BooleanData;
use MutableTypedData\Data\ArrayData;
use MutableTypedData\Data\ComplexData;
use MutableTypedData\Data\MutableData;
use MutableTypedData\Exception\InvalidDefinitionException;
use MutableTypedData\ExpressionLanguage\DataAddressLanguageProvider;
use MutableTypedData\Validator\ValidatorInterface;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
/**
* Creates data item objects.
*
* This may be subclassed to customize various aspects:
* - To add custom data types, override $types.
* - To customise the class used for multiple data, override $multipleData.
* - To add Expression Language functions, override
* $expressionLanguageProviders.
* - To add validators, override $validators.
*
* Data items keep a reference to the factory class that created them, and so
* all subsequent data item creation will use the same factory class for
* consistency.
*/
class DataItemFactory {
/**
* Mapping of type machine names to classes.
*
* @var array
*/
static protected $types = [
'string' => StringData::class,
'boolean' => BooleanData::class,
'complex' => ComplexData::class,
'mutable' => MutableData::class,
];
/**
* The class to use for multiple-valued data.
*
* @var string
*/
static protected $multipleData = ArrayData::class;
/**
* The names of Expression Language function provider classes to use.
*
* Subclasses may override this to add their own, but must always include
* the classes that are given here.
*
* @var string[]
*/
static protected $expressionLanguageProviders = [
DataAddressLanguageProvider::class,
];
/**
* Mapping of validator names to validator classes.
*
* Keys are identifiers to be used in definitions. Values are fully-qualified
* class names.
*
* @var array
*/
static protected $validators = [
];
/**
* The instantiated Expression Language.
*/
static protected $expressionLanguage;
/**
* Gets the class for a data type.
*
* @param string $type_name
* The data type name, as defined in static::$types.
*
* @return string
* The class name.
*/
public static function getTypeClass(string $type_name): string {
return static::$types[$type_name];
}
/**
* Creates a data object from a definition provider.
*
* @param \MutableTypedData\Definition\DefinitionProviderInterface|string $class_or_object
* Either the definition provider object, or its class name.
*
* @return \MutableTypedData\Data\DataItem
* The new data item.
*/
public static function createFromProvider($class_or_object): DataItem {
if (!is_a($class_or_object, DefinitionProviderInterface::class, TRUE)) {
throw new \TypeError('Parameter to createFromProvider() must implement DefinitionProviderInterface.');
}
if (is_object($class_or_object)) {
$provider_class = get_class($class_or_object);
}
else {
$provider_class = $class_or_object;
}
$definition = $provider_class::getDefinition();
$data = static::createFromDefinition($definition);
// TODO: typehint for setDefinitionSource might be callable???
$data->setDefinitionSource($provider_class . '::getDefinition');
return $data;
}
/**
* Creates a data object from a definition providing callback.
*
* @param callable $callback
* A callback which returns a definition.
*
* @return \MutableTypedData\Data\DataItem
* The new data item.
*/
public static function createFromCallback(callable $callback): DataItem {
$definition = $callback();
$data = static::createFromDefinition($definition);
$data->setDefinitionSource($callback);
return $data;
}
/**
* Creates a new data item from a definition.
*
* WARNING: this way of creating a data item may not be used to create the
* root data item if the data item is going to be serialized. Use
* createFromCallback() or createFromProvider() instead.
*
* @param \MutableTypedData\Definition\DataDefinition $definition
* A definition object.
* @param \MutableTypedData\Data\DataItem $parent
* (optional) The parent data item if the item to be created is not a root.
* @param int $delta
* (optional) The delta of the item to be created if it is part of a
* multiple data set.
*
* @return \MutableTypedData\Data\DataItem
* The new data item.
*/
public static function createFromDefinition(DataDefinition $definition, ?DataItem $parent = NULL, ?int $delta = NULL): DataItem {
// Ensure a machine name.
if ($parent) {
$machine_name = $definition->getName();
// Allow an empty machine name if this is a delta.
// TODO: this is inconsistent, as this only happens with a root that has
// no name! See TODO on ArrayData::createItem().
if (is_null($machine_name) && is_int($delta)) {
$machine_name = $delta;
}
if (!is_string($machine_name) && !is_numeric($machine_name)) {
throw new InvalidDefinitionException("Machine name must be a string or number.");
}
// We allow a 0 machine name because the first delta in a multiple value
// will be 0.
if (is_null($machine_name)) {
throw new InvalidDefinitionException("Non-root properties must have a machine name.");
}
}
if ($definition->isMultiple()) {
$item = new static::$multipleData($definition);
}
else {
if (!isset(static::$types[$definition->getType()])) {
throw new InvalidDefinitionException(sprintf("Unknown data type '%s' at '%s'.",
$definition->getType(),
$definition->getName()
));
}
$class = static::$types[$definition->getType()];
$item = new $class($definition);
}
if ($parent) {
$item->setParent($parent, $delta);
}
$item->setFactory(static::class);
return $item;
}
/**
* Gets the Expresssion Language object.
*
* @return \Symfony\Component\ExpressionLanguage\ExpressionLanguage
* The Expression Language.
*/
public static function getExpressionLanguage(): ExpressionLanguage {
if (!isset(static::$expressionLanguage)) {
static::$expressionLanguage = new ExpressionLanguage();
foreach (static::$expressionLanguageProviders as $provider_class) {
static::$expressionLanguage->registerProvider(new $provider_class());
}
}
return static::$expressionLanguage;
}
/**
* Gets a validator object.
*
* @param string $validator_name
* The validator's name, as defined in static::$validators.
*
* @return \MutableTypedData\Validator\ValidatorInterface
* The validator instance.
*
* @throws \MutableTypedData\Exception\InvalidDefinitionException
* Throws an exception if the given string is not a defined validator name.
*/
public static function getValidator(string $validator_name): ValidatorInterface {
if (!isset(static::$validators[$validator_name])) {
throw new InvalidDefinitionException(sprintf("Validator %s not found.",
$validator_name
));
}
$class = static::$validators[$validator_name];
return new $class;
}
}