Completed
Push — master ( e301b1...a83709 )
by Alex
03:52
created

ObjectModelSerializer::_writeObjectProperties()   D

Complexity

Conditions 14
Paths 41

Size

Total Lines 113
Code Lines 84

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 113
rs 4.9516
c 0
b 0
f 0
cc 14
eloc 84
nc 41
nop 6

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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->getRequest()->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->getRequest()->getTargetResourceType();
52
        } else {
53
            assert($requestTargetSource == TargetSource::PROPERTY, '$requestTargetSource != TargetSource::PROPERTY');
54
            $resourceProperty = $this->getRequest()->getProjectedProperty();
55
            $resourceType = $resourceProperty->getResourceType();
56
        }
57
58
        $needPop = $this->pushSegmentForRoot();
59
        $entry = $this->_writeEntryElement(
60
            $entryObject,
61
            $resourceType,
62
            $this->getRequest()->getRequestUrl()->getUrlAsString(),
63
            $this->getRequest()->getContainerName()
64
        );
65
        $this->popSegment($needPop);
66
67
        return $entry;
68
    }
69
70
    /**
71
     * Write top level feed element.
72
     *
73
     * @param array &$entryObjects Array of entry resources to be written
74
     *
75
     * @return ODataFeed
76
     */
77
    public function writeTopLevelElements(&$entryObjects)
78
    {
79
        assert(is_array($entryObjects), '!is_array($entryObjects)');
80
        $requestTargetSource = $this->getRequest()->getTargetSource();
81
        $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...
82
        if ($requestTargetSource == TargetSource::ENTITY_SET) {
83
            $title = $this->getRequest()->getContainerName();
84 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...
85
            assert($requestTargetSource == TargetSource::PROPERTY, '$requestTargetSource != TargetSource::PROPERTY');
86
            $resourceProperty = $this->getRequest()->getProjectedProperty();
87
            assert(
88
                $resourceProperty->getKind() == ResourcePropertyKind::RESOURCESET_REFERENCE,
89
                '$resourceProperty->getKind() != ResourcePropertyKind::RESOURCESET_REFERENCE'
90
            );
91
            $title = $resourceProperty->getName();
92
        }
93
94
        $relativeUri = $this->getRequest()->getIdentifier();
95
        $feed = new ODataFeed();
96
97
        if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) {
98
            $feed->rowCount = $this->getRequest()->getCountValue();
99
        }
100
101
        $needPop = $this->pushSegmentForRoot();
102
        $targetResourceType = $this->getRequest()->getTargetResourceType();
103
        assert(null != $targetResourceType, "Target resource type must not be null");
104
        $this->_writeFeedElements(
105
            $entryObjects,
106
            $targetResourceType,
107
            $title,
108
            $this->getRequest()->getRequestUrl()->getUrlAsString(),
109
            $relativeUri,
110
            $feed
111
        );
112
        $this->popSegment($needPop);
113
114
        return $feed;
115
    }
116
117
    /**
118
     * Write top level url element.
119
     *
120
     * @param mixed $entryObject The entry resource whose url to be written
121
     *
122
     * @return ODataURL
123
     */
124
    public function writeUrlElement($entryObject)
125
    {
126
        $url = new ODataURL();
127
        if (!is_null($entryObject)) {
128
            $currentResourceType = $this->getCurrentResourceSetWrapper()->getResourceType();
129
            $relativeUri = $this->getEntryInstanceKey(
130
                $entryObject,
131
                $currentResourceType,
132
                $this->getCurrentResourceSetWrapper()->getName()
133
            );
134
135
            $url->url = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
136
        }
137
138
        return $url;
139
    }
140
141
    /**
142
     * Write top level url collection.
143
     *
144
     * @param array $entryObjects Array of entry resources
145
     *                            whose url to be written
146
     *
147
     * @return ODataURLCollection
148
     */
149
    public function writeUrlElements($entryObjects)
150
    {
151
        $urls = new ODataURLCollection();
152
        if (!empty($entryObjects)) {
153
            $i = 0;
154
            foreach ($entryObjects as $entryObject) {
155
                $urls->urls[$i] = $this->writeUrlElement($entryObject);
156
                ++$i;
157
            }
158
159
            if ($i > 0 && $this->needNextPageLink(count($entryObjects))) {
160
                $urls->nextPageLink = $this->getNextLinkUri(
161
                    $entryObjects[$i - 1],
162
                    $this->getRequest()->getRequestUrl()->getUrlAsString()
163
                );
164
            }
165
        }
166
167
        if ($this->getRequest()->queryType == QueryType::ENTITIES_WITH_COUNT()) {
168
            $urls->count = $this->getRequest()->getCountValue();
169
        }
170
171
        return $urls;
172
    }
