Completed
Pull Request — master (#7)
by Christopher
10:13 queued 04:13
created

ObjectModelSerializer::writeTopLevelPrimitive()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 10
nc 1
nop 2
1
<?php
2
3
namespace POData\ObjectModel;
4
5
use POData\Common\ODataConstants;
6
use POData\Common\InvalidOperationException;
7
use POData\Providers\Query\QueryType;
8
use POData\UriProcessor\ResourcePathProcessor\SegmentParser\TargetSource;
9
use POData\UriProcessor\RequestDescription;
10
use POData\IService;
11
use POData\Providers\Metadata\ResourceType;
12
use POData\Providers\Metadata\ResourceTypeKind;
13
use POData\Providers\Metadata\ResourcePropertyKind;
14
use POData\Providers\Metadata\ResourceProperty;
15
use POData\Providers\Metadata\Type\Binary;
16
use POData\Providers\Metadata\Type\Boolean;
17
use POData\Providers\Metadata\Type\StringType;
18
use POData\Providers\Metadata\Type\DateTime;
19
use POData\Common\ODataException;
20
use POData\Common\Messages;
21
22
/**
23
 * Class ObjectModelSerializer.
24
 */
25
class ObjectModelSerializer extends ObjectModelSerializerBase
26
{
27
    /**
28
     * Creates new instance of ObjectModelSerializer.
29
     *
30
     * @param IService           $service
31
     * @param RequestDescription $request the  request submitted by the client
32
     */
33
    public function __construct(IService $service, RequestDescription $request)
34
    {
35
        parent::__construct($service, $request);
36
    }
37
38
    /**
39
     * Write a top level entry resource.
40
     *
41
     * @param mixed $entryObject Reference to the entry object to be written
42
     *
43
     * @return ODataEntry
44
     */
45
    public function writeTopLevelElement($entryObject)
46
    {
47
        $requestTargetSource = $this->request->getTargetSource();
48
49
        $resourceType = null;
0 ignored issues
show
Unused Code introduced by
$resourceType is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
50 View Code Duplication
        if ($requestTargetSource == TargetSource::ENTITY_SET) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
51
            $resourceType = $this->request->getTargetResourceType();
52
        } else {
53
            $this->assert(
54
                $requestTargetSource == TargetSource::PROPERTY,
55
                '$requestTargetSource == TargetSource::PROPERTY'
56
            );
57
            $resourceProperty = $this->request->getProjectedProperty();
58
            //$this->assert($resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE, '$resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE');
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
59
            $resourceType = $resourceProperty->getResourceType();
60
        }
61
62
        $needPop = $this->pushSegmentForRoot();
63
        $entry = $this->_writeEntryElement(
64
            $entryObject,
65
            $resourceType,
66
            $this->request->getRequestUrl()->getUrlAsString(),
67
            $this->request->getContainerName()
68
        );
69
        $this->popSegment($needPop);
70
71
        return $entry;
72
    }
73
74
    /**
75
     * Write top level feed element.
76
     *
77
     * @param array &$entryObjects Array of entry resources to be written
78
     *
79
     * @return ODataFeed
80
     */
81
    public function writeTopLevelElements(&$entryObjects)
82
    {
83
        $this->assert(is_array($entryObjects), 'is_array($entryObjects)');
84
        $requestTargetSource = $this->request->getTargetSource();
85
        $title = null;
0 ignored issues
show
Unused Code introduced by
$title is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
86
        if ($requestTargetSource == TargetSource::ENTITY_SET) {
87
            $title = $this->request->getContainerName();
88 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
89
            $this->assert(
90
                $requestTargetSource == TargetSource::PROPERTY,
91
                '$requestTargetSource == TargetSource::PROPERTY'
92
            );
93
            $resourceProperty = $this->request->getProjectedProperty();
94
            $this->assert(
95
                $resourceProperty->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE,
96
                '$resourceProperty->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE'
97
            );
98
            $title = $resourceProperty->getName();
99
        }
100
101
        $relativeUri = $this->request->getIdentifier();
102
        $feed = new ODataFeed();
103
104
        if ($this->request->queryType == QueryType::ENTITIES_WITH_COUNT()) {
105
            $feed->rowCount = $this->request->getCountValue();
106
        }
107
108
        $needPop = $this->pushSegmentForRoot();
109
        $targetResourceType = $this->request->getTargetResourceType();
110
        $this->_writeFeedElements(
111
            $entryObjects,
112
            $targetResourceType,
0 ignored issues
show
Bug introduced by
It seems like $targetResourceType defined by $this->request->getTargetResourceType() on line 109 can be null; however, POData\ObjectModel\Objec...r::_writeFeedElements() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
113
            $title,
114
            $this->request->getRequestUrl()->getUrlAsString(),
115
            $relativeUri,
116
            $feed
117
        );
118
        $this->popSegment($needPop);
119
120
        return $feed;
121
    }
122
123
    /**
124
     * Write top level url element.
125
     *
126
     * @param mixed $entryObject The entry resource whose url to be written
127
     *
128
     * @return ODataURL
129
     */
130
    public function writeUrlElement($entryObject)
131
    {
132
        $url = new ODataURL();
133
        if (!is_null($entryObject)) {
134
            $currentResourceType = $this->getCurrentResourceSetWrapper()->getResourceType();
135
            $relativeUri = $this->getEntryInstanceKey(
136
                $entryObject,
137
                $currentResourceType,
138
                $this->getCurrentResourceSetWrapper()->getName()
139
            );
140
141
            $url->url = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
142
        }
143
144
        return $url;
145
    }
146
147
    /**
148
     * Write top level url collection.
149
     *
150
     * @param array $entryObjects Array of entry resources
151
     *                            whose url to be written
152
     *
153
     * @return ODataURLCollection
154
     */
155
    public function writeUrlElements($entryObjects)
156
    {
157
        $urls = new ODataURLCollection();
158
        if (!empty($entryObjects)) {
159
            $i = 0;
160
            foreach ($entryObjects as $entryObject) {
161
                $urls->urls[$i] = $this->writeUrlElement($entryObject);
162
                ++$i;
163
            }
164
165
            if ($i > 0 && $this->needNextPageLink(count($entryObjects))) {
166
                $urls->nextPageLink = $this->getNextLinkUri($entryObjects[$i - 1], $this->request->getRequestUrl()->getUrlAsString());
167
            }
168
        }
169
170
        if ($this->request->queryType == QueryType::ENTITIES_WITH_COUNT()) {
171
            $urls->count = $this->request->getCountValue();
172
        }
173
174
        return $urls;
175
    }
176
177
    /**
178
     * Write top level complex resource.
179
     *
180
     * @param mixed        &$complexValue The complex object to be
181
     *                                    written
182
     * @param string       $propertyName  The name of the
183
     *                                    complex property
184
     * @param ResourceType &$resourceType Describes the type of
185
     *                                    complex object
186
     *
187
     * @return ODataPropertyContent
188
     */
189
    public function writeTopLevelComplexObject(
190
        &$complexValue,
191
        $propertyName,
192
        ResourceType & $resourceType
193
    ) {
194
        $propertyContent = new ODataPropertyContent();
195
        $this->_writeComplexValue(
196
            $complexValue,
197
            $propertyName,
198
            $resourceType,
199
            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
        $propertyContent = new ODataPropertyContent();
224
        $this->_writeBagValue(
225
            $BagValue,
226
            $propertyName,
227
            $resourceType,
228
            null,
229
            $propertyContent
230
        );
231
232
        return $propertyContent;
233
    }
234
235
    /**
236
     * Write top level primitive value.
237
     *
238
     * @param mixed            &$primitiveValue   The primitve value to be
239
     *                                            written
240
     * @param ResourceProperty &$resourceProperty Resource property
241
     *                                            describing the
242
     *                                            primitive property
243
     *                                            to be written
244
     *
245
     * @return ODataPropertyContent
246
     */
247
    public function writeTopLevelPrimitive(
248
        &$primitiveValue,
249
        ResourceProperty & $resourceProperty
250
    ) {
251
        $propertyContent = new ODataPropertyContent();
252
        $propertyContent->properties[] = new ODataProperty();
253
        $this->_writePrimitiveValue(
254
            $primitiveValue,
255
            $resourceProperty,
256
            $propertyContent->properties[0]
257
        );
258
259
        return $propertyContent;
260
    }
261
262
    /**
263
     * Write an entry element.
264
     *
265
     * @param mixed        $entryObject  Object representing entry element
266
     * @param ResourceType $resourceType Expected type of the entry object
267
     * @param string       $absoluteUri  Absolute uri of the entry element
268
     * @param string       $relativeUri  Relative uri of the entry element
269
     *
270
     * @return ODataEntry
271
     */
272
    private function _writeEntryElement(
273
        $entryObject,
274
        ResourceType $resourceType,
275
        $absoluteUri,
0 ignored issues
show
Unused Code introduced by
The parameter $absoluteUri is not used and could be removed.

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

Loading history...
276
        $relativeUri
0 ignored issues
show
Unused Code introduced by
The parameter $relativeUri is not used and could be removed.

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

Loading history...
277
    ) {
278
        $entry = new ODataEntry();
279
        $entry->resourceSetName = $this->getCurrentResourceSetWrapper()->getName();
280
281
        if (is_null($entryObject)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
282
            //According to atom standard an empty entry must have an Author
283
            //node.
284
        } else {
285
            $relativeUri = $this->getEntryInstanceKey(
286
                $entryObject,
287
                $resourceType,
288
                $this->getCurrentResourceSetWrapper()->getName()
289
            );
290
291
            $absoluteUri = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
292
            $title = $resourceType->getName();
293
            //TODO Resolve actual resource type
294
            $actualResourceType = $resourceType;
295
            $this->_writeMediaResourceMetadata(
296
                $entryObject,
297
                $actualResourceType,
298
                $title,
299
                $relativeUri,
300
                $entry
301
            );
302
303
            $entry->id = $absoluteUri;
304
            $entry->eTag = $this->getETagForEntry($entryObject, $resourceType);
305
            $entry->title = $title;
306
            $entry->editLink = $relativeUri;
307
            $entry->type = $actualResourceType->getFullName();
308
            $odataPropertyContent = new ODataPropertyContent();
309
            $this->_writeObjectProperties(
310
                $entryObject,
311
                $actualResourceType,
312
                $absoluteUri,
313
                $relativeUri,
314
                $entry,
315
                $odataPropertyContent
316
            );
317
            $entry->propertyContent = $odataPropertyContent;
318
        }
319
320
        return $entry;
321
    }
322
323
    /**
324
     * Writes the feed elements.
325
     *
326
     * @param array        &$entryObjects Array of entries in the feed element
327
     * @param ResourceType &$resourceType The resource type of the f the elements
328
     *                                    in the collection
329
     * @param string       $title         Title of the feed element
330
     * @param string       $absoluteUri   Absolute uri representing the feed element
331
     * @param string       $relativeUri   Relative uri representing the feed element
332
     * @param ODataFeed    &$feed         Feed to write to
333
     */
334
    private function _writeFeedElements(
335
        &$entryObjects,
336
        ResourceType & $resourceType,
337
        $title,
338
        $absoluteUri,
339
        $relativeUri,
340
        ODataFeed & $feed
341
    ) {
342
        $this->assert(is_array($entryObjects), '_writeFeedElements::is_array($entryObjects)');
343
        $feed->id = $absoluteUri;
344
        $feed->title = $title;
345
        $feed->selfLink = new ODataLink();
346
        $feed->selfLink->name = ODataConstants::ATOM_SELF_RELATION_ATTRIBUTE_VALUE;
347
        $feed->selfLink->title = $title;
348
        $feed->selfLink->url = $relativeUri;
349
350
        if (empty($entryObjects)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
351
            //TODO // ATOM specification: if a feed contains no entries,
352
            //then the feed should have at least one Author tag
353
        } else {
354
            foreach ($entryObjects as $entryObject) {
355
                $feed->entries[] = $this->_writeEntryElement($entryObject, $resourceType, null, null);
356
            }
357
358
            if ($this->needNextPageLink(count($entryObjects))) {
359
                $lastObject = end($entryObjects);
360
                $feed->nextPageLink = $this->getNextLinkUri($lastObject, $absoluteUri);
361
            }
362
        }
363
    }
364
365
    /**
366
     * Write values of properties of given entry (resource) or complex object.
367
     *
368
     * @param mixed        $customObject  Entity or complex object
369
     *                                    with properties
370
     *                                    to write out
371
     * @param ResourceType &$resourceType Resource type describing
372
     *                                    the metadata of
373
     *                                    the custom object
374
     * @param string       $absoluteUri   Absolute uri for the given
375
     *                                    entry object
376
     *                                    NULL for complex object
377
     * @param string       $relativeUri   Relative uri for the given
378
     *                                    custom object
379
     * @param ODataEntry           ODataEntry|null           ODataEntry instance to
380
     *                                                    place links and
381
     *                                                    expansion of the
382
     *                                                    entry object,
383
     *                                                    NULL for complex object
384
     * @param ODataPropertyContent &$odataPropertyContent ODataPropertyContent
385
     *                                                    instance in which
386
     *                                                    to place the values
387
     */
388
    private function _writeObjectProperties(
389
        $customObject,
390
        ResourceType & $resourceType,
391
        $absoluteUri,
392
        $relativeUri,
393
        &$odataEntry,
394
        ODataPropertyContent & $odataPropertyContent
395
    ) {
396
        $resourceTypeKind = $resourceType->getResourceTypeKind();
397
        if (is_null($absoluteUri) == ($resourceTypeKind == ResourceTypeKind::ENTITY)
398
        ) {
399
            throw ODataException::createInternalServerError(
400
                Messages::badProviderInconsistentEntityOrComplexTypeUsage(
401
                    $resourceType->getName()
402
                )
403
            );
404
        }
405
406
        $this->assert(
407
            (($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry))
408
            || (($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry)),
409
            '(($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry)) 
410
            || (($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry))'
411
        );
412
        $projectionNodes = null;
413
        $navigationProperties = null;
414
        if ($resourceTypeKind == ResourceTypeKind::ENTITY) {
415
            $projectionNodes = $this->getProjectionNodes();
416
            $navigationProperties = array();
417
        }
418
419
        if (is_null($projectionNodes)) {
420
            //This is the code path to handle properties of Complex type
421
            //or Entry without projection (i.e. no expansion or selection)
422
            $resourceProperties = array();
0 ignored issues
show
Unused Code introduced by
$resourceProperties is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
423
            if ($resourceTypeKind == ResourceTypeKind::ENTITY) {
424
                // If custom object is an entry then it can contain navigation
425
                // properties which are invisible (because the corresponding
426
                // resource set is invisible).
427
                // IDSMP::getResourceProperties will give collection of properties
428
                // which are visible.
429
                $currentResourceSetWrapper1 = $this->getCurrentResourceSetWrapper();
430
                $resourceProperties = $this->service
431
                    ->getProvidersWrapper()
432
                    ->getResourceProperties(
433
                        $currentResourceSetWrapper1,
0 ignored issues
show
Bug introduced by
It seems like $currentResourceSetWrapper1 defined by $this->getCurrentResourceSetWrapper() on line 429 can be null; however, POData\Providers\Provide...getResourceProperties() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
434
                        $resourceType
435
                    );
436
            } else {
437
                $resourceProperties = $resourceType->getAllProperties();
438
            }
439
440
            //First write out primitve types
441
            foreach ($resourceProperties as $name => $resourceProperty) {
442
                if ($resourceProperty->getKind() == ResourcePropertyKind::PRIMITIVE
443
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY)
444
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG)
445
                    || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)
446
                ) {
447
                    $odataProperty = new ODataProperty();
448
                    $primitiveValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
449
                    $this->_writePrimitiveValue($primitiveValue, $resourceProperty, $odataProperty);
450
                    $odataPropertyContent->properties[] = $odataProperty;
451
                }
452
            }
453
454
            //Write out bag and complex type
455
            $i = 0;
456
            foreach ($resourceProperties as $resourceProperty) {
457
                if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG)) {
458
                    //Handle Bag Property (Bag of Primitive or complex)
459
                    $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
460
                    $resourceType2 = $resourceProperty->getResourceType();
461
                    $this->_writeBagValue(
462
                        $propertyValue,
463
                        $resourceProperty->getName(),
464
                        $resourceType2,
465
                        $relativeUri . '/' . $resourceProperty->getName(),
466
                        $odataPropertyContent
467
                    );
468
                } else {
469
                    $resourcePropertyKind = $resourceProperty->getKind();
470
                    if ($resourcePropertyKind == ResourcePropertyKind::COMPLEX_TYPE) {
471
                        $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
472
                        $resourceType1 = $resourceProperty->getResourceType();
473
                        $this->_writeComplexValue(
474
                            $propertyValue,
475
                            $resourceProperty->getName(),
476
                            $resourceType1,
477
                            $relativeUri . '/' . $resourceProperty->getName(),
478
                            $odataPropertyContent
479
                        );
480
                    } elseif ($resourceProperty->getKind() == ResourcePropertyKind::PRIMITIVE
481
                        || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY)
482
                        || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG)
