Test Failed
Pull Request — master (#7)
by
unknown
11:40
created

ObjectModelSerializer::_writeCustomProperties()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 14
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
namespace POData\ObjectModel;
4
5
use POData\Common\ODataConstants;
6
use POData\Common\InvalidOperationException;
7
use POData\Providers\Query\QueryType;
8
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource;
9
use POData\UriProcessor\RequestDescription;
10
use POData\IService;
11
use POData\Providers\Metadata\ResourceType;
12
use POData\Providers\Metadata\ResourceTypeKind;
13
use POData\Providers\Metadata\ResourcePropertyKind;
14
use POData\Providers\Metadata\ResourceProperty;
15
use POData\Providers\Metadata\Type\Binary;
16
use POData\Providers\Metadata\Type\Boolean;
17
use POData\Providers\Metadata\Type\StringType;
18
use POData\Providers\Metadata\Type\DateTime;
19
use POData\Common\ODataException;
20
use POData\Common\Messages;
21
use ArrayAccess;
22
23
/**
24
 * Class ObjectModelSerializer
25
 * @package POData\ObjectModel
26
 */
27
class ObjectModelSerializer extends ObjectModelSerializerBase
28
{
29
    /**
30
     * Creates new instance of ObjectModelSerializer.
31
     *
32
     * @param IService $service
33
     *
34
     * @param RequestDescription $request the  request submitted by the client.
35
     *
36
     */
37
    public function __construct(IService $service, RequestDescription $request)
38
    {
39
        parent::__construct($service, $request);
40
    }
41
42
    /**
43
     * Write a top level entry resource.
44
     *
45
     * @param mixed $entryObject Reference to the entry object to be written.
46
     *
47
     * @return ODataEntry
48
     */
49
    public function writeTopLevelElement($entryObject)
50
    {
51
        $requestTargetSource = $this->request->getTargetSource();
52
53
        $resourceType = null;
54
        if ($requestTargetSource == TargetSource::ENTITY_SET) {
55
            $resourceType = $this->request->getTargetResourceType();
56
        } else {
57
            $this->assert(
58
                $requestTargetSource == TargetSource::PROPERTY,
59
                '$requestTargetSource == TargetSource::PROPERTY'
60
            );
61
            $resourceProperty = $this->request->getProjectedProperty();
62
            //$this->assert($resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE, '$resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE');
63
            $resourceType = $resourceProperty->getResourceType();
64
        }
65
66
        $needPop = $this->pushSegmentForRoot();
67
        $entry = $this->_writeEntryElement(
68
            $entryObject,
69
            $resourceType,
70
            $this->request->getRequestUrl()->getUrlAsString(),
71
            $this->request->getContainerName()
72
        );
73
        $this->popSegment($needPop);
74
        return $entry;
75
    }
76
77
    /**
78
     * Write top level feed element.
79
     *
80
     * @param array &$entryObjects Array of entry resources to be written.
81
     *
82
     * @return ODataFeed.
0 ignored issues
show
Documentation Bug introduced by
The doc comment ODataFeed. at position 0 could not be parsed: Unknown type name 'ODataFeed.' at position 0 in ODataFeed..
Loading history...
83
     */
84
    public function writeTopLevelElements(&$entryObjects)
85
    {
86
        $this->assert(is_array($entryObjects), 'is_array($entryObjects)');
87
        $requestTargetSource = $this->request->getTargetSource();
88
        $title = null;
89
        if ($requestTargetSource == TargetSource::ENTITY_SET) {
90
            $title = $this->request->getContainerName();
91
        } else {
92
            $this->assert(
93
                $requestTargetSource == TargetSource::PROPERTY,
94
                '$requestTargetSource == TargetSource::PROPERTY'
95
            );
96
            $resourceProperty = $this->request->getProjectedProperty();
97
            $this->assert(
98
                $resourceProperty->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE,
99
                '$resourceProperty->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE'
100
            );
101
            $title = $resourceProperty->getName();
102
        }
103
104
        $relativeUri = $this->request->getIdentifier();
105
        $feed = new ODataFeed();
106
107
        if ($this->request->queryType == QueryType::ENTITIES_WITH_COUNT) {
108
            $feed->rowCount = $this->request->getCountValue();
109
        }
110
111
        $needPop = $this->pushSegmentForRoot();
112
        $targetResourceType = $this->request->getTargetResourceType();
113
        $this->_writeFeedElements(
114
            $entryObjects,
115
            $targetResourceType,
116
            $title,
117
            $this->request->getRequestUrl()->getUrlAsString(),
118
            $relativeUri,
119
            $feed
120
        );
121
        $this->popSegment($needPop);
122
        return $feed;
123
    }
124
125
    /**
126
     * Write top level url element.
127
     *
128
     * @param mixed $entryObject The entry resource whose url to be written.
129
     *
130
     * @return ODataURL
131
     */
132
    public function writeUrlElement($entryObject)
133
    {
134
        $url = new ODataURL();
135
        if (!is_null($entryObject)) {
136
            $currentResourceType = $this->getCurrentResourceSetWrapper()->getResourceType();
137
            $relativeUri = $this->getEntryInstanceKey(
138
                $entryObject,
139
                $currentResourceType,
140
                $this->getCurrentResourceSetWrapper()->getName()
141
            );
142
143
            $url->url = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
144
        }
145
146
        return $url;
147
    }
148
149
    /**
150
     * Write top level url collection.
151
     *
152
     * @param array $entryObjects Array of entry resources
153
     * whose url to be written.
154
     *
155
     * @return ODataURLCollection
156
     */
157
    public function writeUrlElements($entryObjects)
158
    {
159
        $urls = new ODataURLCollection();
160
        if (!empty($entryObjects)) {
161
            $i = 0;
162
            foreach ($entryObjects as $entryObject) {
163
                $urls->urls[$i] = $this->writeUrlElement($entryObject);
164
                $i++;
165
            }
166
167
            if ($i > 0 && $this->needNextPageLink(count($entryObjects))) {
168
                $urls->nextPageLink = $this->getNextLinkUri($entryObjects[$i - 1], $this->request->getRequestUrl()->getUrlAsString());
169
            }
170
        }
171
172
        if ($this->request->queryType == QueryType::ENTITIES_WITH_COUNT) {
173
            $urls->count = $this->request->getCountValue();
174
        }
175
176
        return $urls;
177
    }
178
179
    /**
180
     * Write top level complex resource.
181
     *
182
     * @param mixed                &$complexValue         The complex object to be
183
     *                                                    written.
184
     * @param string               $propertyName          The name of the
185
     *                                                    complex property.
186
     * @param ResourceType         &$resourceType         Describes the type of
187
     *                                                    complex object.
188
     *
189
     * @return ODataPropertyContent
190
     */
191
    public function writeTopLevelComplexObject(
192
        &$complexValue,
193
        $propertyName,
194
        ResourceType &$resourceType
195
    ) {
196
        $propertyContent = new ODataPropertyContent();
197
        $this->_writeComplexValue(
198
            $complexValue,
199
            $propertyName, $resourceType, null,
200
            $propertyContent
201
        );
202
203
        return $propertyContent;
204
    }
205
206
    /**
207
     * Write top level bag resource.
208
     *
209
     * @param mixed                &$BagValue             The bag object to be
210
     *                                                    written.
211
     * @param string               $propertyName          The name of the
212
     *                                                    bag property.
213
     * @param ResourceType         &$resourceType         Describes the type of
214
     *                                                    bag object.
215
     *
216
     * @return ODataPropertyContent
217
     */
218
    public function writeTopLevelBagObject(
219
        &$BagValue,
220
        $propertyName,
221
        ResourceType &$resourceType
222
    ) {
223
224
        $propertyContent = new ODataPropertyContent();
225
        $this->_writeBagValue(
226
            $BagValue,
227
            $propertyName, $resourceType, null,
228
            $propertyContent
229
        );
230
231
        return $propertyContent;
232
    }
233
234
    /**
235
     * Write top level primitive value.
236
     *
237
     * @param mixed                &$primitiveValue       The primitve value to be
238
     *                                                    written.
239
     * @param ResourceProperty     &$resourceProperty     Resource property
240
     *                                                    describing the
241
     *                                                    primitive property
242
     *                                                    to be written.
243
     *
244
     * @return ODataPropertyContent
245
     */
246
    public function writeTopLevelPrimitive(
247
        &$primitiveValue,
248
        ResourceProperty &$resourceProperty
249
    ) {
250
        $propertyContent = new ODataPropertyContent();
251
        $propertyContent->properties[] = new ODataProperty();
252
        $this->_writePrimitiveValue(
253
            $primitiveValue,
254
            $resourceProperty,
255
            $propertyContent->properties[0]
256
        );
257
258
        return $propertyContent;
259
    }
260
261
    /**
262
     * Write an entry element.
263
     *
264
     * @param mixed        $entryObject  Object representing entry element.
265
     * @param ResourceType $resourceType Expected type of the entry object.
266
     * @param string       $absoluteUri   Absolute uri of the entry element.
267
     * @param string       $relativeUri   Relative uri of the entry element.
268
     *
269
     * @return ODataEntry
270
     */
271
    private function _writeEntryElement(
272
        $entryObject,
273
        ResourceType $resourceType,
274
        $absoluteUri,
0 ignored issues
show
Unused Code introduced by
The parameter $absoluteUri is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

274
        /** @scrutinizer ignore-unused */ $absoluteUri,

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

Loading history...
275
        $relativeUri
0 ignored issues
show
Unused Code introduced by
The parameter $relativeUri is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

275
        /** @scrutinizer ignore-unused */ $relativeUri

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

Loading history...
276
    ) {
277
        $entry = new ODataEntry();
278
        $entry->resourceSetName = $this->getCurrentResourceSetWrapper()->getName();
279
280
        if (is_null($entryObject)) {
281
            //According to atom standard an empty entry must have an Author
282
            //node.
283
        } else {
284
            $relativeUri = $this->getEntryInstanceKey(
285
                $entryObject,
286
                $resourceType,
287
                $this->getCurrentResourceSetWrapper()->getName()
288
            );
289
290
            $absoluteUri = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
291
            $title = $resourceType->getName();
292
            //TODO Resolve actual resource type
293
            $actualResourceType = $resourceType;
294
            $this->_writeMediaResourceMetadata(
295
                $entryObject,
296
                $actualResourceType,
297
                $title,
298
                $relativeUri,
299
                $entry
300
            );
301
302
            $entry->id = $absoluteUri;
303
            $entry->eTag = $this->getETagForEntry($entryObject, $resourceType);
304
            $entry->title = $title;
305
            $entry->editLink = $relativeUri;
306
            $entry->type = $actualResourceType->getFullName();
307
308
            $entry->customProperties = new ODataPropertyContent();
309
310
            $this->_writeCustomProperties(
311
                $entryObject,
312
                $entry->customProperties
313
            );
314
315
            $odataPropertyContent = new ODataPropertyContent();
316
            $this->_writeObjectProperties(
317
                $entryObject,
318
                $actualResourceType,
319
                $absoluteUri,
320
                $relativeUri,
321
                $entry,
322
                $odataPropertyContent
323
            );
324
            $entry->propertyContent = $odataPropertyContent;
325
        }
326
327
        return $entry;
328
    }
329
330
    /**
331
     * Writes the feed elements
332
     *
333
     * @param array        &$entryObjects Array of entries in the feed element.
334
     * @param ResourceType &$resourceType The resource type of the f the elements
335
     *                                    in the collection.
336
     * @param string       $title         Title of the feed element.
337
     * @param string       $absoluteUri   Absolute uri representing the feed element.
338
     * @param string       $relativeUri   Relative uri representing the feed element.
339
     * @param ODataFeed    &$feed    Feed to write to.
340
     *
341
     * @return void
342
     */
343
    private function _writeFeedElements(
344
        &$entryObjects,
345
        ResourceType &$resourceType,
346
        $title,
347
        $absoluteUri,
348
        $relativeUri,
349
        ODataFeed &$feed
350
    ) {
351
        $this->assert(is_array($entryObjects) || $entryObjects instanceof ArrayAccess, '_writeFeedElements::is_array($entryObjects)');
352
        $feed->id = $absoluteUri;
353
        $feed->title = $title;
354
        $feed->selfLink = new ODataLink();
355
        $feed->selfLink->name = ODataConstants::ATOM_SELF_RELATION_ATTRIBUTE_VALUE;
356
        $feed->selfLink->title = $title;
357
        $feed->selfLink->url = $relativeUri;
358
359
        if (empty($entryObjects)) {
360
            //TODO // ATOM specification: if a feed contains no entries,
361
            //then the feed should have at least one Author tag
362
        } else {
363
            foreach ($entryObjects as $entryObject) {
364
                $feed->entries[] = $this->_writeEntryElement($entryObject, $resourceType, null, null);
365
            }
366
367
            if ($this->needNextPageLink(count($entryObjects))) {
368
                $end = end($entryObjects);
369
                $feed->nextPageLink = $this->getNextLinkUri($end, $absoluteUri);
370
            }
371
        }
372
    }
373
374
    private function _writeCustomProperties(
375
        $customObject,
376
        ODataPropertyContent &$odataPropertyContent
377
    )
378
    {
379
        $properties = $this->service->getQueryProvider()->getCustomProperties($customObject);
0 ignored issues
show
Bug introduced by
The method getQueryProvider() does not exist on POData\IService. Since it exists in all sub-types, consider adding an abstract or default implementation to POData\IService. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

379
        $properties = $this->service->/** @scrutinizer ignore-call */ getQueryProvider()->getCustomProperties($customObject);
Loading history...
380
381
        //First write out primitve types
382
        foreach ($properties as $name => $value) {
383
            $odataProperty = new ODataProperty();
384
            $odataProperty->name = $name;
385
            $odataProperty->typeName = 'Edm.String';
386
            $odataProperty->value = json_encode($value);
387
            $odataPropertyContent->properties[] = $odataProperty;
388
        }
389
    }
390
391
    /**
392
     * Write values of properties of given entry (resource) or complex object.
393
     *
394
     * @param mixed                $customObject          Entity or complex object
395
     *                                                    with properties
396
     *                                                    to write out.
397
     * @param ResourceType         &$resourceType         Resource type describing
398
     *                                                    the metadata of
399
     *                                                    the custom object.
400
     * @param string               $absoluteUri           Absolute uri for the given
401
     *                                                    entry object
402
     *                                                    NULL for complex object.
403
     * @param string               $relativeUri           Relative uri for the given
404
     *                                                    custom object.
405
     * @param ODataEntry           ODataEntry|null           ODataEntry instance to
406
     *                                                    place links and
407
     *                                                    expansion of the
408
     *                                                    entry object,
409
     *                                                    NULL for complex object.
410
     * @param ODataPropertyContent &$odataPropertyContent ODataPropertyContent
411
     *                                                    instance in which
412
     *                                                    to place the values.
413
     *
414
     * @return void
415
     */
416
    private function _writeObjectProperties(
417
        $customObject,
418
        ResourceType &$resourceType,
419
        $absoluteUri,
420
        $relativeUri,
421
        &$odataEntry,
422
        ODataPropertyContent &$odataPropertyContent
423
    ) {
424
        $resourceTypeKind = $resourceType->getResourceTypeKind();
425
        if (is_null($absoluteUri) == ($resourceTypeKind == ResourceTypeKind::ENTITY)
426
        ) {
427
            throw ODataException::createInternalServerError(
428
                Messages::badProviderInconsistentEntityOrComplexTypeUsage(
429
                    $resourceType->getName()
430
                )
431
            );
432
        }
433
434
        $this->assert(
435
            (($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry))
436
            || (($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry)),
437
            '(($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry))
438
            || (($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry))'
439
        );
440
        $projectionNodes = null;
441
        $navigationProperties = null;
442
        if ($resourceTypeKind == ResourceTypeKind::ENTITY) {
0 ignored issues
show
introduced by
The condition $resourceTypeKind == POD...esourceTypeKind::ENTITY is always false.
Loading history...
443
            $projectionNodes = $this->getProjectionNodes();
444
            $navigationProperties = array();
445
        }
446
447
        if (is_null($projectionNodes)) {
0 ignored issues
show
introduced by
The condition is_null($projectionNodes) is always true.
Loading history...
448
            //This is the code path to handle properties of Complex type
449
            //or Entry without projection (i.e. no expansion or selection)
450
            $resourceProperties = array();
451
            if ($resourceTypeKind == ResourceTypeKind::ENTITY) {
0 ignored issues
show
introduced by
The condition $resourceTypeKind == POD...esourceTypeKind::ENTITY is always false.
Loading history...
452
                // If custom object is an entry then it can contain navigation
453
                // properties which are invisible (because the corresponding
454
                // resource set is invisible).
455
                // IDSMP::getResourceProperties will give collection of properties
456
                // which are visible.
457
                $currentResourceSetWrapper1 = $this->getCurrentResourceSetWrapper();
458
                $resourceProperties = $this->service
459
                    ->getProvidersWrapper()
460
                    ->getResourceProperties(
461
                        $currentResourceSetWrapper1,
462
                        $resourceType
463
                    );
464
            } else {
465
                $resourceProperties = $resourceType->getAllProperties();
466
            }
467
468
            //First write out primitve types
469
            foreach ($resourceProperties as $name => $resourceProperty) {
470
                if ($resourceProperty->getKind() == ResourcePropertyKind::PRIMITIVE
471
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY)
472
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG)
473
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)
474
                ) {
475
                    $odataProperty = new ODataProperty();
476
                    $primitiveValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
477
                    $this->_writePrimitiveValue($primitiveValue, $resourceProperty, $odataProperty);
478
                    $odataPropertyContent->properties[] = $odataProperty;
479
                }
480
            }
481
482
            //Write out bag and complex type
483
            $i = 0;
484
            foreach ($resourceProperties as $resourceProperty) {
485
                if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG)) {
0 ignored issues
show
Bug introduced by
POData\Providers\Metadat...sourcePropertyKind::BAG of type integer is incompatible with the type POData\Providers\Metadata\ResourcePropertyKind expected by parameter $kind of POData\Providers\Metadat...rceProperty::isKindOf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

485
                if ($resourceProperty->isKindOf(/** @scrutinizer ignore-type */ ResourcePropertyKind::BAG)) {
Loading history...
486
                    //Handle Bag Property (Bag of Primitive or complex)
487
                    $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
488
                    $resourceType2 = $resourceProperty->getResourceType();
489
                    $this->_writeBagValue(
490
                        $propertyValue,
491
                        $resourceProperty->getName(),
492
                        $resourceType2,
493
                        $relativeUri . '/' . $resourceProperty->getName(),
494
                        $odataPropertyContent
495
                    );
496
                } else {
497
                    $resourcePropertyKind = $resourceProperty->getKind();
498
                    if ($resourcePropertyKind == ResourcePropertyKind::COMPLEX_TYPE) {
499
                        $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
500
                        $resourceType1 = $resourceProperty->getResourceType();
501
                        $this->_writeComplexValue(
502
                            $propertyValue,
503
                            $resourceProperty->getName(),
504
                            $resourceType1,
505
                            $relativeUri . '/' . $resourceProperty->getName(),
506
                            $odataPropertyContent
507
                        );
508
                    } else if ($resourceProperty->getKind() == ResourcePropertyKind::PRIMITIVE
509
                        || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY)
510
                        || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG)
511
                        || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)
