Passed
Push — master ( 4ab488...bcfbc7 )
by Bálint
03:58
created

ObjectModelSerializer::writeUrlElements()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 20
c 0
b 0
f 0
rs 9.2222
cc 6
nc 6
nop 1
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) {
0 ignored issues
show
introduced by
The condition $requestTargetSource == ...argetSource::ENTITY_SET is always false.
Loading history...
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) {
0 ignored issues
show
introduced by
The condition $requestTargetSource == ...argetSource::ENTITY_SET is always false.
Loading history...
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
            $odataPropertyContent = new ODataPropertyContent();
308
            $this->_writeObjectProperties(
309
                $entryObject,
310
                $actualResourceType,
311
                $absoluteUri,
312
                $relativeUri,
313
                $entry,
314
                $odataPropertyContent
315
            );
316
            $entry->propertyContent = $odataPropertyContent;
317
        }
318
319
        return $entry;
320
    }
321
322
    /**
323
     * Writes the feed elements
324
     *
325
     * @param array        &$entryObjects Array of entries in the feed element.
326
     * @param ResourceType &$resourceType The resource type of the f the elements
327
     *                                    in the collection.
328
     * @param string       $title         Title of the feed element.
329
     * @param string       $absoluteUri   Absolute uri representing the feed element.
330
     * @param string       $relativeUri   Relative uri representing the feed element.
331
     * @param ODataFeed    &$feed    Feed to write to.
332
     *
333
     * @return void
334
     */
335
    private function _writeFeedElements(
336
        &$entryObjects,
337
        ResourceType &$resourceType,
338
        $title,
339
        $absoluteUri,
340
        $relativeUri,
341
        ODataFeed &$feed
342
    ) {
343
        $this->assert(is_array($entryObjects) || $entryObjects instanceof ArrayAccess, '_writeFeedElements::is_array($entryObjects)');
344
        $feed->id = $absoluteUri;
345
        $feed->title = $title;
346
        $feed->selfLink = new ODataLink();
347
        $feed->selfLink->name = ODataConstants::ATOM_SELF_RELATION_ATTRIBUTE_VALUE;
348
        $feed->selfLink->title = $title;
349
        $feed->selfLink->url = $relativeUri;
350
351
        if (empty($entryObjects)) {
352
            //TODO // ATOM specification: if a feed contains no entries,
353
            //then the feed should have at least one Author tag
354
        } else {
355
            foreach ($entryObjects as $entryObject) {
356
                $feed->entries[] = $this->_writeEntryElement($entryObject, $resourceType, null, null);
357
            }
358
359
            if ($this->needNextPageLink(count($entryObjects))) {
360
                $end = end($entryObjects);
361
                $feed->nextPageLink = $this->getNextLinkUri($end, $absoluteUri);
362
            }
363
        }
364
    }
365
366
    /**
367
     * Write values of properties of given entry (resource) or complex object.
368
     *
369
     * @param mixed                $customObject          Entity or complex object
370
     *                                                    with properties
371
     *                                                    to write out.
372
     * @param ResourceType         &$resourceType         Resource type describing
373
     *                                                    the metadata of
374
     *                                                    the custom object.
375
     * @param string               $absoluteUri           Absolute uri for the given
376
     *                                                    entry object
377
     *                                                    NULL for complex object.
378
     * @param string               $relativeUri           Relative uri for the given
379
     *                                                    custom object.
380
     * @param ODataEntry           ODataEntry|null           ODataEntry instance to
381
     *                                                    place links and
382
     *                                                    expansion of the
383
     *                                                    entry object,
384
     *                                                    NULL for complex object.
385
     * @param ODataPropertyContent &$odataPropertyContent ODataPropertyContent
386
     *                                                    instance in which
387
     *                                                    to place the values.
388
     *
389
     * @return void
390
     */