173
174
    /**
175
     * Write top level complex resource.
176
     *
177
     * @param mixed        &$complexValue The complex object to be
178
     *                                    written
179
     * @param string       $propertyName  The name of the
180
     *                                    complex property
181
     * @param ResourceType &$resourceType Describes the type of
182
     *                                    complex object
183
     *
184
     * @return ODataPropertyContent
185
     */
186
    public function writeTopLevelComplexObject(
187
        &$complexValue,
188
        $propertyName,
189
        ResourceType &$resourceType
190
    ) {
191
        $propertyContent = new ODataPropertyContent();
192
        $this->_writeComplexValue(
193
            $complexValue,
194
            $propertyName,
195
            $resourceType,
196
            null,
197
            $propertyContent
198
        );
199
200
        return $propertyContent;
201
    }
202
203
    /**
204
     * Write top level bag resource.
205
     *
206
     * @param mixed        &$BagValue     The bag object to be
207
     *                                    written
208
     * @param string       $propertyName  The name of the
209
     *                                    bag property
210
     * @param ResourceType &$resourceType Describes the type of
211
     *                                    bag object
212
     *
213
     * @return ODataPropertyContent
214
     */
215
    public function writeTopLevelBagObject(
216
        &$BagValue,
217
        $propertyName,
218
        ResourceType & $resourceType
219
    ) {
220
        $propertyContent = new ODataPropertyContent();
221
        $this->_writeBagValue(
222
            $BagValue,
223
            $propertyName,
224
            $resourceType,
225
            null,
226
            $propertyContent
227
        );
228
229
        return $propertyContent;
230
    }
231
232
    /**
233
     * Write top level primitive value.
234
     *
235
     * @param mixed            &$primitiveValue   The primitve value to be
236
     *                                            written
237
     * @param ResourceProperty &$resourceProperty Resource property
238
     *                                            describing the
239
     *                                            primitive property
240
     *                                            to be written
241
     *
242
     * @return ODataPropertyContent
243
     */
244
    public function writeTopLevelPrimitive(
245
        &$primitiveValue,
246
        ResourceProperty & $resourceProperty
247
    ) {
248
        $propertyContent = new ODataPropertyContent();
249
        $propertyContent->properties[] = new ODataProperty();
250
        $this->_writePrimitiveValue(
251
            $primitiveValue,
252
            $resourceProperty,
253
            $propertyContent->properties[0]
254
        );
255
256
        return $propertyContent;
257
    }
258
259
    /**
260
     * Write an entry element.
261
     *
262
     * @param mixed        $entryObject  Object representing entry element
263
     * @param ResourceType $resourceType Expected type of the entry object
264
     * @param string       $absoluteUri  Absolute uri of the entry element
265
     * @param string       $relativeUri  Relative uri of the entry element
266
     *
267
     * @return ODataEntry
268
     */
269
    private function _writeEntryElement(
270
        $entryObject,
271
        ResourceType $resourceType,
272
        $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...
273
        $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...
274
    ) {
275
        $entry = new ODataEntry();
276
        $entry->resourceSetName = $this->getCurrentResourceSetWrapper()->getName();
277
278
        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...
279
            //According to atom standard an empty entry must have an Author
280
            //node.
281
        } else {
282
            $relativeUri = $this->getEntryInstanceKey(
283
                $entryObject,
284
                $resourceType,
285
                $this->getCurrentResourceSetWrapper()->getName()
286
            );
287
288
            $absoluteUri = rtrim($this->absoluteServiceUri, '/') . '/' . $relativeUri;
289
            $title = $resourceType->getName();
290
            //TODO Resolve actual resource type
291
            $actualResourceType = $resourceType;
292
            $this->_writeMediaResourceMetadata(
293
                $entryObject,
294
                $actualResourceType,
295
                $title,
296
                $relativeUri,
297
                $entry
298
            );
299
300
            $entry->id = $absoluteUri;
301
            $entry->eTag = $this->getETagForEntry($entryObject, $resourceType);
302
            $entry->title = $title;
303
            $entry->editLink = $relativeUri;
304
            $entry->type = $actualResourceType->getFullName();
305
            $odataPropertyContent = new ODataPropertyContent();
306
            $this->_writeObjectProperties(
307
                $entryObject,
308
                $actualResourceType,
309
                $absoluteUri,
310
                $relativeUri,
311
                $entry,
312
                $odataPropertyContent
313
            );
314
            $entry->propertyContent = $odataPropertyContent;
315
        }