512
                    ) {
513
                        continue;
514
                    } else {
515
                            $this->assert(
516
                                ($resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE)
517
                             || ($resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE)
518
                             || ($resourcePropertyKind == ResourcePropertyKind::KEY_RESOURCE_REFERENCE),
519
                                '($resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE)
520
                             || ($resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE)
521
                             || ($resourcePropertyKind == ResourcePropertyKind::KEY_RESOURCE_REFERENCE)'
522
                            );
523
524
                        $navigationProperties[$i] = new NavigationPropertyInfo($resourceProperty, $this->shouldExpandSegment($resourceProperty->getName()));
525
                        if ($navigationProperties[$i]->expanded) {
526
                            $navigationProperties[$i]->value = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
527
                        }
528
529
                        $i++;
530
                    }
531
                }
532
            }
533
534
        } else { //This is the code path to handle projected properties of Entry
535
            $i = 0;
536
            foreach ($projectionNodes as $projectionNode) {
537
                $propertyName = $projectionNode->getPropertyName();
538
                $resourceProperty = $resourceType->resolveProperty($propertyName);
539
                $this->assert(!is_null($resourceProperty), '!is_null($resourceProperty)');
540
541
                if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY) {
542
                    $currentResourceSetWrapper2 = $this->getCurrentResourceSetWrapper();
543
                    $resourceProperties = $this->service
544
                        ->getProvidersWrapper()
545
                        ->getResourceProperties(
546
                            $currentResourceSetWrapper2,
547
                            $resourceType
548
                        );
549
                    //Check for the visibility of this navigation property
550
                    if (array_key_exists($resourceProperty->getName(), $resourceProperties)) {
551
                        $navigationProperties[$i] = new NavigationPropertyInfo($resourceProperty, $this->shouldExpandSegment($propertyName));
552
                        if ($navigationProperties[$i]->expanded) {
553
                            $navigationProperties[$i]->value = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
554
                        }
555
556
                        $i++;
557
                        continue;
558
                    }
559
                }
560
561
                //Primitve, complex or bag property
562
                $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
563
                $propertyTypeKind = $resourceProperty->getKind();
564
                $propertyResourceType = $resourceProperty->getResourceType();
565
                $this->assert(!is_null($propertyResourceType), '!is_null($propertyResourceType)');
566
                if (ResourceProperty::sIsKindOf($propertyTypeKind, ResourcePropertyKind::BAG)) {
567
                    $bagResourceType = $resourceProperty->getResourceType();
568
                    $this->_writeBagValue(
569
                        $propertyValue,
570
                        $propertyName,
571
                        $bagResourceType,
572
                        $relativeUri . '/' . $propertyName,
573
                        $odataPropertyContent
574
                    );
575
                } else if (ResourceProperty::sIsKindOf($propertyTypeKind, ResourcePropertyKind::PRIMITIVE)) {
576
                    $odataProperty = new ODataProperty();
577
                    $this->_writePrimitiveValue($propertyValue, $resourceProperty, $odataProperty);
578
                    $odataPropertyContent->properties[] = $odataProperty;
579
                } else if ($propertyTypeKind == ResourcePropertyKind::COMPLEX_TYPE) {
580
                    $complexResourceType = $resourceProperty->getResourceType();
581
                    $this->_writeComplexValue(
582
                        $propertyValue,
583
                        $propertyName,
584
                        $complexResourceType,
585
                        $relativeUri . '/' . $propertyName,
586
                        $odataPropertyContent
587
                    );
588
                } else {
589
                    //unexpected
590
                    $this->assert(false, '$propertyTypeKind = Primitive or Bag or ComplexType');
591
                }
592
            }