483
                        || $resourceProperty->getKind() == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)
484
                    ) {
485
                        continue;
486
                    } else {
487
                        $this->assert(
488
                            ($resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE)
489
                             || ($resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE),
490
                            '($resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE)
491
                             || ($resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE)'
492
                        );
493
494
                        $navigationProperties[$i] = new NavigationPropertyInfo($resourceProperty, $this->shouldExpandSegment($resourceProperty->getName()));
495
                        if ($navigationProperties[$i]->expanded) {
496
                            $navigationProperties[$i]->value = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
497
                        }
498
499
                        ++$i;
500
                    }
501
                }
502
            }
503
        } else { //This is the code path to handle projected properties of Entry
504
            $i = 0;
505
            foreach ($projectionNodes as $projectionNode) {
506
                $propertyName = $projectionNode->getPropertyName();
507
                $resourceProperty = $resourceType->resolveProperty($propertyName);
508
                $this->assert(!is_null($resourceProperty), '!is_null($resourceProperty)');
509
510
                if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY) {
511
                    $currentResourceSetWrapper2 = $this->getCurrentResourceSetWrapper();
512
                    $resourceProperties = $this->service
513
                        ->getProvidersWrapper()
514
                        ->getResourceProperties(
515
                            $currentResourceSetWrapper2,
0 ignored issues
show
Bug introduced by
It seems like $currentResourceSetWrapper2 defined by $this->getCurrentResourceSetWrapper() on line 511 can be null; however, POData\Providers\Provide...getResourceProperties() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
516
                            $resourceType
517
                        );
518
                    //Check for the visibility of this navigation property
519
                    if (array_key_exists($resourceProperty->getName(), $resourceProperties)) {
520
                        $navigationProperties[$i] = new NavigationPropertyInfo($resourceProperty, $this->shouldExpandSegment($propertyName));
0 ignored issues
show
Bug introduced by
It seems like $resourceProperty defined by $resourceType->resolveProperty($propertyName) on line 507 can be null; however, POData\ObjectModel\Navig...ertyInfo::__construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
521
                        if ($navigationProperties[$i]->expanded) {
522
                            $navigationProperties[$i]->value = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
0 ignored issues
show
Bug introduced by
It seems like $resourceProperty defined by $resourceType->resolveProperty($propertyName) on line 507 can be null; however, POData\ObjectModel\Objec...ase::getPropertyValue() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
523
                        }
524
525
                        ++$i;
526
                        continue;
527
                    }
528
                }
529
530
                //Primitve, complex or bag property
531
                $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
0 ignored issues
show
Bug introduced by
It seems like $resourceProperty defined by $resourceType->resolveProperty($propertyName) on line 507 can be null; however, POData\ObjectModel\Objec...ase::getPropertyValue() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
532
                $propertyTypeKind = $resourceProperty->getKind();
533
                $propertyResourceType = $resourceProperty->getResourceType();
534
                $this->assert(!is_null($propertyResourceType), '!is_null($propertyResourceType)');
535
                if (ResourceProperty::sIsKindOf($propertyTypeKind, ResourcePropertyKind::BAG)) {
536
                    $bagResourceType = $resourceProperty->getResourceType();
537
                    $this->_writeBagValue(
538
                        $propertyValue,
539
                        $propertyName,
540
                        $bagResourceType,
541
                        $relativeUri . '/' . $propertyName,
542
                        $odataPropertyContent
543
                    );
544
                } elseif (ResourceProperty::sIsKindOf($propertyTypeKind, ResourcePropertyKind::PRIMITIVE)) {
545
                    $odataProperty = new ODataProperty();
546
                    $this->_writePrimitiveValue($propertyValue, $resourceProperty, $odataProperty);
0 ignored issues
show
Bug introduced by
It seems like $resourceProperty defined by $resourceType->resolveProperty($propertyName) on line 507 can be null; however, POData\ObjectModel\Objec...:_writePrimitiveValue() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
547
                    $odataPropertyContent->properties[] = $odataProperty;
548
                } elseif ($propertyTypeKind == ResourcePropertyKind::COMPLEX_TYPE) {
549
                    $complexResourceType = $resourceProperty->getResourceType();
550
                    $this->_writeComplexValue(
551
                        $propertyValue,
552
                        $propertyName,
553
                        $complexResourceType,
554
                        $relativeUri . '/' . $propertyName,
555
                        $odataPropertyContent
556
                    );
557
                } else {
558
                    //unexpected
559
                    $this->assert(false, '$propertyTypeKind = Primitive or Bag or ComplexType');
560
                }
561
            }