316
317
        return $entry;
318
    }
319
320
    /**
321
     * Writes the feed elements.
322
     *
323
     * @param array        &$entryObjects Array of entries in the feed element
324
     * @param ResourceType &$resourceType The resource type of the f the elements
325
     *                                    in the collection
326
     * @param string       $title         Title of the feed element
327
     * @param string       $absoluteUri   Absolute uri representing the feed element
328
     * @param string       $relativeUri   Relative uri representing the feed element
329
     * @param ODataFeed    &$feed         Feed to write to
330
     */
331
    private function _writeFeedElements(
332
        &$entryObjects,
333
        ResourceType &$resourceType,
334
        $title,
335
        $absoluteUri,
336
        $relativeUri,
337
        ODataFeed & $feed
338
    ) {
339
        assert(is_array($entryObjects), '!_writeFeedElements::is_array($entryObjects)');
340
        $feed->id = $absoluteUri;
341
        $feed->title = $title;
342
        $feed->selfLink = new ODataLink();
343
        $feed->selfLink->name = ODataConstants::ATOM_SELF_RELATION_ATTRIBUTE_VALUE;
344
        $feed->selfLink->title = $title;
345
        $feed->selfLink->url = $relativeUri;
346
347
        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...
348
            //TODO // ATOM specification: if a feed contains no entries,
349
            //then the feed should have at least one Author tag
350
        } else {
351
            foreach ($entryObjects as $entryObject) {
352
                $feed->entries[] = $this->_writeEntryElement($entryObject, $resourceType, null, null);
353
            }
354
355
            if ($this->needNextPageLink(count($entryObjects))) {
356
                $lastObject = end($entryObjects);
357
                $feed->nextPageLink = $this->getNextLinkUri($lastObject, $absoluteUri);
358
            }
359
        }
360
    }
361
362
    /**
363
     * Write values of properties of given entry (resource) or complex object.
364
     *
365
     * @param mixed        $customObject  Entity or complex object
366
     *                                    with properties
367
     *                                    to write out
368
     * @param ResourceType &$resourceType Resource type describing
369
     *                                    the metadata of
370
     *                                    the custom object
371
     * @param string       $absoluteUri   Absolute uri for the given
372
     *                                    entry object
373
     *                                    NULL for complex object
374
     * @param string       $relativeUri   Relative uri for the given
375
     *                                    custom object
376
     * @param ODataEntry           ODataEntry|null           ODataEntry instance to
377
     *                                                    place links and
378
     *                                                    expansion of the
379
     *                                                    entry object,
380
     *                                                    NULL for complex object
381
     * @param ODataPropertyContent &$odataPropertyContent ODataPropertyContent
382
     *                                                    instance in which
383
     *                                                    to place the values
384
     */
