Test Failed
Push — master ( f2073e...d0d4fa )
by Bálint
05:24
created

_writeCustomTopLevelProperties()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 13
rs 10
cc 2
nc 2
nop 0
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
74
        $toplevel_properties = $this->_writeCustomTopLevelProperties();
75
        array_push($entry->customProperties->properties,...$toplevel_properties);
76
77
        $this->popSegment($needPop);
78
        return $entry;
79
    }
80
81
    /**
82
     * Write top level feed element.
83
     *
84
     * @param array &$entryObjects Array of entry resources to be written.
85
     *
86
     * @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...
87
     */
88
    public function writeTopLevelElements(&$entryObjects)
89
    {
90
        $this->assert(is_iterable($entryObjects), 'is_iterable($entryObjects)');
91
        $requestTargetSource = $this->request->getTargetSource();
92
        $title = null;
93
        if ($requestTargetSource == TargetSource::ENTITY_SET) {
94
            $title = $this->request->getContainerName();
95
        } else {
96
            $this->assert(
97
                $requestTargetSource == TargetSource::PROPERTY,
98
                '$requestTargetSource == TargetSource::PROPERTY'
99
            );
100
            $resourceProperty = $this->request->getProjectedProperty();
101
            $this->assert(
102
                $resourceProperty->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE,
103
                '$resourceProperty->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE'
104
            );
105
            $title = $resourceProperty->getName();
106
        }
107
108
        $relativeUri = $this->request->getIdentifier();
109
        $feed = new ODataFeed();
110
111
        if ($this->request->queryType == QueryType::ENTITIES_WITH_COUNT) {
112
            $feed->rowCount = $this->request->getCountValue();
113
        }
114
115
        $toplevel_properties =  $this->_writeCustomTopLevelProperties();
116
        array_push($feed->customProperties->properties,...$toplevel_properties);
117
118
        $needPop = $this->pushSegmentForRoot();
119
        $targetResourceType = $this->request->getTargetResourceType();
120
        $this->_writeFeedElements(
121
            $entryObjects,
122
            $targetResourceType,
123
            $title,
124
            $this->request->getRequestUrl()->getUrlAsString(),
125
            $relativeUri,
126
            $feed
127
        );
128
        $this->popSegment($needPop);
129
        return $feed;
130
    }
131
132
    /**
133
     * Write top level url element.
134
     *
135
     * @param mixed $entryObject The entry resource whose url to be written.
136
     *
137
     * @return ODataURL
138
     */
139
    public function writeUrlElement($entryObject)
140
    {
141
        $url = new ODataURL();
142
        if (!is_null($entryObject)) {
143
            $currentResourceType = $this->getCurrentResourceSetWrapper()->getResourceType();
144
            $relativeUri = $this->getEntryInstanceKey(
145
                $entryObject,
146
                $currentResourceType,
147
                $this->getCurrentResourceSetWrapper()->getName()
148
            );
149
150
            $url->url = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
151
        }
152
153
        return $url;
154
    }
155
156
    /**
157
     * Write top level url collection.
158
     *
159
     * @param array $entryObjects Array of entry resources
160
     * whose url to be written.
161
     *
162
     * @return ODataURLCollection
163
     */
164
    public function writeUrlElements($entryObjects)
165
    {
166
        $urls = new ODataURLCollection();
167
        if (!empty($entryObjects)) {
168
            $i = 0;
169
            foreach ($entryObjects as $entryObject) {
170
                $urls->urls[$i] = $this->writeUrlElement($entryObject);
171
                $i++;
172
            }
173
174
            if ($i > 0 && $this->needNextPageLink(count($entryObjects))) {
175
                $urls->nextPageLink = $this->getNextLinkUri($entryObjects[$i - 1], $this->request->getRequestUrl()->getUrlAsString());
176
            }
177
        }
178
179
        if ($this->request->queryType == QueryType::ENTITIES_WITH_COUNT) {
180
            $urls->count = $this->request->getCountValue();
181
        }
182
183
        return $urls;
184
    }
185
186
    /**
187
     * Write top level complex resource.
188
     *
189
     * @param mixed                &$complexValue         The complex object to be
190
     *                                                    written.
191
     * @param string               $propertyName          The name of the
192
     *                                                    complex property.
193
     * @param ResourceType         &$resourceType         Describes the type of
194
     *                                                    complex object.
195
     *
196
     * @return ODataPropertyContent
197
     */