562
        }
563
564
        if (!is_null($navigationProperties)) {
565
            //Write out navigation properties (deferred or inline)
566
            foreach ($navigationProperties as $navigationPropertyInfo) {
567
                $propertyName = $navigationPropertyInfo->resourceProperty->getName();
568
                $type = $navigationPropertyInfo->resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE ?
569
                    'application/atom+xml;type=entry' : 'application/atom+xml;type=feed';
570
                $link = new ODataLink();
571
                $link->name = ODataConstants::ODATA_RELATED_NAMESPACE . $propertyName;
572
                $link->title = $propertyName;
573
                $link->type = $type;
574
                $link->url = $relativeUri . '/' . $propertyName;
575
576
                if ($navigationPropertyInfo->expanded) {
577
                    $propertyRelativeUri = $relativeUri . '/' . $propertyName;
578
                    $propertyAbsoluteUri = trim($absoluteUri, '/') . '/' . $propertyName;
579
                    $needPop = $this->pushSegmentForNavigationProperty($navigationPropertyInfo->resourceProperty);
580
                    $navigationPropertyKind = $navigationPropertyInfo->resourceProperty->getKind();
581
                    $this->assert(
582
                        $navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE
583
                        || $navigationPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE,
584
                        '$navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE 
585
                        || $navigationPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE'
586
                    );
587
                    $currentResourceSetWrapper = $this->getCurrentResourceSetWrapper();
588
                    $this->assert(!is_null($currentResourceSetWrapper), '!is_null($currentResourceSetWrapper)');
589
                    $link->isExpanded = true;
590
                    if (!is_null($navigationPropertyInfo->value)) {
591
                        if ($navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE) {
592
                            $inlineFeed = new ODataFeed();
593
                            $link->isCollection = true;
594
                            $currentResourceType = $currentResourceSetWrapper->getResourceType();
595
                            $this->_writeFeedElements(
596
                                $navigationPropertyInfo->value,
597
                                $currentResourceType,
598
                                $propertyName,
599
                                $propertyAbsoluteUri,
600
                                $propertyRelativeUri,
601
                                $inlineFeed
602
                            );
603
                            $link->expandedResult = $inlineFeed;
604
                        } else {
605
                            $link->isCollection = false;
606
                            $currentResourceType1 = $currentResourceSetWrapper->getResourceType();
607
608
                            $link->expandedResult = $this->_writeEntryElement(
609
                                $navigationPropertyInfo->value,
610
                                $currentResourceType1,
611
                                $propertyAbsoluteUri,
612
                                $propertyRelativeUri
613
                            );
614
                        }
615
                    } else {
616
                        $link->expandedResult = null;
617
                    }
618
619
                    $this->popSegment($needPop);
620
                }
621
622
                $odataEntry->links[] = $link;
623
            }
624
        }