385
    private function _writeObjectProperties(
386
        $customObject,
387
        ResourceType & $resourceType,
388
        $absoluteUri,
389
        $relativeUri,
390
        &$odataEntry,
391
        ODataPropertyContent & $odataPropertyContent
392
    ) {
393
        $resourceTypeKind = $resourceType->getResourceTypeKind();
394
        if (is_null($absoluteUri) == ($resourceTypeKind == ResourceTypeKind::ENTITY)
395
        ) {
396
            throw ODataException::createInternalServerError(
397
                Messages::badProviderInconsistentEntityOrComplexTypeUsage(
398
                    $resourceType->getName()
399
                )
400
            );
401
        }
402
403
        assert(
404
            (($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry))
405
            || (($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry)),
406
            '!(($resourceTypeKind == ResourceTypeKind::ENTITY) && ($odataEntry instanceof ODataEntry))'
407
            .' && !(($resourceTypeKind == ResourceTypeKind::COMPLEX) && is_null($odataEntry))'
408
        );
409
        $projectionNodes = null;
410
        $navigationProperties = null;
411
        if ($resourceTypeKind == ResourceTypeKind::ENTITY) {
412
            $projectionNodes = $this->getProjectionNodes();
413
            $navigationProperties = array();
414
        }
415
416
        if (is_null($projectionNodes)) {
417
            list($odataPropertyContent, $navigationProperties) = $this->writeObjectPropertiesUnexpanded(
418
                $customObject,
419
                $resourceType,
420
                $relativeUri,
421
                $odataPropertyContent,
422
                $resourceTypeKind,
423
                $navigationProperties
424
            );
425
426
        } else { //This is the code path to handle projected properties of Entry
427
            list($navigationProperties, $odataPropertyContent) = $this->writeObjectPropertiesExpanded(
428
                $customObject,
429
                $resourceType,
430
                $relativeUri,
431
                $odataPropertyContent,
432
                $projectionNodes,
433
                $navigationProperties
434
            );
435
        }
436
437
        if (!is_null($navigationProperties)) {
438
            //Write out navigation properties (deferred or inline)
439
            foreach ($navigationProperties as $navigationPropertyInfo) {
440
                $propertyName = $navigationPropertyInfo->resourceProperty->getName();
441
                $type = $navigationPropertyInfo->resourceProperty->getKind() == ResourcePropertyKind::RESOURCE_REFERENCE ?
442
                    'application/atom+xml;type=entry' : 'application/atom+xml;type=feed';
443
                $link = new ODataLink();
444
                $link->name = ODataConstants::ODATA_RELATED_NAMESPACE . $propertyName;
445
                $link->title = $propertyName;
446
                $link->type = $type;
447
                $link->url = $relativeUri . '/' . $propertyName;
448
449
                if ($navigationPropertyInfo->expanded) {
450
                    $propertyRelativeUri = $relativeUri . '/' . $propertyName;
451
                    $propertyAbsoluteUri = trim($absoluteUri, '/') . '/' . $propertyName;
452
                    $needPop = $this->pushSegmentForNavigationProperty($navigationPropertyInfo->resourceProperty);
453
                    $navigationPropertyKind = $navigationPropertyInfo->resourceProperty->getKind();
454
                    assert(
455
                        $navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE
456
                        || $navigationPropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE,
457
                        '$navigationPropertyKind != ResourcePropertyKind::RESOURCESET_REFERENCE 
458
                        && $navigationPropertyKind != ResourcePropertyKind::RESOURCE_REFERENCE'
459
                    );
460
                    $currentResourceSetWrapper = $this->getCurrentResourceSetWrapper();
461
                    assert(!is_null($currentResourceSetWrapper), 'is_null($currentResourceSetWrapper)');
462
                    $link->isExpanded = true;
463
                    if (!is_null($navigationPropertyInfo->value)) {
464
                        $currentResourceType = $currentResourceSetWrapper->getResourceType();
465
                        if ($navigationPropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE) {
466
                            $inlineFeed = new ODataFeed();
467
                            $link->isCollection = true;
468
469
                            $this->_writeFeedElements(
470
                                $navigationPropertyInfo->value,
471
                                $currentResourceType,
472
                                $propertyName,
473
                                $propertyAbsoluteUri,
474
                                $propertyRelativeUri,
475
                                $inlineFeed
476
                            );
477
                            $link->expandedResult = $inlineFeed;
478
                        } else {
479
                            $link->isCollection = false;
480
                            $link->expandedResult = $this->_writeEntryElement(
481
                                $navigationPropertyInfo->value,
482
                                $currentResourceType,
483
                                $propertyAbsoluteUri,
484
                                $propertyRelativeUri
485
                            );
486
                        }
487
                    } else {
488
                        $link->expandedResult = null;
489
                    }
490
491
                    $this->popSegment($needPop);
492
                }
493
494
                $odataEntry->links[] = $link;
495
            }
496
        }
497
    }
498
499
    /**
500
     * Writes a primitive value and related information to the given
501
     * ODataProperty instance.
502
     *
503
     * @param mixed            &$primitiveValue   The primitive value to write
504
     * @param ResourceProperty &$resourceProperty The metadata of the primitive
505
     *                                            property value
506
     * @param ODataProperty    &$odataProperty    ODataProperty instance to which
507
     *                                            the primitive value and related
508
     *                                            information to write out
509
     *
510
     * @throws ODataException If given value is not primitive
511
     */
512
    private function _writePrimitiveValue(
513
        &$primitiveValue,
514
        ResourceProperty & $resourceProperty,
515
        ODataProperty & $odataProperty
516
    ) {
517
        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...
518
            //TODO ERROR: The property 'PropertyName'
519
            //is defined as primitive type but value is an object
520
        }
521
522
        $odataProperty->name = $resourceProperty->getName();
523
        $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...