198
    public function writeTopLevelComplexObject(
199
        &$complexValue,
200
        $propertyName,
201
        ResourceType &$resourceType
202
    ) {
203
        $propertyContent = new ODataPropertyContent();
204
        $this->_writeComplexValue(
205
            $complexValue,
206
            $propertyName, $resourceType, null,
207
            $propertyContent
208
        );
209
210
        return $propertyContent;
211
    }
212
213
    /**
214
     * Write top level bag resource.
215
     *
216
     * @param mixed                &$BagValue             The bag object to be
217
     *                                                    written.
218
     * @param string               $propertyName          The name of the
219
     *                                                    bag property.
220
     * @param ResourceType         &$resourceType         Describes the type of
221
     *                                                    bag object.
222
     *
223
     * @return ODataPropertyContent
224
     */
225
    public function writeTopLevelBagObject(
226
        &$BagValue,
227
        $propertyName,
228
        ResourceType &$resourceType
229
    ) {
230
231
        $propertyContent = new ODataPropertyContent();
232
        $this->_writeBagValue(
233
            $BagValue,
234
            $propertyName, $resourceType, null,
235
            $propertyContent
236
        );
237
238
        return $propertyContent;
239
    }
240
241
    /**
242
     * Write top level primitive value.
243
     *
244
     * @param mixed                &$primitiveValue       The primitve value to be
245
     *                                                    written.
246
     * @param ResourceProperty     &$resourceProperty     Resource property
247
     *                                                    describing the
248
     *                                                    primitive property
249
     *                                                    to be written.
250
     *
251
     * @return ODataPropertyContent
252
     */
253
    public function writeTopLevelPrimitive(
254
        &$primitiveValue,
255
        ResourceProperty &$resourceProperty
256
    ) {
257
        $propertyContent = new ODataPropertyContent();
258
        $propertyContent->properties[] = new ODataProperty();
259
        $this->_writePrimitiveValue(
260
            $primitiveValue,
261
            $resourceProperty,
262
            $propertyContent->properties[0]
263
        );
264
265
        return $propertyContent;
266
    }
267
268
    /**
269
     * Write an entry element.
270
     *
271
     * @param mixed        $entryObject  Object representing entry element.
272
     * @param ResourceType $resourceType Expected type of the entry object.
273
     * @param string       $absoluteUri   Absolute uri of the entry element.
274
     * @param string       $relativeUri   Relative uri of the entry element.
275
     *
276
     * @return ODataEntry
277
     */