625
    }
626
627
    /**
628
     * Writes a primitive value and related information to the given
629
     * ODataProperty instance.
630
     *
631
     * @param mixed            &$primitiveValue   The primitive value to write
632
     * @param ResourceProperty &$resourceProperty The metadata of the primitive
633
     *                                            property value
634
     * @param ODataProperty    &$odataProperty    ODataProperty instance to which
635
     *                                            the primitive value and related
636
     *                                            information to write out
637
     *
638
     * @throws ODataException If given value is not primitive
639
     */
640
    private function _writePrimitiveValue(
641
        &$primitiveValue,
642
        ResourceProperty & $resourceProperty,
643
        ODataProperty & $odataProperty
644
    ) {
645
        if (is_object($primitiveValue)) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
646
            //TODO ERROR: The property 'PropertyName'
647
            //is defined as primitive type but value is an object
648
        }
649
650
        $odataProperty->name = $resourceProperty->getName();
651
        $odataProperty->typeName = $resourceProperty->getInstanceType()->getFullTypeName();
0 ignored issues
show
Bug introduced by
The method getFullTypeName does only exist in POData\Providers\Metadata\Type\IType, but not in ReflectionClass.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
652
        if (is_null($primitiveValue)) {
653
            $odataProperty->value = null;
654
        } else {
655
            $resourceType = $resourceProperty->getResourceType();
656
            $this->_primitiveToString(
657
                $resourceType,
658
                $primitiveValue,
659
                $odataProperty->value
660
            );
661
        }