391
    private function _writeObjectProperties(
392
        $customObject,
393
        ResourceType &$resourceType,
394
        $absoluteUri,
395
        $relativeUri,
396
        &$odataEntry,
397
        ODataPropertyContent &$odataPropertyContent
398
    ) {
399
        $resourceTypeKind = $resourceType->getResourceTypeKind();
400
        if (is_null($absoluteUri) == ($resourceTypeKind == ResourceTypeKind::ENTITY)
401
        ) {
402
            throw ODataException::createInternalServerError(
403
                Messages::badProviderInconsistentEntityOrComplexTypeUsage(
404
                    $resourceType->getName()
405
                )
406
            );
407
        }
408
409
        $this->assert(
410
            (($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry))
411
            || (($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry)),
412
            '(($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry))
413
            || (($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry))'
414
        );
415
        $projectionNodes = null;
416
        $navigationProperties = null;
417
        if ($resourceTypeKind == ResourceTypeKind::ENTITY) {
0 ignored issues
show
introduced by
The condition $resourceTypeKind == POD...esourceTypeKind::ENTITY is always false.
Loading history...
418
            $projectionNodes = $this->getProjectionNodes();
419
            $navigationProperties = array();
420
        }
421
422
        if (is_null($projectionNodes)) {
0 ignored issues
show
introduced by
The condition is_null($projectionNodes) is always true.
Loading history...
423
            //This is the code path to handle properties of Complex type
424
            //or Entry without projection (i.e. no expansion or selection)
425
            $resourceProperties = array();
426
            if ($resourceTypeKind == ResourceTypeKind::ENTITY) {
0 ignored issues
show
introduced by
The condition $resourceTypeKind == POD...esourceTypeKind::ENTITY is always false.
Loading history...
427
                // If custom object is an entry then it can contain navigation
428
                // properties which are invisible (because the corresponding
429
                // resource set is invisible).
430
                // IDSMP::getResourceProperties will give collection of properties
431
                // which are visible.
432
                $currentResourceSetWrapper1 = $this->getCurrentResourceSetWrapper();
433
                $resourceProperties = $this->service
434
                    ->getProvidersWrapper()
435
                    ->getResourceProperties(
436
                        $currentResourceSetWrapper1,
437
                        $resourceType
438
                    );
439
            } else {
440
                $resourceProperties = $resourceType->getAllProperties();
441
            }
442
443
            //First write out primitve types
444
            foreach ($resourceProperties as $name => $resourceProperty) {
445
                if ($resourceProperty->getKind() == ResourcePropertyKind::PRIMITIVE
446
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY)
447
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG)
448
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)
449
                ) {
450
                    $odataProperty = new ODataProperty();
451
                    $primitiveValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
452
                    $this->_writePrimitiveValue($primitiveValue, $resourceProperty, $odataProperty);
453
                    $odataPropertyContent->properties[] = $odataProperty;
454
                }
455
            }
456
457
            //Write out bag and complex type
458
            $i = 0;
459
            foreach ($resourceProperties as $resourceProperty) {
460
                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

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

661
        $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...
662
        if (is_null($primitiveValue)) {
663
            $odataProperty->value = null;
664
        } else {
665
            $resourceType = $resourceProperty->getResourceType();
666
            $this->_primitiveToString(
667
                $resourceType,
668
                $primitiveValue,
669
                $odataProperty->value
0 ignored issues
show
Bug introduced by
It seems like $odataProperty->value can also be of type POData\ObjectModel\ODataPropertyContent and POData\ObjectModel\ODataBagContent; 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

669
                /** @scrutinizer ignore-type */ $odataProperty->value
Loading history...
670
            );
671
        }
672
    }
673
674
    /**
675
     * Write value of a complex object.
676
     *
677
     * @param mixed                &$complexValue         Complex object to write.
678
     * @param string               $propertyName          Name of the
679
     *                                                    complex property
680
     *                                                    whose value need
681
     *                                                    to be written.
682
     * @param ResourceType         &$resourceType         Expected type
683
     *                                                    of the property.
684
     * @param string               $relativeUri           Relative uri for the
685
     *                                                    complex type element.
686
     * @param ODataPropertyContent &$odataPropertyContent Content to write to.
687
     *
688
     * @return void
689
     */
