Passed
Push — master ( 7c438c...2c13cb )
by Alex
01:26
created

_addReferencePropertyInternal()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 37
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 37
rs 8.5806
cc 4
eloc 22
nc 3
nop 4
1
<?php
2
3
namespace POData\Providers\Metadata;
4
5
use POData\Common\InvalidOperationException;
6
use POData\Providers\Metadata\Type\IType;
7
use POData\Providers\Metadata\Type\TypeCode;
8
9
/**
10
 * Class SimpleMetadataProvider.
11
 */
12
class SimpleMetadataProvider implements IMetadataProvider
13
{
14
    protected $resourceSets = [];
15
    protected $resourceTypes = [];
16
    protected $associationSets = [];
17
    protected $containerName;
18
    protected $namespaceName;
19
    public $mappedDetails = null;
20
21
    //Begin Implementation of IMetadataProvider
22
23
    /**
24
     * get the Container name for the data source.
25
     *
26
     * @return string container name
27
     */
28
    public function getContainerName()
29
    {
30
        return $this->containerName;
31
    }
32
33
    /**
34
     * get Namespace name for the data source.
35
     *
36
     * @return string namespace
37
     */
38
    public function getContainerNamespace()
39
    {
40
        return $this->namespaceName;
41
    }
42
43
    /**
44
     * get all entity set information.
45
     *
46
     * @return ResourceSet[]
47
     */
48
    public function getResourceSets($params = null)
49
    {
50
        $parameters = [];
51
        if (is_string($params)) {
52
            $parameters[] = $params;
53
        } elseif (isset($params) && !is_array($params)) {
54
            throw new \ErrorException('Input parameter must be absent, null, string or array');
55
        } else {
56
            $parameters = $params;
57
        }
58
        if (!is_array($parameters) || 0 == count($parameters)) {
59
            return array_values($this->resourceSets);
60
        }
61
        assert(is_array($parameters));
62
        $return = [];
63
        $counter = 0;
64
        foreach ($this->resourceSets as $resource) {
65
            $resName = $resource->getName();
66
            if (in_array($resName, $parameters)) {
67
                $return[] = $resource;
68
                $counter++;
69
            }
70
        }
71
        assert($counter == count($return));
72
73
        return $return;
74
    }
75
76
    /**
77
     * get all resource types in the data source.
78
     *
79
     * @return ResourceType[]
80
     */
81
    public function getTypes()
82
    {
83
        return array_values($this->resourceTypes);
84
    }
85
86
    /**
87
     * get a resource set based on the specified resource set name.
88
     *
89
     * @param string $name Name of the resource set
90
     *
91
     * @return ResourceSet|null resource set with the given name if found else NULL
92
     */
93
    public function resolveResourceSet($name)
94
    {
95
        if (array_key_exists($name, $this->resourceSets)) {
96
            return $this->resourceSets[$name];
97
        }
98
    }
99
100
    /**
101
     * get a resource type based on the resource type name.
102
     *
103
     * @param string $name Name of the resource type
104
     *
105
     * @return ResourceType|null resource type with the given resource type name if found else NULL
106
     */
107
    public function resolveResourceType($name)
108
    {
109
        if (array_key_exists($name, $this->resourceTypes)) {
110
            return $this->resourceTypes[$name];
111
        }
112
    }
113
114
    /**
115
     * get a resource set based on the specified resource association set name.
116
     *
117
     * @param string $name Name of the resource assocation set
118
     *
119
     * @return ResourceAssociationSet|null resource association set with the given name if found else NULL
120
     */
121
    public function resolveAssociationSet($name)
122
    {
123
        if (array_key_exists($name, $this->associationSets)) {
124
            return $this->associationSets[$name];
125
        }
126
    }
127
128
    /**
129
     * The method must return a collection of all the types derived from
130
     * $resourceType The collection returned should NOT include the type
131
     * passed in as a parameter.
132
     *
133
     * @param ResourceType $resourceType Resource to get derived resource types from
134
     *
135
     * @return ResourceType[]
136
     */
137
    public function getDerivedTypes(ResourceType $resourceType)
138
    {
139
        return [];
140
    }
141
142
    /**
143
     * @param ResourceType $resourceType Resource to check for derived resource types
144
     *
145
     * @return bool true if $resourceType represents an Entity Type which has derived Entity Types, else false
146
     */
147
    public function hasDerivedTypes(ResourceType $resourceType)
148
    {
149
        return false;
150
    }
151
152
    /**
153
     * Gets the ResourceAssociationSet instance for the given source
154
     * association end.
155
     *
156
     * @param ResourceSet      $sourceResourceSet      Resource set
157
     *                                                 of the source
158
     *                                                 association end
159
     * @param ResourceType     $sourceResourceType     Resource type of the source
160
     *                                                 association end
161
     * @param ResourceProperty $targetResourceProperty Resource property of
162
     *                                                 the source
163
     *                                                 association end
164
     *
165
     * @return ResourceAssociationSet|null
166
     */
167
    public function getResourceAssociationSet(ResourceSet $sourceResourceSet, ResourceType $sourceResourceType, ResourceProperty $targetResourceProperty)
168
    {
169
        //e.g.
170
        //ResourceSet => Representing 'Customers' entity set
171
        //ResourceType => Representing'Customer' entity type
172
        //ResourceProperty => Representing 'Orders' property
173
        //We have created ResourceAssoicationSet while adding
174
        //ResourceSetReference or ResourceReference
175
        //and kept in $this->associationSets
176
        //$metadata->addResourceSetReferenceProperty(
177
        //             $customersEntityType,
178
        //             'Orders',
179
        //             $ordersResourceSet
180
        //             );
181
182
        $targetResourceSet = $targetResourceProperty->getResourceType()->getCustomState();
183
        if (is_null($targetResourceSet)) {
184
            throw new InvalidOperationException(
185
                'Failed to retrieve the custom state from ' . $targetResourceProperty->getResourceType()->getName()
186
            );
187
        }
188
189
        //Customer_Orders_Orders, Order_Customer_Customers
190
        $key = ResourceAssociationSet::keyName(
191
            $sourceResourceType,
192
            $targetResourceProperty->getName(),
193
            $targetResourceSet
194
        );
195
196
        $associationSet = array_key_exists($key, $this->associationSets) ? $this->associationSets[$key] : null;
197
        assert(
198
            null == $associationSet || $associationSet instanceof ResourceAssociationSet,
199
            "Retrieved resource assocation must be either null or an instance of ResourceAssociationSet"
200
        );
201
        return $associationSet;
202
    }
203
204
    //End Implementation of IMetadataProvider
205
206
    /**
207
     * @param string $containerName container name for the datasource
208
     * @param string $namespaceName namespace for the datasource
209
     */
210
    public function __construct($containerName, $namespaceName)
211
    {
212
        $this->containerName = $containerName;
213
        $this->namespaceName = $namespaceName;
214
    }
215
216
    /**
217
     * Add an entity type.
218
     *
219
     * @param \ReflectionClass $refClass  reflection class of the entity
220
     * @param string           $name      name of the entity
221
     * @param string           $namespace namespace of the data source
222
     *
223
     * @throws InvalidOperationException when the name is already in use
224
     *
225
     * @return ResourceType
226
     */
227
    public function addEntityType(\ReflectionClass $refClass, $name, $namespace = null)
228
    {
229
        return $this->createResourceType($refClass, $name, $namespace, ResourceTypeKind::ENTITY, null);
230
    }
231
232
    /**
233
     * Add a complex type.
234
     *
235
     * @param \ReflectionClass $refClass         reflection class of the complex entity type
236
     * @param string           $name             name of the entity
237
     * @param string           $namespace        namespace of the data source
238
     * @param ResourceType     $baseResourceType base resource type
239
     *
240
     * @throws InvalidOperationException when the name is already in use
241
     *
242
     * @return ResourceType
243
     */
244
    public function addComplexType(\ReflectionClass $refClass, $name, $namespace = null, $baseResourceType = null)
245
    {
246
        return $this->createResourceType($refClass, $name, $namespace, ResourceTypeKind::COMPLEX, $baseResourceType);
247
    }
248
249
    /**
250
     * @param string       $name         name of the resource set
251
     * @param ResourceType $resourceType resource type
252
     *
253
     * @throws InvalidOperationException
254
     *
255
     * @return ResourceSet
256
     */
257
    public function addResourceSet($name, ResourceType $resourceType)
258
    {
259
        if (array_key_exists($name, $this->resourceSets)) {
260
            throw new InvalidOperationException('Resource Set already added');
261
        }
262
263
        $this->resourceSets[$name] = new ResourceSet($name, $resourceType);
264
        //No support for multiple ResourceSet with same EntityType
265
        //So keeping reference to the 'ResourceSet' with the entity type
266
        $resourceType->setCustomState($this->resourceSets[$name]);
267
        ksort($this->resourceSets);
268
269
        return $this->resourceSets[$name];
270
    }
271
272
    /**
273
     * To add a Key-primitive property to a resouce (Complex/Entuty).
274
     *
275
     * @param ResourceType $resourceType resource type to which key property
276
     *                                   is to be added
277
     * @param string       $name         name of the key property
278
     * @param TypeCode     $typeCode     type of the key property
279
     */
280
    public function addKeyProperty($resourceType, $name, $typeCode)
281
    {
282
        $this->_addPrimitivePropertyInternal($resourceType, $name, $typeCode, true);
283
    }
284
285
    /**
286
     * To add a NonKey-primitive property (Complex/Entity).
287
     *
288
     * @param ResourceType $resourceType resource type to which key property
289
     *                                   is to be added
290
     * @param string       $name         name of the key property
291
     * @param TypeCode     $typeCode     type of the key property
292
     * @param bool         $isBag        property is bag or not
293
     */
294
    public function addPrimitiveProperty($resourceType, $name, $typeCode, $isBag = false)
295
    {
296
        $this->_addPrimitivePropertyInternal($resourceType, $name, $typeCode, false, $isBag);
297
    }
298
299
    /**
300
     * To add a non-key etag property.
301
     *
302
     * @param ResourceType $resourceType resource type to which key property
303
     *                                   is to be added
304
     * @param string       $name         name of the property
305
     * @param TypeCode     $typeCode     type of the etag property
306
     */
307
    public function addETagProperty($resourceType, $name, $typeCode)
308
    {
309
        $this->_addPrimitivePropertyInternal($resourceType, $name, $typeCode, false, false, true);
310
    }
311
312
    /**
313
     * To add a resource reference property.
314
     *
315
     * @param ResourceType $resourceType      The resource type to add the resource
316
     *                                        reference property to
317
     * @param string       $name              The name of the property to add
318
     * @param ResourceSet  $targetResourceSet The resource set the resource reference
319
     *                                        property points to
320
     */
321
    public function addResourceReferenceProperty($resourceType, $name, $targetResourceSet)
322
    {
323
        $this->_addReferencePropertyInternal(
324
            $resourceType,
325
            $name,
326
            $targetResourceSet,
327
            ResourcePropertyKind::RESOURCE_REFERENCE
328
        );
329
    }
330
331
    /**
332
     * To add a resource set reference property.
333
     *
334
     * @param ResourceType $resourceType      The resource type to add the
335
     *                                        resource reference set property to
336
     * @param string       $name              The name of the property to add
337
     * @param ResourceSet  $targetResourceSet The resource set the resource
338
     *                                        reference set property points to
339
     */
340
    public function addResourceSetReferenceProperty($resourceType, $name, $targetResourceSet)
341
    {
342
        $this->_addReferencePropertyInternal(
343
            $resourceType,
344
            $name,
345
            $targetResourceSet,
346
            ResourcePropertyKind::RESOURCESET_REFERENCE
347
        );
348
    }
349
350
    /**
351
     * To add a complex property to entity or complex type.
352
     *
353
     * @param ResourceType $resourceType        The resource type to which the
354
     *                                          complex property needs to add
355
     * @param string       $name                name of the complex property
356
     * @param ResourceType $complexResourceType complex resource type
357
     * @param bool         $isBag               complex type is bag or not
358
     *
359
     * @return ResourceProperty
360
     */
361
    public function addComplexProperty($resourceType, $name, $complexResourceType, $isBag = false)
362
    {
363
        if ($resourceType->getResourceTypeKind() != ResourceTypeKind::ENTITY
364
            && $resourceType->getResourceTypeKind() != ResourceTypeKind::COMPLEX
365
        ) {
366
            throw new InvalidOperationException('Complex property can be added to an entity or another complex type');
367
        }
368
369
        $this->checkInstanceProperty($name, $resourceType);
370
371
        $kind = ResourcePropertyKind::COMPLEX_TYPE;
372
        if ($isBag) {
373
            $kind = $kind | ResourcePropertyKind::BAG;
374
        }
375
376
        $resourceProperty = new ResourceProperty($name, null, $kind, $complexResourceType);
377
        $resourceType->addProperty($resourceProperty);
378
379
        return $resourceProperty;
380
    }
381
382
    /**
383
     * To add a Key/NonKey-primitive property to a resource (complex/entity).
384
     *
385
     * @param ResourceType $resourceType   Resource type
386
     * @param string       $name           name of the property
387
     * @param TypeCode     $typeCode       type of property
388
     * @param bool         $isKey          property is key or not
389
     * @param bool         $isBag          property is bag or not
390
     * @param bool         $isETagProperty property is etag or not
391
     */
392
    private function _addPrimitivePropertyInternal($resourceType, $name, $typeCode, $isKey = false, $isBag = false, $isETagProperty = false)
393
    {
394
        $this->checkInstanceProperty($name, $resourceType);
395
396
        $primitiveResourceType = ResourceType::getPrimitiveResourceType($typeCode);
0 ignored issues
show
Documentation introduced by
$typeCode is of type object<POData\Providers\Metadata\Type\TypeCode>, but the function expects a object<POData\Providers\...\Type\EdmPrimitiveType>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
397
398
        if ($isETagProperty && $isBag) {
399
            throw new InvalidOperationException(
400
                'Only primitve property can be etag property, bag property cannot be etag property.'
401
            );
402
        }
403
404
        $kind = $isKey ? ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY : ResourcePropertyKind::PRIMITIVE;
405
        if ($isBag) {
406
            $kind = $kind | ResourcePropertyKind::BAG;
407
        }
408
409
        if ($isETagProperty) {
410
            $kind = $kind | ResourcePropertyKind::ETAG;
411
        }
412
413
        $resourceProperty = new ResourceProperty($name, null, $kind, $primitiveResourceType);
414
        $resourceType->addProperty($resourceProperty);
415
    }
416
417
    /**
418
     * To add a navigation property (resource set or resource reference)
419
     * to a resource type.
420
     *
421
     * @param ResourceType         $resourceType         The resource type to add
422
     *                                                   the resource reference
423
     *                                                   or resource
424
     *                                                   reference set property to
425
     * @param string               $name                 The name of the
426
     *                                                   property to add
427
     * @param ResourceSet          $targetResourceSet    The resource set the
428
     *                                                   resource reference
429
     *                                                   or reference
430
     *                                                   set property
431
     *                                                   points to
432
     * @param ResourcePropertyKind $resourcePropertyKind The property kind
433
     */
434
    private function _addReferencePropertyInternal(
435
        ResourceType $resourceType,
436
        $name,
437
        ResourceSet $targetResourceSet,
438
        $resourcePropertyKind
439
    ) {
440
        $this->checkInstanceProperty($name, $resourceType);
441
442
        if (!($resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE
443
            || $resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE)
444
        ) {
445
            throw new InvalidOperationException(
446
                'Property kind should be ResourceSetReference or ResourceReference'
447
            );
448
        }
449
450
        $targetResourceType = $targetResourceSet->getResourceType();
451
        $resourceProperty = new ResourceProperty($name, null, $resourcePropertyKind, $targetResourceType);
452
        $resourceType->addProperty($resourceProperty);
453
454
        //Create instance of AssociationSet for this relationship
455
        $sourceResourceSet = $resourceType->getCustomState();
456
        if (is_null($sourceResourceSet)) {
457
            throw new InvalidOperationException('Failed to retrieve the custom state from ' . $resourceType->getName());
458
        }
459
460
        //Customer_Orders_Orders, Order_Customer_Customers
461
        //(source type::name _ source property::name _ target set::name)
462
        $setKey = ResourceAssociationSet::keyName($resourceType, $name, $targetResourceSet);
463
        //$setKey = $resourceType->getName() . '_' . $name . '_' . $targetResourceType->getName();
0 ignored issues
show
Unused Code Comprehensibility introduced by
44% 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...
464
        $set = new ResourceAssociationSet(
465
            $setKey,
466
            new ResourceAssociationSetEnd($sourceResourceSet, $resourceType, $resourceProperty),
467
            new ResourceAssociationSetEnd($targetResourceSet, $targetResourceType, null)
468
        );
469
        $this->associationSets[$setKey] = $set;
470
    }
471
472
    /**
473
     * @param \ReflectionClass $refClass
474
     * @param string $name
475
     * @param string|null $namespace
476
     * @param $typeKind
477
     * @param null|ResourceType $baseResourceType
478
     *
479
     * @throws InvalidOperationException
480
     *
481
     * @return ResourceType
482
     */
483
    private function createResourceType(
484
        \ReflectionClass $refClass,
485
        $name,
486
        $namespace,
487
        $typeKind,
488
        $baseResourceType
489
    ) {
490
        if (array_key_exists($name, $this->resourceTypes)) {
491
            throw new InvalidOperationException('Type with same name already added');
492
        }
493
494
        $entityType = new ResourceType($refClass, $typeKind, $name, $namespace, $baseResourceType);
495
        $this->resourceTypes[$name] = $entityType;
496
        ksort($this->resourceTypes);
497
498
        return $entityType;
499
    }
500
501
    /**
502
     * @param string $name
503
     * @param ResourceType $resourceType
504
     *
505
     * @throws InvalidOperationException
506
     */
507
    private function checkInstanceProperty($name, ResourceType $resourceType)
508
    {
509
        $instance = $resourceType->getInstanceType();
510
        $hasMagicGetter = $instance instanceof IType || $instance->hasMethod('__get');
511
512
        if (!$hasMagicGetter) {
513
            try {
514
                if ($instance instanceof \ReflectionClass) {
515
                    $instance->getProperty($name);
516
                }
517
            } catch (\ReflectionException $exception) {
518
                throw new InvalidOperationException(
519
                    'Can\'t add a property which does not exist on the instance type.'
520
                );
521
            }
522
        }
523
    }
524
}
525