278
    private function _writeEntryElement(
279
        $entryObject,
280
        ResourceType $resourceType,
281
        $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

281
        /** @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...
282
        $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

282
        /** @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...
283
    ) {
284
        $entry = new ODataEntry();
285
        $entry->resourceSetName = $this->getCurrentResourceSetWrapper()->getName();
286
287
        if (is_null($entryObject)) {
288
            //According to atom standard an empty entry must have an Author
289
            //node.
290
        } else {
291
            $actualResourceType = $this->service->getProvidersWrapper()->resolveResourceTypeByClassname(get_class($entryObject));
292
            if ($actualResourceType) {
293
                $actualResourceSet = $actualResourceType->getCustomState();
294
            } else {
295
                $actualResourceType = $resourceType;
296
                $actualResourceSet = $this->getCurrentResourceSetWrapper();
297
            }
298
            $relativeUri = $this->getEntryInstanceKey(
299
                $entryObject,
300
                $actualResourceType,
301
                $actualResourceSet->getName()
302
            );
303
            $entry->resourceSetName = $actualResourceSet->getName();
304
305
            $absoluteUri = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
306
            $title = $resourceType->getName();
307
            $this->_writeMediaResourceMetadata(
308
                $entryObject,
309
                $actualResourceType,
310
                $title,
311
                $relativeUri,
312
                $entry
313
            );
314
315
            $entry->id = $absoluteUri;
316
            $entry->eTag = $this->getETagForEntry($entryObject, $resourceType);
317
            $entry->title = $title;
318
            $entry->editLink = $relativeUri;
319
            $entry->type = $actualResourceType->getFullName();
320
321
            $this->_writeCustomProperties(
322
                $entryObject,
323
                $entry->customProperties
324
            );
325
326
            $odataPropertyContent = new ODataPropertyContent();
327
            $this->_writeObjectProperties(
328
                $entryObject,
329
                $actualResourceType,
330
                $absoluteUri,
331
                $relativeUri,
332
                $entry,
333
                $odataPropertyContent
334
            );
335
            $entry->propertyContent = $odataPropertyContent;
336
        }
337
338
        return $entry;
339
    }
340
341
    /**
342
     * Writes the feed elements
343
     *
344
     * @param array        &$entryObjects Array of entries in the feed element.
345
     * @param ResourceType &$resourceType The resource type of the f the elements
346
     *                                    in the collection.
347
     * @param string       $title         Title of the feed element.
348
     * @param string       $absoluteUri   Absolute uri representing the feed element.
349
     * @param string       $relativeUri   Relative uri representing the feed element.
350
     * @param ODataFeed    &$feed    Feed to write to.
351
     *
352
     * @return void
353
     */
354
    private function _writeFeedElements(
355
        &$entryObjects,
356
        ResourceType &$resourceType,
357
        $title,
358
        $absoluteUri,
359
        $relativeUri,
360
        ODataFeed &$feed
361
    ) {
362
        $this->assert(is_iterable($entryObjects) || $entryObjects instanceof ArrayAccess, '_writeFeedElements::is_iterable($entryObjects)');
363
        $feed->id = $absoluteUri;
364
        $feed->title = $title;
365
        $feed->selfLink = new ODataLink();
366
        $feed->selfLink->name = ODataConstants::ATOM_SELF_RELATION_ATTRIBUTE_VALUE;
367
        $feed->selfLink->title = $title;
368
        $feed->selfLink->url = $relativeUri;
369
370
        if (empty($entryObjects)) {
371
            //TODO // ATOM specification: if a feed contains no entries,
372
            //then the feed should have at least one Author tag
373
        } else {
374
            foreach ($entryObjects as $entryObject) {
375
                $feed->entries[] = $this->_writeEntryElement($entryObject, $resourceType, null, null);
376
            }
377
378
            if ($this->needNextPageLink(count($entryObjects))) {
379
                $end = method_exists($entryObjects, 'last') ? $entryObjects->last() : end($entryObjects);
380
                $feed->nextPageLink = $this->getNextLinkUri($end, $absoluteUri);
381
            }
382
        }
383
    }
384
385
    private function _writeCustomProperties(
386
        $customObject,
387
        ODataPropertyContent &$odataPropertyContent
388
    )
389
    {
390
        $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

390
        $properties = $this->service->/** @scrutinizer ignore-call */ getQueryProvider()->getCustomProperties($customObject);
Loading history...
391
392
        //First write out primitve types
393
        foreach ($properties as $name => $value) {
394
            $odataProperty = new ODataProperty();
395
            $odataProperty->name = $name;
396
            $odataProperty->value = $value;
397
            $odataPropertyContent->properties[] = $odataProperty;
398
        }
399
    }
400
401
    private function _writeCustomTopLevelProperties()
402
    {
403
        $odataProperties= [];
404
        $properties = $this->service->getQueryProvider()->getCustomFeedProperties();
405
406
        //First write out primitve types
407
        foreach ($properties as $name => $value) {
408
            $odataProperty = new ODataProperty();
409
            $odataProperty->name = $name;
410
            $odataProperty->value = $value;
411
            $odataProperties[] = $odataProperty;
412
        }
413
        return $odataProperties;
414
    }
415
416
    /**
417
     * Write values of properties of given entry (resource) or complex object.
418
     *
419
     * @param mixed                $customObject          Entity or complex object
420
     *                                                    with properties
421
     *                                                    to write out.
422
     * @param ResourceType         &$resourceType         Resource type describing
423
     *                                                    the metadata of
424
     *                                                    the custom object.
425
     * @param string               $absoluteUri           Absolute uri for the given
426
     *                                                    entry object
427
     *                                                    NULL for complex object.
428
     * @param string               $relativeUri           Relative uri for the given
429
     *                                                    custom object.
430
     * @param ODataEntry           ODataEntry|null           ODataEntry instance to
431
     *                                                    place links and
432
     *                                                    expansion of the
433
     *                                                    entry object,
434
     *                                                    NULL for complex object.
435
     * @param ODataPropertyContent &$odataPropertyContent ODataPropertyContent
436
     *                                                    instance in which
437
     *                                                    to place the values.
438
     *
439
     * @return void
440
     */
441
    private function _writeObjectProperties(
442
        $customObject,
443
        ResourceType &$resourceType,
444
        $absoluteUri,
445
        $relativeUri,
446
        &$odataEntry,
447
        ODataPropertyContent &$odataPropertyContent
448
    ) {
449
        $resourceTypeKind = $resourceType->getResourceTypeKind();
450
        if (is_null($absoluteUri) == ($resourceTypeKind == ResourceTypeKind::ENTITY)
451
        ) {
452
            throw ODataException::createInternalServerError(
453
                Messages::badProviderInconsistentEntityOrComplexTypeUsage(
454
                    $resourceType->getName()
455
                )
456
            );
457
        }
458
459
        $this->assert(
460
            (($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry))
461
            || (($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry)),
462
            '(($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry))
463
            || (($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry))'
464
        );
465
        $projectionNodes = null;
466
        $navigationProperties = null;
467
        if ($resourceTypeKind == ResourceTypeKind::ENTITY) {
0 ignored issues
show
introduced by
The condition $resourceTypeKind == POD...esourceTypeKind::ENTITY is always false.
Loading history...
468
            $projectionNodes = $this->getProjectionNodes();
469
            $navigationProperties = array();
470
        }
471
472
        if (is_null($projectionNodes)) {
0 ignored issues
show
introduced by
The condition is_null($projectionNodes) is always true.
Loading history...
473
            //This is the code path to handle properties of Complex type
474
            //or Entry without projection (i.e. no expansion or selection)
475
            $resourceProperties = array();
476
            if ($resourceTypeKind == ResourceTypeKind::ENTITY) {
0 ignored issues
show
introduced by
The condition $resourceTypeKind == POD...esourceTypeKind::ENTITY is always false.
Loading history...
477
                // If custom object is an entry then it can contain navigation
478
                // properties which are invisible (because the corresponding
479
                // resource set is invisible).
480
                // IDSMP::getResourceProperties will give collection of properties
481
                // which are visible.
482
                $currentResourceSetWrapper1 = $this->getCurrentResourceSetWrapper();
483
                $resourceProperties = $this->service
484
                    ->getProvidersWrapper()
485
                    ->getResourceProperties(
486
                        $currentResourceSetWrapper1,
487
                        $resourceType
488
                    );
489
            } else {
490
                $resourceProperties = $resourceType->getAllProperties();
491
            }
492
493
            //First write out primitve types
494
            foreach ($resourceProperties as $name => $resourceProperty) {
495
                if ($resourceProperty->getKind() == ResourcePropertyKind::PRIMITIVE
496
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY)
497
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG)
498
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)
499
                ) {
500
                    $odataProperty = new ODataProperty();
501
                    $primitiveValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
502
                    $this->_writePrimitiveValue($primitiveValue, $resourceProperty, $odataProperty);
503
                    $odataPropertyContent->properties[] = $odataProperty;
504
                }
505
            }