690
    private function _writeComplexValue(&$complexValue,
691
        $propertyName, ResourceType &$resourceType, $relativeUri,
692
        ODataPropertyContent &$odataPropertyContent
693
    ) {
694
        $odataProperty = new ODataProperty();
695
        $odataProperty->name = $propertyName;
696
        if (is_null($complexValue)) {
697
            $odataProperty->value = null;
698
            $odataProperty->typeName = $resourceType->getFullName();
699
        } else {
700
            $content = new ODataPropertyContent();
701
            $actualType = $this->_complexObjectToContent(
702
                $complexValue,
703
                $propertyName,
704
                $resourceType,
705
                $relativeUri,
706
                $content
707
            );
708
709
            $odataProperty->typeName = $actualType->getFullName();
710
            $odataProperty->value = $content;
711
        }
712
713
        $odataPropertyContent->properties[] = $odataProperty;
714
    }
715
716
    /**
717
     * Write value of a bag instance.
718
     *
719
     * @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...
720
     * @param string               $propertyName          Property name of the bag.
721
     * @param ResourceType         &$resourceType         Type describing the
722
     *                                                    bag value.
723
     * @param string               $relativeUri           Relative Url to the bag.
724
     * @param ODataPropertyContent &$odataPropertyContent On return, this object
725
     *                                                    will hold bag value which
726
     *                                                    can be used by writers.
727
     *
728
     * @return void
729
     */
730
    private function _writeBagValue(&$BagValue,
731
        $propertyName, ResourceType &$resourceType, $relativeUri,
732
        ODataPropertyContent &$odataPropertyContent
733
    ) {
734
        $bagItemResourceTypeKind = $resourceType->getResourceTypeKind();
735
        $this->assert(
736
            $bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE
737
            || $bagItemResourceTypeKind == ResourceTypeKind::COMPLEX,
738
            '$bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE
739
            || $bagItemResourceTypeKind == ResourceTypeKind::COMPLEX'
740
        );
741
742
        $odataProperty = new ODataProperty();
743
        $odataProperty->name = $propertyName;
744
        $odataProperty->typeName = 'Collection(' . $resourceType->getFullName() . ')';
745
        if (is_null($BagValue) || (is_array($BagValue) && empty ($BagValue))) {
746
            $odataProperty->value = null;
747
        } else {
748
            $odataBagContent = new ODataBagContent();
749
            foreach ($BagValue as $itemValue) {
750
                if (!is_null($itemValue)) {
751
                    if ($bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE) {
752
                        $primitiveValueAsString = null;
753
                        $this->_primitiveToString($resourceType, $itemValue, $primitiveValueAsString);
754
                        $odataBagContent->propertyContents[] = $primitiveValueAsString;
755
                    } else if ($bagItemResourceTypeKind == ResourceTypeKind::COMPLEX) {
756
                        $complexContent = new ODataPropertyContent();
757
                        $actualType = $this->_complexObjectToContent(
0 ignored issues
show
Unused Code introduced by
The assignment to $actualType is dead and can be removed.
Loading history...
758
                            $itemValue,
759
                            $propertyName,
760
                            $resourceType,
761
                            $relativeUri,
762
                            $complexContent
763
                        );
764
                        //TODO add type in case of base type
765
                        $odataBagContent->propertyContents[] = $complexContent;
766
                    }
767
                }
768
            }
769
770
            $odataProperty->value = $odataBagContent;
771
        }
772
773
        $odataPropertyContent->properties[] = $odataProperty;
774
    }
775
776
    /**
777
     * Write media resource metadata (for MLE and Named Streams)
778
     *
779
     * @param mixed        $entryObject  The entry instance being serialized.
780
     * @param ResourceType &$resourceType Resource type of the entry instance.
781
     * @param string       $title         Title for the current
782
     *                                    current entry instance.
783
     * @param string       $relativeUri   Relative uri for the
784
     *                                    current entry instance.
785
     * @param ODataEntry   &$odataEntry   OData entry to write to.
786
     *
787
     * @return void
788
     */