593
        }
594
595
        if (!is_null($navigationProperties)) {
0 ignored issues
show
introduced by
The condition is_null($navigationProperties) is always true.
Loading history...
596
            //Write out navigation properties (deferred or inline)
597
            foreach ($navigationProperties as $navigationPropertyInfo) {
598
                $propertyName = $navigationPropertyInfo->resourceProperty->getName();
599
                $type = $navigationPropertyInfo->resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE ?
600
                    'application/atom+xml;type=entry' : 'application/atom+xml;type=feed';
601
                $link = new ODataLink();
602
                $link->name = ODataConstants::ODATA_RELATED_NAMESPACE . $propertyName;
603
                $link->title = $propertyName;
604
                $link->type = $type;
605
                $link->url = $relativeUri . '/' . $propertyName;
606
607
                if ($navigationPropertyInfo->expanded) {
608
                    $propertyRelativeUri = $relativeUri . '/' . $propertyName;
609
                    $propertyAbsoluteUri = trim($absoluteUri, '/') . '/' . $propertyName;
610
                    $needPop = $this->pushSegmentForNavigationProperty($navigationPropertyInfo->resourceProperty);
611
                    $navigationPropertyKind = $navigationPropertyInfo->resourceProperty->getKind();
612
                    $this->assert(
613
                        $navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE
614
                        || $navigationPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE
615
                        || $navigationPropertyKind == ResourcePropertyKind::KEY_RESOURCE_REFERENCE,
616
                        '$navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE
617
                        || $navigationPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE
618
                        || $navigationPropertyKind == ResourcePropertyKind::KEY_RESOURCE_REFERENCE'
619
                    );
620
                    $currentResourceSetWrapper = $this->getCurrentResourceSetWrapper();
621
                    $this->assert(!is_null($currentResourceSetWrapper), '!is_null($currentResourceSetWrapper)');
622
                    $link->isExpanded = true;
623
                    if (!is_null($navigationPropertyInfo->value)) {
624
                        if ($navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE) {
625
                            $inlineFeed = new ODataFeed();
626
                            $link->isCollection = true;
627
                            $currentResourceType = $currentResourceSetWrapper->getResourceType();
628
                            $this->_writeFeedElements(
629
                                $navigationPropertyInfo->value,
630
                                $currentResourceType,
631
                                $propertyName,
632
                                $propertyAbsoluteUri,
633
                                $propertyRelativeUri,
634
                                $inlineFeed
635
                            );
636
                            $link->expandedResult = $inlineFeed;
637
                        } else {
638
639
                            $link->isCollection = false;
640
                            $currentResourceType1 = $currentResourceSetWrapper->getResourceType();
641
642
                            $link->expandedResult = $this->_writeEntryElement(
643
                                $navigationPropertyInfo->value,
644
                                $currentResourceType1,
645
                                $propertyAbsoluteUri,
646
                                $propertyRelativeUri
647
                            );
648
                        }
649
                    } else {
650
                        $link->expandedResult = null;
651
                    }
652
653
                    $this->popSegment($needPop);
654
                }
655
656
                $odataEntry->links[] = $link;
657
            }
658
        }