524
        if (is_null($primitiveValue)) {
525
            $odataProperty->value = null;
526
        } else {
527
            $resourceType = $resourceProperty->getResourceType();
528
            $odataProperty->value = $this->_primitiveToString($resourceType, $primitiveValue);
529
        }
530
    }
531
532
    /**
533
     * Write value of a complex object.
534
     *
535
     * @param mixed                &$complexValue         Complex object to write
536
     * @param string               $propertyName          Name of the
537
     *                                                    complex property
538
     *                                                    whose value need
539
     *                                                    to be written
540
     * @param ResourceType         &$resourceType         Expected type
541
     *                                                    of the property
542
     * @param string               $relativeUri           Relative uri for the
543
     *                                                    complex type element
544
     * @param ODataPropertyContent &$odataPropertyContent Content to write to
545
     */
546
    private function _writeComplexValue(
547
        &$complexValue,
548
        $propertyName,
549
        ResourceType & $resourceType,
550
        $relativeUri,
551
        ODataPropertyContent & $odataPropertyContent
552
    ) {
553
        $odataProperty = new ODataProperty();
554
        $odataProperty->name = $propertyName;
555
        if (is_null($complexValue)) {
556
            $odataProperty->value = null;
557
            $odataProperty->typeName = $resourceType->getFullName();
558
        } else {
559
            $content = new ODataPropertyContent();
560
            $actualType = $this->_complexObjectToContent(
561
                $complexValue,
562
                $propertyName,
563
                $resourceType,
564
                $relativeUri,
565
                $content
566
            );
567
568
            $odataProperty->typeName = $actualType->getFullName();
569
            $odataProperty->value = $content;
570
        }
571
572
        $odataPropertyContent->properties[] = $odataProperty;
573
    }
574
575
    /**
576
     * Write value of a bag instance.
577
     *
578
     * @param array/NULL           &$BagValue             Bag value to write
579
     * @param string               $propertyName          Property name of the bag
580
     * @param ResourceType         &$resourceType         Type describing the
581
     *                                                    bag value
582
     * @param string               $relativeUri           Relative Url to the bag
583
     * @param ODataPropertyContent &$odataPropertyContent On return, this object
584
     *                                                    will hold bag value which
585
     *                                                    can be used by writers
586
     */
587
    private function _writeBagValue(
588
        &$BagValue,
589
        $propertyName,
590
        ResourceType & $resourceType,
591
        $relativeUri,
592
        ODataPropertyContent & $odataPropertyContent
593
    ) {
594
        assert(null == $BagValue || is_array($BagValue), "Bag parameter must be null or array");
595
        $bagItemResourceTypeKind = $resourceType->getResourceTypeKind();
596
        assert(
597
            $bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE
598
            || $bagItemResourceTypeKind == ResourceTypeKind::COMPLEX,
599
            '$bagItemResourceTypeKind != ResourceTypeKind::PRIMITIVE'
600
            .' && $bagItemResourceTypeKind != ResourceTypeKind::COMPLEX'
601
        );
602
603
        $odataProperty = new ODataProperty();
604
        $odataProperty->name = $propertyName;
605
        $odataProperty->typeName = 'Collection(' . $resourceType->getFullName() . ')';
606
607
        if (is_null($BagValue) || (is_array($BagValue) && empty($BagValue))) {
608
            $odataProperty->value = null;
609
        } else {
610
            $odataBagContent = new ODataBagContent();
611
            // strip out null elements
612
            $BagValue = array_diff($BagValue, [null]);
613
            foreach ($BagValue as $itemValue) {
614
                if ($bagItemResourceTypeKind == ResourceTypeKind::PRIMITIVE) {
615
                    $odataBagContent->propertyContents[] = $this->_primitiveToString($resourceType, $itemValue);
616
                } elseif ($bagItemResourceTypeKind == ResourceTypeKind::COMPLEX) {
617
                    $complexContent = new ODataPropertyContent();
618
                    $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...
619
                        $itemValue,
620
                        $propertyName,
621
                        $resourceType,
622
                        $relativeUri,
623
                        $complexContent
624
                    );
625
                    //TODO add type in case of base type
626
                    $odataBagContent->propertyContents[] = $complexContent;
627
                }
628
            }
629
630
            $odataProperty->value = $odataBagContent;
631
        }
632
633
        $odataPropertyContent->properties[] = $odataProperty;
634
    }
