Completed
Push — master ( 07d9eb...8877af )
by Alex
03:43
created

KeyDescriptor::getODataProperties()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 17
rs 9.4285
cc 2
eloc 12
nc 2
nop 0
1
<?php
2
3
namespace POData\UriProcessor\ResourcePathProcessor\SegmentParser;
4
5
use POData\Common\InvalidOperationException;
6
use POData\Common\Messages;
7
use POData\Common\ODataException;
8
use POData\ObjectModel\ODataProperty;
9
use POData\Providers\Metadata\ResourceSet;
10
use POData\Providers\Metadata\ResourceType;
11
use POData\Providers\Metadata\Type\Boolean;
12
use POData\Providers\Metadata\Type\DateTime;
13
use POData\Providers\Metadata\Type\Decimal;
14
use POData\Providers\Metadata\Type\Double;
15
use POData\Providers\Metadata\Type\Guid;
16
use POData\Providers\Metadata\Type\Int32;
17
use POData\Providers\Metadata\Type\Int64;
18
use POData\Providers\Metadata\Type\IType;
19
use POData\Providers\Metadata\Type\Null1;
20
use POData\Providers\Metadata\Type\Single;
21
use POData\Providers\Metadata\Type\StringType;
22
use POData\UriProcessor\QueryProcessor\ExpressionParser\ExpressionLexer;
23
use POData\UriProcessor\QueryProcessor\ExpressionParser\ExpressionTokenId;
24
25
/**
26
 * Class KeyDescriptor.
27
 *
28
 * A type used to represent Key (identifier) for an entity (resource), This class
29
 * can parse an Astoria KeyPredicate, KeyPredicate will be in one of the following
30
 * two formats:
31
 *  1) KeyValue                                      : If the Entry has a single key
32
 *                                                     Property the predicate may
33
 *                                                     include only the value of the
34
 *                                                     key Property.
35
 *      e.g. 'ALFKI' in Customers('ALFKI')
36
 *  2) Property = KeyValue [, Property = KeyValue]*  : If the key is made up of two
37
 *                                                     or more Properties, then its
38
 *                                                     value must be stated using
39
 *                                                     name/value pairs.
40
 *      e.g. 'ALFKI' in Customers(CustomerID = 'ALFKI'),
41
 *          "OrderID=10248,ProductID=11" in Order_Details(OrderID=10248,ProductID=11)
42
 *
43
 * Entity's identifier is a collection of value for key properties. These values
44
 * can be named or positional, depending on how they were specified in the URI.
45
 *  e.g. Named values:
46
 *         Customers(CustomerID = 'ALFKI'), Order_Details(OrderID=10248,ProductID=11)
47
 *       Positional values:
48
 *         Customers('ALFKI'), Order_Details(10248, 11)
49
 * Note: Currently WCF Data Service does not support multiple 'Positional values' so
50
 *       Order_Details(10248, 11) is not valid, but this class can parse both types.
51
 * Note: This type is also used to parse and validate skiptoken value as they are
52
 *       comma separated positional values.
53
 */