662
    }
663
664
    /**
665
     * Write value of a complex object.
666
     *
667
     * @param mixed                &$complexValue         Complex object to write
668
     * @param string               $propertyName          Name of the
669
     *                                                    complex property
670
     *                                                    whose value need
671
     *                                                    to be written
672
     * @param ResourceType         &$resourceType         Expected type
673
     *                                                    of the property
674
     * @param string               $relativeUri           Relative uri for the
675
     *                                                    complex type element
676
     * @param ODataPropertyContent &$odataPropertyContent Content to write to
677
     */
678
    private function _writeComplexValue(
679
        &$complexValue,
680
        $propertyName,
681
        ResourceType & $resourceType,
682
        $relativeUri,
683
        ODataPropertyContent & $odataPropertyContent
684
    ) {
685
        $odataProperty = new ODataProperty();
686
        $odataProperty->name = $propertyName;
687
        if (is_null($complexValue)) {
688
            $odataProperty->value = null;
689
            $odataProperty->typeName = $resourceType->getFullName();
690
        } else {
691
            $content = new ODataPropertyContent();
692
            $actualType = $this->_complexObjectToContent(
693
                $complexValue,
694
                $propertyName,
695
                $resourceType,
696
                $relativeUri,
697
                $content
698
            );
699
700
            $odataProperty->typeName = $actualType->getFullName();
701
            $odataProperty->value = $content;
702
        }
703
704
        $odataPropertyContent->properties[] = $odataProperty;
705
    }