789
    private function _writeMediaResourceMetadata(
790
        $entryObject,
791
        ResourceType &$resourceType,
792
        $title,
793
        $relativeUri,
794
        ODataEntry &$odataEntry
795
    ) {
796
        if ($resourceType->isMediaLinkEntry()) {
797
            $odataEntry->isMediaLinkEntry = true;
798
            $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

798
            /** @scrutinizer ignore-call */ 
799
            $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...
799
            $eTag = $streamProvider->getStreamETag($entryObject, null);
800
            $readStreamUri = $streamProvider->getReadStreamUri($entryObject, null, $relativeUri);
801
            $mediaContentType = $streamProvider->getStreamContentType($entryObject, null);
802
            $mediaLink = new ODataMediaLink(
803
                $title,
804
                $streamProvider->getDefaultStreamEditMediaUri($relativeUri, null),
805
                $readStreamUri,
806
                $mediaContentType,
807
                $eTag
808
            );
809
810
            $odataEntry->mediaLink = $mediaLink;
811
        }
812
813
        if ($resourceType->hasNamedStream()) {
814
            foreach ($resourceType->getAllNamedStreams() as $title => $resourceStreamInfo) {
0 ignored issues
show
introduced by
$title is overwriting one of the parameters of this function.
Loading history...
815
                $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...
816
                $readStreamUri = $streamProvider->getReadStreamUri($entryObject, $resourceStreamInfo, $relativeUri);
817
                $mediaContentType = $streamProvider->getStreamContentType($entryObject, $resourceStreamInfo);
818
                $odataEntry->mediaLinks[] = new ODataMediaLink(
819
                    $title,
820
                    $streamProvider->getDefaultStreamEditMediaUri($relativeUri, $resourceStreamInfo),
821
                    $readStreamUri,
822
                    $mediaContentType,
823
                    $eTag
824
                );
825
            }
826
        }
827
    }
828
    /**
829
     * Convert the given primitive value to string.
830
     * Note: This method will not handle null primitive value.
831
     *
832
     * @param ResourceType &$primtiveResourceType Type of the primitive property
833
     *                                            whose value need to be converted.
834
     * @param mixed        $primitiveValue        Primitive value to convert.
835
     * @param string       &$stringValue          On return, this parameter will
836
     *                                            contain converted value.
837
     *
838
     * @return void
839
     */
840
    private function _primitiveToString(ResourceType &$primtiveResourceType,
841
        $primitiveValue, &$stringValue
842
    ) {
843
        $type = $primtiveResourceType->getInstanceType();
844
        if ($type instanceof Boolean) {
845
            $stringValue = ($primitiveValue === true) ? 'true' : 'false';
846
        } else if ($type instanceof Binary) {
847
            $stringValue = base64_encode($primitiveValue);
848
        } else if ($type instanceof DateTime && $primitiveValue instanceOf \DateTime) {
849
            $stringValue = $primitiveValue->format(\DateTime::ATOM);
850
        } else if ($type instanceof StringType) {
851
            $stringValue = mb_convert_encoding($primitiveValue, 'UTF-8');
852
        } else {
853
            $stringValue = strval($primitiveValue);
854
        }
855
    }
856
857
    /**
858
     * Write value of a complex object.
859
     * Note: This method will not handle null complex value.
860
     *
861
     * @param mixed                &$complexValue         Complex object to write.
862
     * @param string               $propertyName          Name of the
863
     *                                                    complex property
864
     *                                                    whose value
865
     *                                                    need to be written.
866
     * @param ResourceType         &$resourceType         Expected type of the
867
     *                                                    property.
868
     * @param string               $relativeUri           Relative uri for the
869
     *                                                    complex type element.
870
     * @param ODataPropertyContent &$odataPropertyContent Content to write to.
871
     *
872
     * @return ResourceType The actual type of the complex object.
873
     *
874
     * @return void
875
     */
876
    private function _complexObjectToContent(&$complexValue,
877
        $propertyName, ResourceType &$resourceType, $relativeUri,
878
        ODataPropertyContent &$odataPropertyContent
879
    ) {
880
        $count = count($this->complexTypeInstanceCollection);
881
        for ($i = 0; $i < $count; $i++) {
882
            if ($this->complexTypeInstanceCollection[$i] === $complexValue) {
883
                throw new InvalidOperationException(Messages::objectModelSerializerLoopsNotAllowedInComplexTypes($propertyName));
884
            }
885
        }
886
887
        $this->complexTypeInstanceCollection[$count] = &$complexValue;
888
889
        //TODO function to resolve actual type from $resourceType
890
        $actualType = $resourceType;
891
        $odataEntry = null;
892
        $this->_writeObjectProperties(
893
            $complexValue, $actualType,
894
            null, $relativeUri, $odataEntry, $odataPropertyContent
895
        );
896
        unset($this->complexTypeInstanceCollection[$count]);
897
        return $actualType;
898
    }
899
}
900