506
507
            //Write out bag and complex type
508
            $i = 0;
509
            foreach ($resourceProperties as $resourceProperty) {
510
                if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG)) {
511
                    //Handle Bag Property (Bag of Primitive or complex)
512
                    $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
513
                    $resourceType2 = $resourceProperty->getResourceType();
514
                    $this->_writeBagValue(
515
                        $propertyValue,
516
                        $resourceProperty->getName(),
517
                        $resourceType2,
518
                        $relativeUri . '/' . $resourceProperty->getName(),
519
                        $odataPropertyContent
520
                    );
521
                } else {
522
                    $resourcePropertyKind = $resourceProperty->getKind();
523
                    if ($resourcePropertyKind == ResourcePropertyKind::COMPLEX_TYPE) {
524
                        $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
525
                        $resourceType1 = $resourceProperty->getResourceType();
526
                        $this->_writeComplexValue(
527
                            $propertyValue,
528
                            $resourceProperty->getName(),
529
                            $resourceType1,
530
                            $relativeUri . '/' . $resourceProperty->getName(),
531
                            $odataPropertyContent
532
                        );
533
                    } else if ($resourceProperty->getKind() == ResourcePropertyKind::PRIMITIVE
534
                        || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY)
535
                        || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG)
536
                        || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)
537
                    ) {
538
                        continue;
539
                    } else {
540
                            $this->assert(
541
                                ($resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE)
542
                             || ($resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE)
543
                             || ($resourcePropertyKind == ResourcePropertyKind::KEY_RESOURCE_REFERENCE),
544
                                '($resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE)
545
                             || ($resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE)
546
                             || ($resourcePropertyKind == ResourcePropertyKind::KEY_RESOURCE_REFERENCE)'
547
                            );
548
549
                        $navigationProperties[$i] = new NavigationPropertyInfo($resourceProperty, $this->shouldExpandSegment($resourceProperty->getName()));
550
                        if ($navigationProperties[$i]->expanded) {
551
                            $navigationProperties[$i]->value = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
552
                        }
553
554
                        $i++;
555
                    }