635
636
    /**
637
     * Write media resource metadata (for MLE and Named Streams).
638
     *
639
     * @param mixed        $entryObject   The entry instance being serialized
640
     * @param ResourceType &$resourceType Resource type of the entry instance
641
     * @param string       $title         Title for the current
642
     *                                    current entry instance
643
     * @param string       $relativeUri   Relative uri for the
644
     *                                    current entry instance
645
     * @param ODataEntry   &$odataEntry   OData entry to write to
646
     */
647
    private function _writeMediaResourceMetadata(
648
        $entryObject,
649
        ResourceType & $resourceType,
650
        $title,
651
        $relativeUri,
652
        ODataEntry & $odataEntry
653
    ) {
654
        if ($resourceType->isMediaLinkEntry()) {
655
            $odataEntry->isMediaLinkEntry = true;
656
            $streamProvider = $this->getService()->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...
657
            $eTag = $streamProvider->getStreamETag($entryObject, null);
658
            $readStreamUri = $streamProvider->getReadStreamUri($entryObject, null, $relativeUri);
659
            $mediaContentType = $streamProvider->getStreamContentType($entryObject, null);
660
            $mediaLink = new ODataMediaLink(
661
                $title,
662
                $streamProvider->getDefaultStreamEditMediaUri($relativeUri, null),
663
                $readStreamUri,
664
                $mediaContentType,
665
                $eTag
666
            );
667
668
            $odataEntry->mediaLink = $mediaLink;
669
        }
670
671
        if ($resourceType->hasNamedStream()) {
672
            foreach ($resourceType->getAllNamedStreams() as $title => $resourceStreamInfo) {
673
                $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...
674
                $readStreamUri = $streamProvider->getReadStreamUri($entryObject, $resourceStreamInfo, $relativeUri);
675
                $mediaContentType = $streamProvider->getStreamContentType($entryObject, $resourceStreamInfo);
676
                $mediaLink = new ODataMediaLink(
677
                    $title,
678
                    $streamProvider->getDefaultStreamEditMediaUri($relativeUri, $resourceStreamInfo),
679
                    $readStreamUri,
680
                    $mediaContentType,
681
                    $eTag
682
                );
683
684
                $odataEntry->mediaLinks[] = $mediaLink;
685
            }
686
        }
687
    }
688
689
    /**
690
     * Convert the given primitive value to string.
691
     * Note: This method will not handle null primitive value.
692
     *
693
     * @param ResourceType &$primitiveResourceType Type of the primitive property
694
     *                                            whose value need to be converted
695
     * @param mixed $primitiveValue Primitive value to convert
696
     * @return string
697
698
     */
699
    private function _primitiveToString(
700
        ResourceType &$primitiveResourceType,
701
        $primitiveValue
702
    ) {
703
        $type = $primitiveResourceType->getInstanceType();
704
        if ($type instanceof Boolean) {
705
            $stringValue = ($primitiveValue === true) ? 'true' : 'false';
706
        } elseif ($type instanceof Binary) {
707
            $stringValue = base64_encode($primitiveValue);
708
        } elseif ($type instanceof DateTime && $primitiveValue instanceof \DateTime) {
709
            $stringValue = $primitiveValue->format(\DateTime::ATOM);
710
        } elseif ($type instanceof StringType) {
711
            $stringValue = utf8_encode($primitiveValue);
712
        } else {
713
            $stringValue = strval($primitiveValue);
714
        }
715
        return $stringValue;
716
    }
717
718
    /**
719
     * Write value of a complex object.
720
     * Note: This method will not handle null complex value.
721
     *
722
     * @param mixed                &$complexValue         Complex object to write
723
     * @param string               $propertyName          Name of the
724
     *                                                    complex property
725
     *                                                    whose value
726
     *                                                    need to be written
727
     * @param ResourceType         &$resourceType         Expected type of the
728
     *                                                    property
729
     * @param string               $relativeUri           Relative uri for the
730
     *                                                    complex type element
731
     * @param ODataPropertyContent &$odataPropertyContent Content to write to
732
     *
733
     * @return ResourceType The actual type of the complex object
734
     */