706
707
    /**
708
     * Write value of a bag instance.
709
     *
710
     * @param array/NULL           &$BagValue             Bag value to write
711
     * @param string               $propertyName          Property name of the bag
712
     * @param ResourceType         &$resourceType         Type describing the
713
     *                                                    bag value
714
     * @param string               $relativeUri           Relative Url to the bag
715
     * @param ODataPropertyContent &$odataPropertyContent On return, this object
716
     *                                                    will hold bag value which
717
     *                                                    can be used by writers
718
     */
719
    private function _writeBagValue(
720
        &$BagValue,
721
        $propertyName,
722
        ResourceType & $resourceType,
723
        $relativeUri,
724
        ODataPropertyContent & $odataPropertyContent
725
    ) {
726
        $bagItemResourceTypeKind = $resourceType->getResourceTypeKind();
727
        $this->assert(
728
            $bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE
729
            || $bagItemResourceTypeKind == ResourceTypeKind::COMPLEX,
730
            '$bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE
731
            || $bagItemResourceTypeKind == ResourceTypeKind::COMPLEX'
732
        );
733
734
        $odataProperty = new ODataProperty();
735
        $odataProperty->name = $propertyName;
736
        $odataProperty->typeName = 'Collection(' . $resourceType->getFullName() . ')';
737
        if (is_null($BagValue) || (is_array($BagValue) && empty($BagValue))) {
738
            $odataProperty->value = null;
739
        } else {
740
            $odataBagContent = new ODataBagContent();
741
            foreach ($BagValue as $itemValue) {
742
                if (!is_null($itemValue)) {
743
                    if ($bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE) {
744
                        $primitiveValueAsString = null;
745
                        $this->_primitiveToString($resourceType, $itemValue, $primitiveValueAsString);
746
                        $odataBagContent->propertyContents[] = $primitiveValueAsString;
747
                    } elseif ($bagItemResourceTypeKind == ResourceTypeKind::COMPLEX) {
748
                        $complexContent = new ODataPropertyContent();
749
                        $actualType = $this->_complexObjectToContent(
0 ignored issues
show
Unused Code introduced by
$actualType is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
750
                            $itemValue,
751
                            $propertyName,
752
                            $resourceType,
753
                            $relativeUri,
754
                            $complexContent
755
                        );
756
                        //TODO add type in case of base type
757
                        $odataBagContent->propertyContents[] = $complexContent;
758
                    }
759
                }
760
            }
761
762
            $odataProperty->value = $odataBagContent;
763
        }
764
765
        $odataPropertyContent->properties[] = $odataProperty;
766
    }