659
    }
660
661
    /**
662
     * Writes a primitive value and related information to the given
663
     * ODataProperty instance.
664
     *
665
     * @param mixed            &$primitiveValue   The primitive value to write.
666
     * @param ResourceProperty &$resourceProperty The metadata of the primitive
667
     *                                            property value.
668
     * @param ODataProperty    &$odataProperty    ODataProperty instance to which
669
     *                                            the primitive value and related
670
     *                                            information to write out.
671
     *
672
     * @throws ODataException If given value is not primitive.
673
     *
674
     * @return void
675
     */
676
    private function _writePrimitiveValue(&$primitiveValue,
677
        ResourceProperty &$resourceProperty, ODataProperty &$odataProperty
678
    ) {
679
        if (is_object($primitiveValue)) {
680
            //TODO ERROR: The property 'PropertyName'
681
            //is defined as primitive type but value is an object
682
        }
683
684
685
        $odataProperty->name = $resourceProperty->getName();
686
        $odataProperty->typeName = $resourceProperty->getInstanceType()->getFullTypeName();
0 ignored issues
show
Bug introduced by
The method getFullTypeName() does not exist on ReflectionClass. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

686
        $odataProperty->typeName = $resourceProperty->getInstanceType()->/** @scrutinizer ignore-call */ getFullTypeName();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
687
        if (is_null($primitiveValue)) {
688
            $odataProperty->value = null;
689
        } else {
690
            $resourceType = $resourceProperty->getResourceType();
691
            $this->_primitiveToString(
692
                $resourceType,
693
                $primitiveValue,
694
                $odataProperty->value
0 ignored issues
show
Bug introduced by
It seems like $odataProperty->value can also be of type POData\ObjectModel\ODataBagContent and POData\ObjectModel\ODataPropertyContent; however, parameter $stringValue of POData\ObjectModel\Objec...r::_primitiveToString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

694
                /** @scrutinizer ignore-type */ $odataProperty->value
Loading history...
695
            );
696
        }
697
    }