735
    private function _complexObjectToContent(
736
        &$complexValue,
737
        $propertyName,
738
        ResourceType & $resourceType,
739
        $relativeUri,
740
        ODataPropertyContent & $odataPropertyContent
741
    ) {
742
        $count = count($this->complexTypeInstanceCollection);
743
        for ($i = 0; $i < $count; ++$i) {
744
            if ($this->complexTypeInstanceCollection[$i] === $complexValue) {
745
                throw new InvalidOperationException(
746
                    Messages::objectModelSerializerLoopsNotAllowedInComplexTypes($propertyName)
747
                );
748
            }
749
        }
750
751
        $this->complexTypeInstanceCollection[$count] = &$complexValue;
752
753
        //TODO function to resolve actual type from $resourceType
754
        $actualType = $resourceType;
755
        $odataEntry = null;
756
        $this->_writeObjectProperties(
757
            $complexValue,
758
            $actualType,
759
            null,
760
            $relativeUri,
761
            $odataEntry,
762
            $odataPropertyContent
763
        );
764
        unset($this->complexTypeInstanceCollection[$count]);
765
766
        return $actualType;
767
    }
768
769
    /**
770
     * @param $customObject
771
     * @param ResourceType $resourceType
772
     * @param $relativeUri
773
     * @param ODataPropertyContent $odataPropertyContent
774
     * @param $resourceTypeKind
775
     * @param $navigationProperties
776
     * @return array
777
     * @throws ODataException
778
     */
779
    private function writeObjectPropertiesUnexpanded(
780
        $customObject,
781
        ResourceType &$resourceType,
782
        $relativeUri,
783
        ODataPropertyContent &$odataPropertyContent,
784
        $resourceTypeKind,
785
        $navigationProperties
786
    ) {
787
        //This is the code path to handle properties of Complex type
788
        //or Entry without projection (i.e. no expansion or selection)
789
        $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...
790
        if ($resourceTypeKind == ResourceTypeKind::ENTITY) {
791
            // If custom object is an entry then it can contain navigation
792
            // properties which are invisible (because the corresponding
793
            // resource set is invisible).
794
            // IDSMP::getResourceProperties will give collection of properties
795
            // which are visible.
796
            $currentResourceSetWrapper1 = $this->getCurrentResourceSetWrapper();
797
            $resourceProperties = $this->getService()
798
                ->getProvidersWrapper()
799
                ->getResourceProperties(
800
                    $currentResourceSetWrapper1,
801
                    $resourceType
802
                );
803
        } else {
804
            $resourceProperties = $resourceType->getAllProperties();
805
        }
806
807
        //First write out primitive types
808
        foreach ($resourceProperties as $name => $resourceProperty) {
809
            $resourceKind = $resourceProperty->getKind();
810
            if ($resourceKind == ResourcePropertyKind::PRIMITIVE
811
                || $resourceKind == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY)
812
                || $resourceKind == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG)
813
                || $resourceKind == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)
814
            ) {
815
                $odataProperty = new ODataProperty();
816
                $primitiveValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
817
                $this->_writePrimitiveValue($primitiveValue, $resourceProperty, $odataProperty);
818
                $odataPropertyContent->properties[] = $odataProperty;
819
            }
820
        }
821
822
        //Write out bag and complex type
823
        $i = 0;
824
        foreach ($resourceProperties as $resourceProperty) {
825
            if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG)) {
826
                //Handle Bag Property (Bag of Primitive or complex)
827
                $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
828
                $resourceType2 = $resourceProperty->getResourceType();
829
                $this->_writeBagValue(
830
                    $propertyValue,
831
                    $resourceProperty->getName(),
832
                    $resourceType2,
833
                    $relativeUri . '/' . $resourceProperty->getName(),
834
                    $odataPropertyContent
835
                );
836
            } else {
837
                $resourceKind = $resourceProperty->getKind();
838
                if ($resourceKind == ResourcePropertyKind::COMPLEX_TYPE) {
839
                    $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
840
                    $resourceType1 = $resourceProperty->getResourceType();
841
                    $this->_writeComplexValue(
842
                        $propertyValue,
843
                        $resourceProperty->getName(),
844
                        $resourceType1,
845
                        $relativeUri . '/' . $resourceProperty->getName(),
846
                        $odataPropertyContent
847
                    );
848
                } elseif ($resourceKind == ResourcePropertyKind::PRIMITIVE
849
                          || $resourceKind == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY)
850
                          || $resourceKind == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::ETAG)
851
                          || $resourceKind == (ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY | ResourcePropertyKind::ETAG)