54
class KeyDescriptor
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
55
{
56
    /**
57
     * Holds collection of named key values
58
     * For e.g. the keypredicate Order_Details(OrderID=10248,ProductID=11) will
59
     * stored in this array as:
60
     * Array([OrderID] => Array( [0] => 10248 [1] => Object(Int32)),
61
     *       [ProductID] => Array( [0] => 11 [1] => Object(Int32)))
62
     * Note: This is mutually exclusive with $_positionalValues. These values
63
     * are not validated against entity's ResourceType, validation will happen
64
     * once validate function is called, $_validatedNamedValues will hold
65
     * validated values.
66
     *
67
     * @var array
68
     */
69
    private $namedValues = [];
70
71
    /**
72
     * Holds collection of positional key values
73
     * For e.g. the keypredicate Order_Details(10248, 11) will
74
     * stored in this array as:
75
     * Array([0] => Array( [0] => 10248 [1] => Object(Int32)),
76
     *       [1] => Array( [0] => 11 [1] => Object(Int32)))
77
     * Note: This is mutually exclusive with $_namedValues. These values are not
78
     * validated against entity's ResourceType, validation will happen once validate
79
     * function is called, $_validatedNamedValues will hold validated values.
80
     *
81
     * @var array
82
     */
83
    private $positionalValues = [];
84
85
    /**
86
     * Holds collection of positional or named values as named values. The validate
87
     * function populates this collection.
88
     *
89
     * @var array
90
     */
91
    private $validatedNamedValues = [];
92
93
    /**
94
     * Creates new instance of KeyDescriptor
95
     * Note: The arguments $namedValues and $positionalValues are mutually
96
     * exclusive. Either both or one will be empty array.
97
     *
98
     * @param array $namedValues      Collection of named key values
99
     * @param array $positionalValues Collection of positional key values
100
     */
101
    private function __construct(array $namedValues, array $positionalValues)
102
    {
103
        $namedCount = count($namedValues);
104
        $posCount = count($positionalValues);
105
        assert(0 == min($namedCount, $posCount), 'At least one of named and positional values arrays must be empty');
106
        if (0 < $namedCount) {
107
            $keys = array_keys($namedValues);
108
            for ($i = 0; $i < $namedCount; $i++) {
109
                $namedValues[$keys[$i]][0] = urldecode($namedValues[$keys[$i]][0]);
110
            }
111
        }
112
        if (0 < $posCount) {
113
            for ($i = 0; $i < $posCount; $i++) {
114
                $positionalValues[$i][0] = urldecode($positionalValues[$i][0]);
115
            }
116
        }
117
        $this->namedValues = $namedValues;
118
        $this->positionalValues = $positionalValues;
119
        $this->validatedNamedValues = [];
120
    }
121
122
    /**
123
     * @param string $keyString
124
     * @param bool $isKey
125
     * @param KeyDescriptor|null $keyDescriptor
126
     * @return bool
127
     */
128
    protected static function parseAndVerifyRawKeyPredicate($keyString, $isKey, KeyDescriptor &$keyDescriptor = null)
129
    {
130
        $result = self::tryParseKeysFromRawKeyPredicate(
131
            $keyString,
132
            $isKey,
133
            !$isKey,
134
            $keyDescriptor
135
        );
136
        assert(true === $result || false === $result, 'Result must be boolean');
137
        assert($result === isset($keyDescriptor), 'Result must match existence of keyDescriptor');
138
        return $result;
139
    }
140
141
    /**
142
     * Gets collection of named key values.
143
     *
144
     * @return array[]
145
     */
146
    public function getNamedValues()
147
    {
148
        return $this->namedValues;
149
    }
150
151
    /**
152
     * Gets collection of positional key values.
153
     *
154
     * @return array[]
155
     */
156
    public function getPositionalValues()
157
    {
158
        return $this->positionalValues;
159
    }
160
161
    /**
162
     * Gets collection of positional key values by reference.
163
     *
164
     * @return array[]
165
     */
166
    public function &getPositionalValuesByRef()
167
    {
168
        return $this->positionalValues;
169
    }
170
171
    /**
172
     * Gets validated named key values, this array will be populated
173
     * in validate function.
174
     *
175
     * @throws InvalidOperationException If this function invoked before invoking validate function
176
     *
177
     * @return array[]
178
     */
179
    public function getValidatedNamedValues()
180
    {
181
        if (empty($this->validatedNamedValues)) {
182
            throw new InvalidOperationException(
183
                Messages::keyDescriptorValidateNotCalled()
184
            );
185
        }
186
187
        return $this->validatedNamedValues;
188
    }
189
190
    /**
191
     * Checks whether the key values have name.
192
     *
193
     * @return bool
194
     */
195
    public function areNamedValues()
0 ignored issues
show
Coding Style introduced by
function areNamedValues() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
196
    {
197
        return !empty($this->namedValues);
198
    }
199
200
    /**
201
     * Check whether this KeyDescription has any key values.
202
     *
203
     * @return bool
204
     */
205
    public function isEmpty()
206
    {
207
        return empty($this->namedValues)
208
             && empty($this->positionalValues);
209
    }
210
211
    /**
212
     * Gets number of values in the key.
213
     *
214
     * @return int
215
     */
216
    public function valueCount()
217
    {
218
        if ($this->isEmpty()) {
219
            return 0;
220
        } elseif (!empty($this->namedValues)) {
221
            return count($this->namedValues);
222
        }
223
224
        return count($this->positionalValues);
225
    }
226
227
    /**
228
     * Attempts to parse value(s) of resource key(s) from the given key predicate
229
     *  and creates instance of KeyDescription representing the same, Once parsing
230
     *  is done one should call validate function to validate the created
231
     *  KeyDescription.
232
     *
233
     * @param string             $keyPredicate  The predicate to parse
234
     * @param KeyDescriptor|null $keyDescriptor On return, Description of key after parsing
235
     *
236
     * @return bool True if the given values were parsed; false if there was a syntax error
237
     */
238
    public static function tryParseKeysFromKeyPredicate(
0 ignored issues
show
Coding Style introduced by
function tryParseKeysFromKeyPredicate() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
239
        $keyPredicate,
240
        KeyDescriptor &$keyDescriptor = null
241
    ) {
242
        $isKey = true;
243
        $keyString = $keyPredicate;
244
        return self::parseAndVerifyRawKeyPredicate($keyString, $isKey, $keyDescriptor);
245
    }
246
247
    /**
248
     * Attempt to parse comma separated values representing a skiptoken and creates
249
     * instance of KeyDescriptor representing the same.
250
     *
251
     * @param string        $skipToken      The skiptoken value to parse
252
     * @param KeyDescriptor &$keyDescriptor On return, Description of values
253
     *                                      after parsing
254
     *
255
     * @return bool True if the given values were parsed; false if there was a syntax error
256
     */
257
    public static function tryParseValuesFromSkipToken($skipToken, &$keyDescriptor)
0 ignored issues
show
Coding Style introduced by
function tryParseValuesFromSkipToken() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
258
    {
259
        $isKey = false;
260
        $keyString = $skipToken;
261
        return self::parseAndVerifyRawKeyPredicate($keyString, $isKey, $keyDescriptor);
262
    }
263
264
    /**
265
     * Validate this KeyDescriptor, If valid, this function populates
266
     * _validatedNamedValues array with key as keyName and value as an array of
267
     * key value and key type.
268
     *
269
     * @param string       $segmentAsString The segment in the form identifier
270
     *                                      (keyPredicate) which this descriptor
271
     *                                      represents
272
     * @param ResourceType $resourceType    The type of the identifier in the segment
273
     *
274
     * @throws ODataException If validation fails
275
     */
276
    public function validate($segmentAsString, ResourceType $resourceType)
277
    {
278
        if ($this->isEmpty()) {
279
            $this->validatedNamedValues = [];
280
281
            return;
282
        }
283
284
        $keyProperties = $resourceType->getKeyProperties();
285
        $keyPropertiesCount = count($keyProperties);
286
        if (!empty($this->namedValues)) {
287 View Code Duplication
            if (count($this->namedValues) != $keyPropertiesCount) {
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...
288
                throw ODataException::createSyntaxError(
289
                    Messages::keyDescriptorKeyCountNotMatching(
290
                        $segmentAsString,
291
                        $keyPropertiesCount,
292
                        count($this->namedValues)
293
                    )
294
                );
295
            }
296
297
            foreach ($keyProperties as $keyName => $keyResourceProperty) {
298
                if (!array_key_exists($keyName, $this->namedValues)) {
299
                    $keysAsString = null;
300
                    foreach (array_keys($keyProperties) as $key) {
301
                        $keysAsString .= $key . ', ';
302
                    }
303
304
                    $keysAsString = rtrim($keysAsString, ' ,');
305
                    throw ODataException::createSyntaxError(
306
                        Messages::keyDescriptorMissingKeys(
307
                            $segmentAsString,
308
                            $keysAsString
309
                        )
310
                    );
311
                }
312
313
                $typeProvided = $this->namedValues[$keyName][1];
314
                $expectedType = $keyResourceProperty->getInstanceType();
315
                assert($expectedType instanceof IType, get_class($expectedType));
316 View Code Duplication
                if (!$expectedType->isCompatibleWith($typeProvided)) {
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...
317
                    throw ODataException::createSyntaxError(
318
                        Messages::keyDescriptorInCompatibleKeyType(
319
                            $segmentAsString,
320
                            $keyName,
321
                            $expectedType->getFullTypeName(),
322
                            $typeProvided->getFullTypeName()
323
                        )
324
                    );
325
                }
326
327
                $this->validatedNamedValues[$keyName] = $this->namedValues[$keyName];
328
            }
329
        } else {
330 View Code Duplication
            if (count($this->positionalValues) != $keyPropertiesCount) {
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...
331
                throw ODataException::createSyntaxError(
332
                    Messages::keyDescriptorKeyCountNotMatching(
333
                        $segmentAsString,
334
                        $keyPropertiesCount,
335
                        count($this->positionalValues)
336
                    )
337
                );
338
            }
339
340
            $i = 0;
341
            foreach ($keyProperties as $keyName => $keyResourceProperty) {
342
                $typeProvided = $this->positionalValues[$i][1];
343
                $expectedType = $keyResourceProperty->getInstanceType();
344
                assert($expectedType instanceof IType, get_class($expectedType));
345
346 View Code Duplication
                if (!$expectedType->isCompatibleWith($typeProvided)) {
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...
347
                    throw ODataException::createSyntaxError(
348
                        Messages::keyDescriptorInCompatibleKeyTypeAtPosition(
349
                            $segmentAsString,
350
                            $keyResourceProperty->getName(),
351
                            $i,
352
                            $expectedType->getFullTypeName(),
353
                            $typeProvided->getFullTypeName()
354
                        )
355
                    );
356
                }
357
358
                $this->validatedNamedValues[$keyName]
359
                    = $this->positionalValues[$i];
360
                ++$i;
361
            }
362
        }
363
    }
364
365
    /**
366
     * Attempts to parse value(s) of resource key(s) from the key predicate and
367
     * creates instance of KeyDescription representing the same, Once parsing is
368
     * done, one should call validate function to validate the created KeyDescription.
369
     *
370
     * @param string        $keyPredicate     The key predicate to parse
371
     * @param bool          $allowNamedValues Set to true if parser should accept
372
     *                                        named values(Property = KeyValue),
373
     *                                        if false then parser will fail on
374
     *                                        such constructs
375
     * @param bool          $allowNull        Set to true if parser should accept
376
     *                                        null values for positional key
377
     *                                        values, if false then parser will
378
     *                                        fail on seeing null values
379
     * @param KeyDescriptor &$keyDescriptor   On return, Description of key after
380
     *                                        parsing
381
     *
382
     * @return bool True if the given values were parsed; false if there was a syntax error
383
     */
384
    private static function tryParseKeysFromRawKeyPredicate(
0 ignored issues
show
Coding Style introduced by
function tryParseKeysFromRawKeyPredicate() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
385
        $keyPredicate,
386
        $allowNamedValues,
387
        $allowNull,
388
        &$keyDescriptor
389
    ) {
390
        $expressionLexer = new ExpressionLexer($keyPredicate);
391
        $currentToken = $expressionLexer->getCurrentToken();
392
393
        //Check for empty predicate e.g. Customers(  )
394
        if ($currentToken->Id == ExpressionTokenId::END) {
395
            $keyDescriptor = new self([], []);
396
397
            return true;
398
        }
399
400
        $namedValues = [];
401
        $positionalValues = [];
402
403
        do {
404
            if (($currentToken->Id == ExpressionTokenId::IDENTIFIER)
405
                && $allowNamedValues
406
            ) {
407
                //named and positional values are mutually exclusive
408
                if (!empty($positionalValues)) {
409
                    return false;
410
                }
411
412
                //expecting keyName=keyValue, verify it
413
                $identifier = $currentToken->getIdentifier();
414
                $expressionLexer->nextToken();
415
                $currentToken = $expressionLexer->getCurrentToken();
416
                if ($currentToken->Id != ExpressionTokenId::EQUAL) {
417
                    return false;
418
                }
419
420
                $expressionLexer->nextToken();
421
                $currentToken = $expressionLexer->getCurrentToken();
422
                if (!$currentToken->isKeyValueToken()) {
423
                    return false;
424
                }
425
426
                if (array_key_exists($identifier, $namedValues)) {
427
                    //Duplication of KeyName not allowed
428
                    return false;
429
                }
430
431
                //Get type of keyValue and validate keyValue
432
                $outValue = $outType = null;
433
                if (!self::getTypeAndValidateKeyValue(
434
                    $currentToken->Text,
435
                    $currentToken->Id,
436
                    $outValue,
437
                    $outType
438
                )
439
                ) {
440
                    return false;
441
                }
442
443
                $namedValues[$identifier] = [$outValue, $outType];
444
            } elseif ($currentToken->isKeyValueToken()
445
                || ($currentToken->Id == ExpressionTokenId::NULL_LITERAL && $allowNull)
446
            ) {
447
                //named and positional values are mutually exclusive
448
                if (!empty($namedValues)) {
449
                    return false;
450
                }
451
452
                //Get type of keyValue and validate keyValue
453
                $outValue = $outType = null;
454
                if (!self::getTypeAndValidateKeyValue(
455
                    $currentToken->Text,
456
                    $currentToken->Id,
457
                    $outValue,
458
                    $outType
459
                )
460
                ) {
461
                    return false;
462
                }
463
464
                $positionalValues[] = [$outValue, $outType];
465
            } else {
466
                return false;
467
            }
468
469
            $expressionLexer->nextToken();
470
            $currentToken = $expressionLexer->getCurrentToken();
471
            if ($currentToken->Id == ExpressionTokenId::COMMA) {
472
                $expressionLexer->nextToken();
473
                $currentToken = $expressionLexer->getCurrentToken();
474
                //end of text and comma, Trailing comma not allowed
475
                if ($currentToken->Id == ExpressionTokenId::END) {
476
                    return false;
477
                }
478
            }
479
        } while ($currentToken->Id != ExpressionTokenId::END);
480
481
        $keyDescriptor = new self($namedValues, $positionalValues);
482
483
        return true;
484
    }
485
486
    /**
487
     * Get the type of an Astoria URI key value, validate the value against the type. If valid, this function
488
     * provides the PHP value equivalent to the Astoria URI key value.
489
     *
490
     * @param string            $value     The Astoria URI key value
491
     * @param ExpressionTokenId $tokenId   The tokenId for $value literal
492
     * @param mixed|null        &$outValue After the invocation, this parameter holds the PHP equivalent to $value,
493
     *                                     if $value is not valid then this parameter will be null
494
     * @param IType|null        &$outType  After the invocation, this parameter holds the type of $value, if $value is
495
     *                                     not a valid key value type then this parameter will be null
496
     *
497
     * @return bool True if $value is a valid type, else false
498
     */
499
    private static function getTypeAndValidateKeyValue($value, $tokenId, &$outValue, &$outType)
0 ignored issues
show
Coding Style introduced by
function getTypeAndValidateKeyValue() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
500
    {
501
        switch ($tokenId) {
502
            case ExpressionTokenId::BOOLEAN_LITERAL:
503
                $outType = new Boolean();
504
                break;
505
            case ExpressionTokenId::DATETIME_LITERAL:
506
                $outType = new DateTime();
507
                break;
508
            case ExpressionTokenId::GUID_LITERAL:
509
                $outType = new Guid();
510
                break;
511
            case ExpressionTokenId::STRING_LITERAL:
512
                $outType = new StringType();
513
                break;
514
            case ExpressionTokenId::INTEGER_LITERAL:
515
                $outType = new Int32();
516
                break;
517
            case ExpressionTokenId::DECIMAL_LITERAL:
518
                $outType = new Decimal();
519
                break;
520
            case ExpressionTokenId::DOUBLE_LITERAL:
521
                $outType = new Double();
522
                break;
523
            case ExpressionTokenId::INT64_LITERAL:
524
                $outType = new Int64();
525
                break;
526
            case ExpressionTokenId::SINGLE_LITERAL:
527
                $outType = new Single();
528
                break;
529
            case ExpressionTokenId::NULL_LITERAL:
530
                $outType = new Null1();
531
                break;
532
            default:
533
                $outType = null;
534
535
                return false;
536
        }
537
538
        if (!$outType->validate($value, $outValue)) {
539
            $outType = $outValue = null;
540
541
            return false;
542
        }
543
544
        return true;
545
    }
546
547
    /**
548
     * Generate relative edit url for this key descriptor and supplied resource set
549
     *
550
     * @param ResourceSet $resourceSet
551
     *
552
     * @return string
553
     * @throws \InvalidArgumentException
554
     */
555
    public function generateRelativeUri(ResourceSet $resourceSet)
556
    {
557
        $resourceType = $resourceSet->getResourceType();
558
        $keys = $resourceType->getKeyProperties();
559
560
        $namedKeys = $this->getNamedValues();
561
        assert(0 !== count($keys), 'count($keys) == 0');
562
        if (count($keys) !== count($namedKeys)) {
563
            $msg = 'Mismatch between supplied key predicates and number of keys defined on resource set';
564
            throw new \InvalidArgumentException($msg);
565
        }
566
        $editUrl = $resourceSet->getName() . '(';
567
        $comma = null;
568
        foreach ($keys as $keyName => $resourceProperty) {
569
            if (!array_key_exists($keyName, $namedKeys)) {
570
                $msg = 'Key predicate '.$keyName.' not present in named values';
571
                throw new \InvalidArgumentException($msg);
572
            }
573
            $keyType = $resourceProperty->getInstanceType();
574
            assert($keyType instanceof IType, '$keyType not instanceof IType');
575
            $keyValue = $namedKeys[$keyName][0];
576
            $keyValue = $keyType->convertToOData($keyValue);
577
578
            $editUrl .= $comma . $keyName . '=' . $keyValue;
579
            $comma = ',';
580
        }
581
582
        $editUrl .= ')';
583
584
        return $editUrl;
585
    }
586
587
    /**
588
     * Convert validated named values into an array of ODataProperties
589
     *
590
     * return array[]
591
     */
592
    public function getODataProperties()
593
    {
594
        $values = $this->getValidatedNamedValues();
595
        $result = [];
596
597
        foreach ($values as $propName => $propDeets) {
598
            assert(2 == count($propDeets));
599
            assert($propDeets[1] instanceof IType);
600
            $property = new ODataProperty();
601
            $property->name = strval($propName);
602
            $property->value = $propDeets[1]->convert($propDeets[0]);
603
            $property->typeName = $propDeets[1]->getFullTypeName();
604
            $result[$propName] = $property;
605
        }
606
607
        return $result;
608
    }
609
}
610