556
                }
557
            }
558
559
        } else { //This is the code path to handle projected properties of Entry
560
            $i = 0;
561
            foreach ($projectionNodes as $projectionNode) {
562
                $propertyName = $projectionNode->getPropertyName();
563
                $resourceProperty = $resourceType->resolveProperty($propertyName);
564
                $this->assert(!is_null($resourceProperty), '!is_null($resourceProperty)');
565
566
                if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY) {
567
                    $currentResourceSetWrapper2 = $this->getCurrentResourceSetWrapper();
568
                    $resourceProperties = $this->service
569
                        ->getProvidersWrapper()
570
                        ->getResourceProperties(
571
                            $currentResourceSetWrapper2,
572
                            $resourceType
573
                        );
574
                    //Check for the visibility of this navigation property
575
                    if (array_key_exists($resourceProperty->getName(), $resourceProperties)) {
576
                        $navigationProperties[$i] = new NavigationPropertyInfo($resourceProperty, $this->shouldExpandSegment($propertyName));
577
                        if ($navigationProperties[$i]->expanded) {
578
                            $navigationProperties[$i]->value = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
579
                        }
580
581
                        $i++;
582
                        continue;
583
                    }
584
                }
585
586
                //Primitve, complex or bag property
587
                $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
588
                $propertyTypeKind = $resourceProperty->getKind();
589
                $propertyResourceType = $resourceProperty->getResourceType();
590
                $this->assert(!is_null($propertyResourceType), '!is_null($propertyResourceType)');
591
                if (ResourceProperty::sIsKindOf($propertyTypeKind, ResourcePropertyKind::BAG)) {
592
                    $bagResourceType = $resourceProperty->getResourceType();
593
                    $this->_writeBagValue(
594
                        $propertyValue,
595
                        $propertyName,
596
                        $bagResourceType,
597
                        $relativeUri . '/' . $propertyName,
598
                        $odataPropertyContent
599
                    );
600
                } else if (ResourceProperty::sIsKindOf($propertyTypeKind, ResourcePropertyKind::PRIMITIVE)) {
601
                    $odataProperty = new ODataProperty();
602
                    $this->_writePrimitiveValue($propertyValue, $resourceProperty, $odataProperty);
603
                    $odataPropertyContent->properties[] = $odataProperty;
604
                } else if ($propertyTypeKind == ResourcePropertyKind::COMPLEX_TYPE) {
605
                    $complexResourceType = $resourceProperty->getResourceType();
606
                    $this->_writeComplexValue(
607
                        $propertyValue,
608
                        $propertyName,
609
                        $complexResourceType,
610
                        $relativeUri . '/' . $propertyName,
611
                        $odataPropertyContent
612
                    );
613
                } else {
614
                    //unexpected
615
                    $this->assert(false, '$propertyTypeKind = Primitive or Bag or ComplexType');
616
                }
617
            }
618
        }