767
768
    /**
769
     * Write media resource metadata (for MLE and Named Streams).
770
     *
771
     * @param mixed        $entryObject   The entry instance being serialized
772
     * @param ResourceType &$resourceType Resource type of the entry instance
773
     * @param string       $title         Title for the current
774
     *                                    current entry instance
775
     * @param string       $relativeUri   Relative uri for the
776
     *                                    current entry instance
777
     * @param ODataEntry   &$odataEntry   OData entry to write to
778
     */
779
    private function _writeMediaResourceMetadata(
780
        $entryObject,
781
        ResourceType & $resourceType,
782
        $title,
783
        $relativeUri,
784
        ODataEntry & $odataEntry
785
    ) {
786
        if ($resourceType->isMediaLinkEntry()) {
787
            $odataEntry->isMediaLinkEntry = true;
788
            $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()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
789
            $eTag = $streamProvider->getStreamETag($entryObject, null);
790
            $readStreamUri = $streamProvider->getReadStreamUri($entryObject, null, $relativeUri);
791
            $mediaContentType = $streamProvider->getStreamContentType($entryObject, null);
792
            $mediaLink = new ODataMediaLink(
793
                $title,
794
                $streamProvider->getDefaultStreamEditMediaUri($relativeUri, null),
795
                $readStreamUri,
796
                $mediaContentType,
797
                $eTag
798
            );
799
800
            $odataEntry->mediaLink = $mediaLink;
801
        }
802
803
        if ($resourceType->hasNamedStream()) {
804
            foreach ($resourceType->getAllNamedStreams() as $title => $resourceStreamInfo) {
805
                $eTag = $streamProvider->getStreamETag($entryObject, $resourceStreamInfo);
0 ignored issues
show
Bug introduced by
The variable $streamProvider does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
806
                $readStreamUri = $streamProvider->getReadStreamUri($entryObject, $resourceStreamInfo, $relativeUri);
807
                $mediaContentType = $streamProvider->getStreamContentType($entryObject, $resourceStreamInfo);
808
                $odataEntry->mediaLinks[] = new ODataMediaLink(
809
                    $title,
810
                    $streamProvider->getDefaultStreamEditMediaUri($relativeUri, $resourceStreamInfo),
811
                    $readStreamUri,
812
                    $mediaContentType,
813
                    $eTag
814
                );
815
            }
816
        }
817
    }