852
                ) {
853
                    continue;
854
                } else {
855
                    assert(
856
                        ($resourceKind == ResourcePropertyKind::RESOURCE_REFERENCE)
857
                        || ($resourceKind == ResourcePropertyKind::RESOURCESET_REFERENCE),
858
                        '($resourceKind != ResourcePropertyKind::RESOURCE_REFERENCE)'
859
                        . '&& ($resourceKind != ResourcePropertyKind::RESOURCESET_REFERENCE)'
860
                    );
861
862
                    $navigationProperties[$i] = new NavigationPropertyInfo(
863
                        $resourceProperty, $this->shouldExpandSegment($resourceProperty->getName())
864
                    );
865
                    if ($navigationProperties[$i]->expanded) {
866
                        $navigationProperties[$i]->value = $this->getPropertyValue(
867
                            $customObject, $resourceType, $resourceProperty
868
                        );
869
                    }
870
871
                    ++$i;
872
                }
873
            }
874
        }
875
        return array($odataPropertyContent, $navigationProperties);
876
    }
877
878
    /**
879
     * @param $customObject
880
     * @param ResourceType $resourceType
881
     * @param $relativeUri
882
     * @param ODataPropertyContent $odataPropertyContent
883
     * @param $projectionNodes
884
     * @param $navigationProperties
885
     * @return array
886
     * @throws ODataException
887
     */
888
    private function writeObjectPropertiesExpanded(
889
        $customObject,
890
        ResourceType &$resourceType,
891
        $relativeUri,
892
        ODataPropertyContent &$odataPropertyContent,
893
        $projectionNodes,
894
        $navigationProperties
895
    ) {
896
        $i = 0;
897
        foreach ($projectionNodes as $projectionNode) {
898
            $propertyName = $projectionNode->getPropertyName();
899
            $resourceProperty = $resourceType->resolveProperty($propertyName);
900
            assert(!is_null($resourceProperty), 'is_null($resourceProperty)');
901
902
            if ($resourceProperty->getTypeKind() == ResourceTypeKind::ENTITY) {
903
                $currentResourceSetWrapper2 = $this->getCurrentResourceSetWrapper();
904
                $resourceProperties = $this->getService()
905
                    ->getProvidersWrapper()
906
                    ->getResourceProperties(
907
                        $currentResourceSetWrapper2,
908
                        $resourceType
909
                    );
910
                //Check for the visibility of this navigation property
911
                if (array_key_exists($resourceProperty->getName(), $resourceProperties)) {
912
                    $navigationProperties[$i] = new NavigationPropertyInfo(
913
                        $resourceProperty,
914
                        $this->shouldExpandSegment($propertyName)
915
                    );
916
                    if ($navigationProperties[$i]->expanded) {
917
                        $navigationProperties[$i]->value = $this->getPropertyValue(
918
                            $customObject,
919
                            $resourceType,
920
                            $resourceProperty
921
                        );
922
                    }
923
924
                    ++$i;
925
                    continue;
926
                }
927
            }
928
929
            //Primitive, complex or bag property
930
            $propertyValue = $this->getPropertyValue($customObject, $resourceType, $resourceProperty);
931
            $propertyTypeKind = $resourceProperty->getKind();
932
            $propertyResourceType = $resourceProperty->getResourceType();
933
            assert(!is_null($propertyResourceType), 'is_null($propertyResourceType)');
934
            if ($resourceProperty->isKindOf(ResourcePropertyKind::BAG)) {
935
                $bagResourceType = $resourceProperty->getResourceType();
936
                $this->_writeBagValue(
937
                    $propertyValue,
938
                    $propertyName,
939
                    $bagResourceType,
940
                    $relativeUri . '/' . $propertyName,
941
                    $odataPropertyContent
942
                );
943
            } elseif ($resourceProperty->isKindOf(ResourcePropertyKind::PRIMITIVE)) {
944
                $odataProperty = new ODataProperty();
945
                $this->_writePrimitiveValue($propertyValue, $resourceProperty, $odataProperty);
946
                $odataPropertyContent->properties[] = $odataProperty;
947
            } elseif ($propertyTypeKind == ResourcePropertyKind::COMPLEX_TYPE) {
948
                $complexResourceType = $resourceProperty->getResourceType();
949
                $this->_writeComplexValue(
950
                    $propertyValue,
951
                    $propertyName,
952
                    $complexResourceType,
953
                    $relativeUri . '/' . $propertyName,
954
                    $odataPropertyContent
955
                );
956
            } else {
957
                //unexpected
958
                assert(false, '$propertyTypeKind != Primitive or Bag or ComplexType');
959
            }
960
        }
961
        return array($navigationProperties, $odataPropertyContent);
962
    }
963
}
964