619
620
        if (!is_null($navigationProperties)) {
0 ignored issues
show
introduced by
The condition is_null($navigationProperties) is always true.
Loading history...
621
            //Write out navigation properties (deferred or inline)
622
            foreach ($navigationProperties as $navigationPropertyInfo) {
623
                $propertyName = $navigationPropertyInfo->resourceProperty->getName();
624
                $type = $navigationPropertyInfo->resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE ?
625
                    'application/atom+xml;type=entry' : 'application/atom+xml;type=feed';
626
                $link = new ODataLink();
627
                $link->name = ODataConstants::ODATA_RELATED_NAMESPACE . $propertyName;
628
                $link->title = $propertyName;
629
                $link->type = $type;
630
                $link->url = $relativeUri . '/' . $propertyName;
631
632
                if ($navigationPropertyInfo->expanded) {
633
                    $propertyRelativeUri = $relativeUri . '/' . $propertyName;
634
                    $propertyAbsoluteUri = trim($absoluteUri, '/') . '/' . $propertyName;
635
                    $needPop = $this->pushSegmentForNavigationProperty($navigationPropertyInfo->resourceProperty);
636
                    $navigationPropertyKind = $navigationPropertyInfo->resourceProperty->getKind();
637
                    $this->assert(
638
                        $navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE
639
                        || $navigationPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE
640
                        || $navigationPropertyKind == ResourcePropertyKind::KEY_RESOURCE_REFERENCE,
641
                        '$navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE
642
                        || $navigationPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE
643
                        || $navigationPropertyKind == ResourcePropertyKind::KEY_RESOURCE_REFERENCE'
644
                    );
645
                    $currentResourceSetWrapper = $this->getCurrentResourceSetWrapper();
646
                    $this->assert(!is_null($currentResourceSetWrapper), '!is_null($currentResourceSetWrapper)');
647
                    $link->isExpanded = true;
648
                    if (!is_null($navigationPropertyInfo->value)) {
649
                        if ($navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE) {
650
                            $inlineFeed = new ODataFeed();
651
                            $link->isCollection = true;
652
                            $currentResourceType = $currentResourceSetWrapper->getResourceType();
653
                            $this->_writeFeedElements(
654
                                $navigationPropertyInfo->value,
655
                                $currentResourceType,
656
                                $propertyName,
657
                                $propertyAbsoluteUri,
658
                                $propertyRelativeUri,
659
                                $inlineFeed
660
                            );
661
                            $link->expandedResult = $inlineFeed;
662
                        } else {
663
664
                            $link->isCollection = false;
665
                            $currentResourceType1 = $currentResourceSetWrapper->getResourceType();
666
667
                            $link->expandedResult = $this->_writeEntryElement(
668
                                $navigationPropertyInfo->value,
669
                                $currentResourceType1,
670
                                $propertyAbsoluteUri,
671
                                $propertyRelativeUri
672
                            );
673
                        }
674
                    } else {
675
                        $link->expandedResult = null;
676
                    }
677
678
                    $this->popSegment($needPop);
679
                }
680
681
                $odataEntry->links[] = $link;
682
            }
683
        }
684
    }
685
686
    /**
687
     * Writes a primitive value and related information to the given
688
     * ODataProperty instance.
689
     *
690
     * @param mixed            &$primitiveValue   The primitive value to write.
691
     * @param ResourceProperty &$resourceProperty The metadata of the primitive
692
     *                                            property value.
693
     * @param ODataProperty    &$odataProperty    ODataProperty instance to which
694
     *                                            the primitive value and related
695
     *                                            information to write out.
696
     *
697
     * @throws ODataException If given value is not primitive.
698
     *
699
     * @return void
700
     */
701
    private function _writePrimitiveValue(&$primitiveValue,
702
        ResourceProperty &$resourceProperty, ODataProperty &$odataProperty
703
    ) {
704
        if (is_object($primitiveValue)) {
705
            //TODO ERROR: The property 'PropertyName'
706
            //is defined as primitive type but value is an object
707
        }
708
709
710
        $odataProperty->name = $resourceProperty->getName();
711
        $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

711
        $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...
712
        if (is_null($primitiveValue)) {
713
            $odataProperty->value = null;
714
        } else {
715
            $resourceType = $resourceProperty->getResourceType();
716
            $this->_primitiveToString(
717
                $resourceType,
718
                $primitiveValue,
719
                $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

719
                /** @scrutinizer ignore-type */ $odataProperty->value
Loading history...
720
            );
721
        }
722
    }
723
724
    /**
725
     * Write value of a complex object.
726
     *
727
     * @param mixed                &$complexValue         Complex object to write.
728
     * @param string               $propertyName          Name of the
729
     *                                                    complex property
730
     *                                                    whose value need
731
     *                                                    to be written.
732
     * @param ResourceType         &$resourceType         Expected type
733
     *                                                    of the property.
734
     * @param string               $relativeUri           Relative uri for the
735
     *                                                    complex type element.
736
     * @param ODataPropertyContent &$odataPropertyContent Content to write to.
737
     *
738
     * @return void
739
     */
740
    private function _writeComplexValue(&$complexValue,
741
        $propertyName, ResourceType &$resourceType, $relativeUri,
742
        ODataPropertyContent &$odataPropertyContent
743
    ) {
744
        $odataProperty = new ODataProperty();
745
        $odataProperty->name = $propertyName;
746
        if (is_null($complexValue)) {
747
            $odataProperty->value = null;
748
            $odataProperty->typeName = $resourceType->getFullName();
749
        } else {
750
            $content = new ODataPropertyContent();
751
            $actualType = $this->_complexObjectToContent(
752
                $complexValue,
753
                $propertyName,
754
                $resourceType,
755
                $relativeUri,
756
                $content
757
            );
758
759
            $odataProperty->typeName = $actualType->getFullName();
760
            $odataProperty->value = $content;
761
        }
762
763
        $odataPropertyContent->properties[] = $odataProperty;
764
    }