698
699
    /**
700
     * Write value of a complex object.
701
     *
702
     * @param mixed                &$complexValue         Complex object to write.
703
     * @param string               $propertyName          Name of the
704
     *                                                    complex property
705
     *                                                    whose value need
706
     *                                                    to be written.
707
     * @param ResourceType         &$resourceType         Expected type
708
     *                                                    of the property.
709
     * @param string               $relativeUri           Relative uri for the
710
     *                                                    complex type element.
711
     * @param ODataPropertyContent &$odataPropertyContent Content to write to.
712
     *
713
     * @return void
714
     */
715
    private function _writeComplexValue(&$complexValue,
716
        $propertyName, ResourceType &$resourceType, $relativeUri,
717
        ODataPropertyContent &$odataPropertyContent
718
    ) {
719
        $odataProperty = new ODataProperty();
720
        $odataProperty->name = $propertyName;
721
        if (is_null($complexValue)) {
722
            $odataProperty->value = null;
723
            $odataProperty->typeName = $resourceType->getFullName();
724
        } else {
725
            $content = new ODataPropertyContent();
726
            $actualType = $this->_complexObjectToContent(
727
                $complexValue,
728
                $propertyName,
729
                $resourceType,
730
                $relativeUri,
731
                $content
732
            );
733
734
            $odataProperty->typeName = $actualType->getFullName();
735
            $odataProperty->value = $content;
736
        }
737
738
        $odataPropertyContent->properties[] = $odataProperty;
739
    }