818
    /**
819
     * Convert the given primitive value to string.
820
     * Note: This method will not handle null primitive value.
821
     *
822
     * @param ResourceType &$primtiveResourceType Type of the primitive property
823
     *                                            whose value need to be converted
824
     * @param mixed        $primitiveValue        Primitive value to convert
825
     * @param string       &$stringValue          On return, this parameter will
826
     *                                            contain converted value
827
     */
828
    private function _primitiveToString(
829
        ResourceType & $primtiveResourceType,
830
        $primitiveValue,
831
        &$stringValue
832
    ) {
833
        $type = $primtiveResourceType->getInstanceType();
834
        if ($type instanceof Boolean) {
835
            $stringValue = ($primitiveValue === true) ? 'true' : 'false';
836
        } elseif ($type instanceof Binary) {
837
            $stringValue = base64_encode($primitiveValue);
838
        } elseif ($type instanceof DateTime && $primitiveValue instanceof \DateTime) {
839
            $stringValue = $primitiveValue->format(\DateTime::ATOM);
840
        } elseif ($type instanceof StringType) {
841
            $stringValue = utf8_encode($primitiveValue);
842
        } else {
843
            $stringValue = strval($primitiveValue);
844
        }
845
    }
846
847
    /**
848
     * Write value of a complex object.
849
     * Note: This method will not handle null complex value.
850
     *
851
     * @param mixed                &$complexValue         Complex object to write
852
     * @param string               $propertyName          Name of the
853
     *                                                    complex property
854
     *                                                    whose value
855
     *                                                    need to be written
856
     * @param ResourceType         &$resourceType         Expected type of the
857
     *                                                    property
858
     * @param string               $relativeUri           Relative uri for the
859
     *                                                    complex type element
860
     * @param ODataPropertyContent &$odataPropertyContent Content to write to
861
     *
862
     * @return ResourceType The actual type of the complex object
863
     */
864
    private function _complexObjectToContent(
865
        &$complexValue,
866
        $propertyName,
867
        ResourceType & $resourceType,
868
        $relativeUri,
869
        ODataPropertyContent & $odataPropertyContent
870
    ) {
871
        $count = count($this->complexTypeInstanceCollection);
872
        for ($i = 0; $i < $count; ++$i) {
873
            if ($this->complexTypeInstanceCollection[$i] === $complexValue) {
874
                throw new InvalidOperationException(Messages::objectModelSerializerLoopsNotAllowedInComplexTypes($propertyName));
875
            }
876
        }
877
878
        $this->complexTypeInstanceCollection[$count] = &$complexValue;
879
880
        //TODO function to resolve actual type from $resourceType
881
        $actualType = $resourceType;
882
        $odataEntry = null;
883
        $this->_writeObjectProperties(
884
            $complexValue,
885
            $actualType,
886
            null,
887
            $relativeUri,
888
            $odataEntry,
889
            $odataPropertyContent
890
        );
891
        unset($this->complexTypeInstanceCollection[$count]);
892
893
        return $actualType;
894
    }
895
}
896