765
766
    /**
767
     * Write value of a bag instance.
768
     *
769
     * @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...
770
     * @param string               $propertyName          Property name of the bag.
771
     * @param ResourceType         &$resourceType         Type describing the
772
     *                                                    bag value.
773
     * @param string               $relativeUri           Relative Url to the bag.
774
     * @param ODataPropertyContent &$odataPropertyContent On return, this object
775
     *                                                    will hold bag value which
776
     *                                                    can be used by writers.
777
     *
778
     * @return void
779
     */
780
    private function _writeBagValue(&$BagValue,
781
        $propertyName, ResourceType &$resourceType, $relativeUri,
782
        ODataPropertyContent &$odataPropertyContent
783
    ) {
784
        $bagItemResourceTypeKind = $resourceType->getResourceTypeKind();
785
        $this->assert(
786
            $bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE
787
            || $bagItemResourceTypeKind == ResourceTypeKind::COMPLEX,
788
            '$bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE
789
            || $bagItemResourceTypeKind == ResourceTypeKind::COMPLEX'
790
        );
791
792
        $odataProperty = new ODataProperty();
793
        $odataProperty->name = $propertyName;
794
        $odataProperty->typeName = 'Collection(' . $resourceType->getFullName() . ')';
795
        if (is_null($BagValue) || (is_array($BagValue) && empty ($BagValue))) {
796
            $odataProperty->value = null;
797
        } else {
798
            $odataBagContent = new ODataBagContent();
799
            foreach ($BagValue as $itemValue) {
800
                if (!is_null($itemValue)) {
801
                    if ($bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE) {
802
                        $primitiveValueAsString = null;
803
                        $this->_primitiveToString($resourceType, $itemValue, $primitiveValueAsString);
804
                        $odataBagContent->propertyContents[] = $primitiveValueAsString;
805
                    } else if ($bagItemResourceTypeKind == ResourceTypeKind::COMPLEX) {
806
                        $complexContent = new ODataPropertyContent();
807
                        $actualType = $this->_complexObjectToContent(
0 ignored issues
show
Unused Code introduced by
The assignment to $actualType is dead and can be removed.
Loading history...
808
                            $itemValue,
809
                            $propertyName,
810
                            $resourceType,
811
                            $relativeUri,
812
                            $complexContent
813
                        );
814
                        //TODO add type in case of base type
815
                        $odataBagContent->propertyContents[] = $complexContent;
816
                    }
817
                }
818
            }
819
820
            $odataProperty->value = $odataBagContent;
821
        }
822
823
        $odataPropertyContent->properties[] = $odataProperty;
824
    }
825
826
    /**
827
     * Write media resource metadata (for MLE and Named Streams)
828
     *
829
     * @param mixed        $entryObject  The entry instance being serialized.
830
     * @param ResourceType &$resourceType Resource type of the entry instance.
831
     * @param string       $title         Title for the current
832
     *                                    current entry instance.
833
     * @param string       $relativeUri   Relative uri for the
834
     *                                    current entry instance.
835
     * @param ODataEntry   &$odataEntry   OData entry to write to.
836
     *
837
     * @return void
838
     */
839
    private function _writeMediaResourceMetadata(
840
        $entryObject,
841
        ResourceType &$resourceType,
842
        $title,
843
        $relativeUri,
844
        ODataEntry &$odataEntry
845
    ) {
846
        if ($resourceType->isMediaLinkEntry()) {
847
            $odataEntry->isMediaLinkEntry = true;
848
            $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

848
            /** @scrutinizer ignore-call */ 
849
            $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...
849
            $eTag = $streamProvider->getStreamETag($entryObject, null);
850
            $readStreamUri = $streamProvider->getReadStreamUri($entryObject, null, $relativeUri);
851
            $mediaContentType = $streamProvider->getStreamContentType($entryObject, null);
852
            $mediaLink = new ODataMediaLink(
853
                $title,
854
                $streamProvider->getDefaultStreamEditMediaUri($relativeUri, null),
855
                $readStreamUri,
856
                $mediaContentType,
857
                $eTag
858
            );
859
860
            $odataEntry->mediaLink = $mediaLink;
861
        }
862
863
        if ($resourceType->hasNamedStream()) {
864
            foreach ($resourceType->getAllNamedStreams() as $title => $resourceStreamInfo) {
0 ignored issues
show
introduced by
$title is overwriting one of the parameters of this function.
Loading history...
865
                $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...
866
                $readStreamUri = $streamProvider->getReadStreamUri($entryObject, $resourceStreamInfo, $relativeUri);
867
                $mediaContentType = $streamProvider->getStreamContentType($entryObject, $resourceStreamInfo);
868
                $odataEntry->mediaLinks[] = new ODataMediaLink(
869
                    $title,
870
                    $streamProvider->getDefaultStreamEditMediaUri($relativeUri, $resourceStreamInfo),
871
                    $readStreamUri,
872
                    $mediaContentType,
873
                    $eTag
874
                );
875
            }
876
        }
877
    }