740
741
    /**
742
     * Write value of a bag instance.
743
     *
744
     * @param array/NULL           &$BagValue             Bag value to write.
0 ignored issues
show
Documentation Bug introduced by
The doc comment array/NULL at position 0 could not be parsed: Unknown type name 'array/NULL' at position 0 in array/NULL.
Loading history...
745
     * @param string               $propertyName          Property name of the bag.
746
     * @param ResourceType         &$resourceType         Type describing the
747
     *                                                    bag value.
748
     * @param string               $relativeUri           Relative Url to the bag.
749
     * @param ODataPropertyContent &$odataPropertyContent On return, this object
750
     *                                                    will hold bag value which
751
     *                                                    can be used by writers.
752
     *
753
     * @return void
754
     */
755
    private function _writeBagValue(&$BagValue,
756
        $propertyName, ResourceType &$resourceType, $relativeUri,
757
        ODataPropertyContent &$odataPropertyContent
758
    ) {
759
        $bagItemResourceTypeKind = $resourceType->getResourceTypeKind();
760
        $this->assert(
761
            $bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE
762
            || $bagItemResourceTypeKind == ResourceTypeKind::COMPLEX,
763
            '$bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE
764
            || $bagItemResourceTypeKind == ResourceTypeKind::COMPLEX'
765
        );
766
767
        $odataProperty = new ODataProperty();
768
        $odataProperty->name = $propertyName;
769
        $odataProperty->typeName = 'Collection(' . $resourceType->getFullName() . ')';
770
        if (is_null($BagValue) || (is_array($BagValue) && empty ($BagValue))) {
771
            $odataProperty->value = null;
772
        } else {
773
            $odataBagContent = new ODataBagContent();
774
            foreach ($BagValue as $itemValue) {
775
                if (!is_null($itemValue)) {
776
                    if ($bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE) {
777
                        $primitiveValueAsString = null;
778
                        $this->_primitiveToString($resourceType, $itemValue, $primitiveValueAsString);
779
                        $odataBagContent->propertyContents[] = $primitiveValueAsString;
780
                    } else if ($bagItemResourceTypeKind == ResourceTypeKind::COMPLEX) {
781
                        $complexContent = new ODataPropertyContent();
782
                        $actualType = $this->_complexObjectToContent(
0 ignored issues
show
Unused Code introduced by
The assignment to $actualType is dead and can be removed.
Loading history...
783
                            $itemValue,
784
                            $propertyName,
785
                            $resourceType,
786
                            $relativeUri,
787
                            $complexContent
788
                        );
789
                        //TODO add type in case of base type
790
                        $odataBagContent->propertyContents[] = $complexContent;
791
                    }
792
                }
793
            }
794
795
            $odataProperty->value = $odataBagContent;
796
        }
797
798
        $odataPropertyContent->properties[] = $odataProperty;
799
    }