878
879
    /**
880
     * Convert the given primitive value to string.
881
     * Note: This method will not handle null primitive value.
882
     *
883
     * @param ResourceType &$primtiveResourceType Type of the primitive property
884
     *                                            whose value need to be converted.
885
     * @param mixed        $primitiveValue        Primitive value to convert.
886
     * @param string       &$stringValue          On return, this parameter will
887
     *                                            contain converted value.
888
     *
889
     * @return void
890
     */
891
    private function _primitiveToString(ResourceType &$primtiveResourceType,
892
        $primitiveValue, &$stringValue
893
    ) {
894
        $type = $primtiveResourceType->getInstanceType();
895
        if ($type instanceof Boolean) {
896
            $stringValue = ($primitiveValue === true) ? 'true' : 'false';
897
        } else if ($type instanceof Binary) {
898
            $stringValue = base64_encode($primitiveValue);
899
        } else if (($type instanceof DateTime || $type instanceof StringType) && ($primitiveValue instanceof \DateTime || $primitiveValue instanceof \DateTimeImmutable)) {
900
            $stringValue = $primitiveValue->format(\DateTime::ATOM);
901
        } else if ($type instanceof StringType && $primitiveValue instanceof \DateInterval) {
902
            $stringValue = (($primitiveValue->d * 86400) + ($primitiveValue->h * 3600) + ($primitiveValue->i * 60) + $primitiveValue->s) * 1000;
903
            // $stringValue = intval($primitiveValue->format('%s'))*1000; // Miliszekundumokkáé
904
        } else if ($type instanceof StringType) {
905
            $stringValue = mb_convert_encoding($primitiveValue, 'UTF-8');
906
        } else {
907
            $stringValue = strval($primitiveValue);
908
        }
909
    }
910
911
    /**
912
     * Write value of a complex object.
913
     * Note: This method will not handle null complex value.
914
     *
915
     * @param mixed                &$complexValue         Complex object to write.
916
     * @param string               $propertyName          Name of the
917
     *                                                    complex property
918
     *                                                    whose value
919
     *                                                    need to be written.
920
     * @param ResourceType         &$resourceType         Expected type of the
921
     *                                                    property.
922
     * @param string               $relativeUri           Relative uri for the
923
     *                                                    complex type element.
924
     * @param ODataPropertyContent &$odataPropertyContent Content to write to.
925
     *
926
     * @return ResourceType The actual type of the complex object.
927
     *
928
     * @return void
929
     */
930
    private function _complexObjectToContent(&$complexValue,
931
        $propertyName, ResourceType &$resourceType, $relativeUri,
932
        ODataPropertyContent &$odataPropertyContent
933
    ) {
934
        $count = count($this->complexTypeInstanceCollection);
935
        for ($i = 0; $i < $count; $i++) {
936
            if ($this->complexTypeInstanceCollection[$i] === $complexValue) {
937
                throw new InvalidOperationException(Messages::objectModelSerializerLoopsNotAllowedInComplexTypes($propertyName));
938
            }
939
        }
940
941
        $this->complexTypeInstanceCollection[$count] = &$complexValue;
942
943
        //TODO function to resolve actual type from $resourceType
944
        $actualType = $resourceType;
945
        $odataEntry = null;
946
        $this->_writeObjectProperties(
947
            $complexValue, $actualType,
948
            null, $relativeUri, $odataEntry, $odataPropertyContent
949
        );
950
        unset($this->complexTypeInstanceCollection[$count]);
951
        return $actualType;
952
    }
953
}
954