800
801
    /**
802
     * Write media resource metadata (for MLE and Named Streams)
803
     *
804
     * @param mixed        $entryObject  The entry instance being serialized.
805
     * @param ResourceType &$resourceType Resource type of the entry instance.
806
     * @param string       $title         Title for the current
807
     *                                    current entry instance.
808
     * @param string       $relativeUri   Relative uri for the
809
     *                                    current entry instance.
810
     * @param ODataEntry   &$odataEntry   OData entry to write to.
811
     *
812
     * @return void
813
     */
814
    private function _writeMediaResourceMetadata(
815
        $entryObject,
816
        ResourceType &$resourceType,
817
        $title,
818
        $relativeUri,
819
        ODataEntry &$odataEntry
820
    ) {
821
        if ($resourceType->isMediaLinkEntry()) {
822
            $odataEntry->isMediaLinkEntry = true;
823
            $streamProvider = $this->service->getStreamProvider();
0 ignored issues
show
Bug introduced by
The method getStreamProvider() does not exist on POData\IService. Did you maybe mean getStreamProviderWrapper()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

823
            /** @scrutinizer ignore-call */ $streamProvider = $this->service->getStreamProvider();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
824
            $eTag = $streamProvider->getStreamETag($entryObject, null);
825
            $readStreamUri = $streamProvider->getReadStreamUri($entryObject, null, $relativeUri);
826
            $mediaContentType = $streamProvider->getStreamContentType($entryObject, null);
827
            $mediaLink = new ODataMediaLink(
828
                $title,
829
                $streamProvider->getDefaultStreamEditMediaUri($relativeUri, null),
830
                $readStreamUri,
831
                $mediaContentType,
832
                $eTag
833
            );
834
835
            $odataEntry->mediaLink = $mediaLink;
836
        }
837
838
        if ($resourceType->hasNamedStream()) {
839
            foreach ($resourceType->getAllNamedStreams() as $title => $resourceStreamInfo) {
0 ignored issues
show
introduced by
$title is overwriting one of the parameters of this function.
Loading history...
840
                $eTag = $streamProvider->getStreamETag($entryObject, $resourceStreamInfo);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $streamProvider does not seem to be defined for all execution paths leading up to this point.
Loading history...
841
                $readStreamUri = $streamProvider->getReadStreamUri($entryObject, $resourceStreamInfo, $relativeUri);
842
                $mediaContentType = $streamProvider->getStreamContentType($entryObject, $resourceStreamInfo);
843
                $odataEntry->mediaLinks[] = new ODataMediaLink(
844
                    $title,
845
                    $streamProvider->getDefaultStreamEditMediaUri($relativeUri, $resourceStreamInfo),
846
                    $readStreamUri,
847
                    $mediaContentType,
848
                    $eTag
849
                );
850
            }
851
        }
852
    }
853
854
    /**
855
     * Convert the given primitive value to string.
856
     * Note: This method will not handle null primitive value.
857
     *
858
     * @param ResourceType &$primtiveResourceType Type of the primitive property
859
     *                                            whose value need to be converted.
860
     * @param mixed        $primitiveValue        Primitive value to convert.
861
     * @param string       &$stringValue          On return, this parameter will
862
     *                                            contain converted value.
863
     *
864
     * @return void
865
     */
866
    private function _primitiveToString(ResourceType &$primtiveResourceType,
867
        $primitiveValue, &$stringValue
868
    ) {
869
        $type = $primtiveResourceType->getInstanceType();
870
        if ($type instanceof Boolean) {
871
            $stringValue = ($primitiveValue === true) ? 'true' : 'false';
872
        } else if ($type instanceof Binary) {
873
            $stringValue = base64_encode($primitiveValue);
874
        } else if (($type instanceof DateTime || $type instanceof StringType) && $primitiveValue instanceof \DateTime) {
875
            $stringValue = $primitiveValue->format(\DateTime::ATOM);
876
        } else if ($type instanceof StringType && $primitiveValue instanceof \DateInterval) {
877
            $stringValue = (($primitiveValue->d * 86400) + ($primitiveValue->h * 3600) + ($primitiveValue->i * 60) + $primitiveValue->s) * 1000;
878
           // $stringValue = intval($primitiveValue->format('%s'))*1000; // Miliszekundumokkáé
879
        } else if ($type instanceof StringType) {
880
            $stringValue = mb_convert_encoding($primitiveValue, 'UTF-8');
881
        } else {
882
            $stringValue = strval($primitiveValue);
883
        }
884
    }
885
886
    /**
887
     * Write value of a complex object.
888
     * Note: This method will not handle null complex value.
889
     *
890
     * @param mixed                &$complexValue         Complex object to write.
891
     * @param string               $propertyName          Name of the
892
     *                                                    complex property
893
     *                                                    whose value
894
     *                                                    need to be written.
895
     * @param ResourceType         &$resourceType         Expected type of the
896
     *                                                    property.
897
     * @param string               $relativeUri           Relative uri for the
898
     *                                                    complex type element.
899
     * @param ODataPropertyContent &$odataPropertyContent Content to write to.
900
     *
901
     * @return ResourceType The actual type of the complex object.
902
     *
903
     * @return void
904
     */
905
    private function _complexObjectToContent(&$complexValue,
906
        $propertyName, ResourceType &$resourceType, $relativeUri,
907
        ODataPropertyContent &$odataPropertyContent
908
    ) {
909
        $count = count($this->complexTypeInstanceCollection);
910
        for ($i = 0; $i < $count; $i++) {
911
            if ($this->complexTypeInstanceCollection[$i] === $complexValue) {
912
                throw new InvalidOperationException(Messages::objectModelSerializerLoopsNotAllowedInComplexTypes($propertyName));
913
            }
914
        }
915
916
        $this->complexTypeInstanceCollection[$count] = &$complexValue;
917
918
        //TODO function to resolve actual type from $resourceType
919
        $actualType = $resourceType;
920
        $odataEntry = null;
921
        $this->_writeObjectProperties(
922
            $complexValue, $actualType,
923
            null, $relativeUri, $odataEntry, $odataPropertyContent
924
        );
925
        unset($this->complexTypeInstanceCollection[$count]);
926
        return $actualType;
927
    }
928
}
929