Completed
Push — master ( ee9e31...e301b1 )
by Alex
03:47
created

SimpleMetadataProvider::checkInstanceProperty()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 17
rs 8.8571
cc 5
eloc 10
nc 8
nop 2
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
use ReflectionClass;
9
10
/**
11
 * Class SimpleMetadataProvider.
12
 */
13
class SimpleMetadataProvider implements IMetadataProvider
14
{
15
    protected $resourceSets = array();
16
    protected $resourceTypes = array();
17
    protected $associationSets = array();
18
    protected $containerName;
19
    protected $namespaceName;
20
    public $mappedDetails = null;
21
22
    //Begin Implementation of IMetadataProvider
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
        return $return;
73
    }
74
75
    /**
76
     * get all resource types in the data source.
77
     *
78
     * @return ResourceType[]
79
     */
80
    public function getTypes()
81
    {
82
        return array_values($this->resourceTypes);
83
    }
84
85
    /**
86
     * get a resource set based on the specified resource set name.
87
     *
88
     * @param string $name Name of the resource set
89
     *
90
     * @return ResourceSet|null resource set with the given name if found else NULL
91
     */
92
    public function resolveResourceSet($name)
93
    {
94
        if (array_key_exists($name, $this->resourceSets)) {
95
            return $this->resourceSets[$name];
96
        }
97
98
        return null;
99
    }
100
101
    /**
102
     * get a resource type based on the resource set name.
103
     *
104
     * @param string $name Name of the resource set
105
     *
106
     * @return ResourceType|null resource type with the given resource set name if found else NULL
107
     */
108
    public function resolveResourceType($name)
109
    {
110
        if (array_key_exists($name, $this->resourceTypes)) {
111
            return $this->resourceTypes[$name];
112
        }
113
114
        return null;
115
    }
116
117
    /**
118
     * The method must return a collection of all the types derived from
119
     * $resourceType The collection returned should NOT include the type
120
     * passed in as a parameter.
121
     *
122
     * @param ResourceType $resourceType Resource to get derived resource types from
123
     *
124
     * @return ResourceType[]
125
     */
126
    public function getDerivedTypes(ResourceType $resourceType)
127
    {
128
        return array();
129
    }
130
131
    /**
132
     * @param ResourceType $resourceType Resource to check for derived resource types
133
     *
134
     * @return bool true if $resourceType represents an Entity Type which has derived Entity Types, else false
135
     */
136
    public function hasDerivedTypes(ResourceType $resourceType)
137
    {
138
        return false;
139
    }
140
141
    /**
142
     * Gets the ResourceAssociationSet instance for the given source
143
     * association end.
144
     *
145
     * @param ResourceSet      $sourceResourceSet      Resource set
146
     *                                                 of the source
147
     *                                                 association end
148
     * @param ResourceType     $sourceResourceType     Resource type of the source
149
     *                                                 association end
150
     * @param ResourceProperty $targetResourceProperty Resource property of
151
     *                                                 the source
152
     *                                                 association end
153
     *
154
     * @return ResourceAssociationSet
155
     */
156
    public function getResourceAssociationSet(ResourceSet $sourceResourceSet, ResourceType $sourceResourceType, ResourceProperty $targetResourceProperty)
157
    {
158
        //e.g.
159
        //ResourceSet => Representing 'Customers' entity set
160
        //ResourceType => Representing'Customer' entity type
161
        //ResourceProperty => Representing 'Orders' property
162
        //We have created ResourceAssoicationSet while adding
163
        //ResourceSetReference or ResourceReference
164
        //and kept in $this->associationSets
165
        //$metadata->addResourceSetReferenceProperty(
166
        //             $customersEntityType,
167
        //             'Orders',
168
        //             $ordersResourceSet
169
        //             );
170
171
        $targetResourceSet = $targetResourceProperty->getResourceType()->getCustomState();
172
        if (is_null($targetResourceSet)) {
173
            throw new InvalidOperationException('Failed to retrieve the custom state from ' . $targetResourceProperty->getResourceType()->getName());
174
        }
175
176
        //Customer_Orders_Orders, Order_Customer_Customers
177
        $key = $sourceResourceType->getName() . '_' . $targetResourceProperty->getName() . '_' . $targetResourceSet->getName();
178
        if (array_key_exists($key, $this->associationSets)) {
179
            return $this->associationSets[$key];
180
        }
181
182
        return null;
183
    }
184
185
    //End Implementation of IMetadataProvider
186
187
    /**
188
     * @param string $containerName container name for the datasource
189
     * @param string $namespaceName namespace for the datasource
190
     */
191
    public function __construct($containerName, $namespaceName)
192
    {
193
        $this->containerName = $containerName;
194
        $this->namespaceName = $namespaceName;
195
    }
196
197
    /**
198
     * Add an entity type.
199
     *
200
     * @param \ReflectionClass $refClass  reflection class of the entity
201
     * @param string           $name      name of the entity
202
     * @param string           $namespace namespace of the data source
203
     *
204
     * @return ResourceType
205
     *
206
     * @throws InvalidOperationException when the name is already in use
207
     */
208
    public function addEntityType(\ReflectionClass $refClass, $name, $namespace = null)
209
    {
210
        return $this->createResourceType($refClass, $name, $namespace, ResourceTypeKind::ENTITY, null);
211
    }
212
213
    /**
214
     * Add a complex type.
215
     *
216
     * @param \ReflectionClass $refClass         reflection class of the complex entity type
217
     * @param string           $name             name of the entity
218
     * @param string           $namespace        namespace of the data source
219
     * @param ResourceType     $baseResourceType base resource type
220
     *
221
     * @return ResourceType
222
     *
223
     * @throws InvalidOperationException when the name is already in use
224
     */
225
    public function addComplexType(\ReflectionClass $refClass, $name, $namespace = null, $baseResourceType = null)
226
    {
227
        return $this->createResourceType($refClass, $name, $namespace, ResourceTypeKind::COMPLEX, $baseResourceType);
228
    }
229
230
    /**
231
     * @param string       $name         name of the resource set
232
     * @param ResourceType $resourceType resource type
233
     *
234
     * @throws InvalidOperationException
235
     *
236
     * @return ResourceSet
237
     */
238
    public function addResourceSet($name, ResourceType $resourceType)
239
    {
240
        if (array_key_exists($name, $this->resourceSets)) {
241
            throw new InvalidOperationException('Resource Set already added');
242
        }
243
244
        $this->resourceSets[$name] = new ResourceSet($name, $resourceType);
245
        //No support for multiple ResourceSet with same EntityType
246
        //So keeping reference to the 'ResourceSet' with the entity type
247
        $resourceType->setCustomState($this->resourceSets[$name]);
248
        ksort($this->resourceSets);
249
250
        return $this->resourceSets[$name];
251
    }
252
253
    /**
254
     * To add a Key-primitive property to a resouce (Complex/Entuty).
255
     *
256
     * @param ResourceType $resourceType resource type to which key property
257
     *                                   is to be added
258
     * @param string       $name         name of the key property
259
     * @param TypeCode     $typeCode     type of the key property
260
     */
261
    public function addKeyProperty($resourceType, $name, $typeCode)
262
    {
263
        $this->_addPrimitivePropertyInternal($resourceType, $name, $typeCode, true);
264
    }
265
266
    /**
267
     * To add a NonKey-primitive property (Complex/Entity).
268
     *
269
     * @param ResourceType $resourceType resource type to which key property
270
     *                                   is to be added
271
     * @param string       $name         name of the key property
272
     * @param TypeCode     $typeCode     type of the key property
273
     * @param bool         $isBag        property is bag or not
274
     */
275
    public function addPrimitiveProperty($resourceType, $name, $typeCode, $isBag = false)
276
    {
277
        $this->_addPrimitivePropertyInternal($resourceType, $name, $typeCode, false, $isBag);
278
    }
279
280
    /**
281
     * To add a non-key etag property.
282
     *
283
     * @param ResourceType $resourceType resource type to which key property
284
     *                                   is to be added
285
     * @param string       $name         name of the property
286
     * @param TypeCode     $typeCode     type of the etag property
287
     */
288
    public function addETagProperty($resourceType, $name, $typeCode)
289
    {
290
        $this->_addPrimitivePropertyInternal($resourceType, $name, $typeCode, false, false, true);
291
    }
292
293
    /**
294
     * To add a resource reference property.
295
     *
296
     * @param ResourceType $resourceType      The resource type to add the resource
297
     *                                        reference property to
298
     * @param string       $name              The name of the property to add
299
     * @param ResourceSet  $targetResourceSet The resource set the resource reference
300
     *                                        property points to
301
     */
302
    public function addResourceReferenceProperty($resourceType, $name, $targetResourceSet)
303
    {
304
        $this->_addReferencePropertyInternal(
305
            $resourceType,
306
            $name,
307
            $targetResourceSet,
308
            ResourcePropertyKind::RESOURCE_REFERENCE
309
        );
310
    }
311
312
    /**
313
     * To add a resource set reference property.
314
     *
315
     * @param ResourceType $resourceType      The resource type to add the
316
     *                                        resource reference set property to
317
     * @param string       $name              The name of the property to add
318
     * @param ResourceSet  $targetResourceSet The resource set the resource
319
     *                                        reference set property points to
320
     */
321
    public function addResourceSetReferenceProperty($resourceType, $name, $targetResourceSet)
322
    {
323
        $this->_addReferencePropertyInternal(
324
            $resourceType,
325
            $name,
326
            $targetResourceSet,
327
            ResourcePropertyKind::RESOURCESET_REFERENCE
328
        );
329
    }
330
331
    /**
332
     * To add a complex property to entity or complex type.
333
     *
334
     * @param ResourceType $resourceType        The resource type to which the
335
     *                                          complex property needs to add
336
     * @param string       $name                name of the complex property
337
     * @param ResourceType $complexResourceType complex resource type
338
     * @param bool         $isBag               complex type is bag or not
339
     *
340
     * @return ResourceProperty
341
     */
342
    public function addComplexProperty($resourceType, $name, $complexResourceType, $isBag = false)
343
    {
344
        if ($resourceType->getResourceTypeKind() != ResourceTypeKind::ENTITY
345
            && $resourceType->getResourceTypeKind() != ResourceTypeKind::COMPLEX
346
        ) {
347
            throw new InvalidOperationException('Complex property can be added to an entity or another complex type');
348
        }
349
350
        $this->checkInstanceProperty($name, $resourceType);
351
352
        $kind = ResourcePropertyKind::COMPLEX_TYPE;
353
        if ($isBag) {
354
            $kind = $kind | ResourcePropertyKind::BAG;
355
        }
356
357
        $resourceProperty = new ResourceProperty($name, null, $kind, $complexResourceType);
358
        $resourceType->addProperty($resourceProperty);
359
360
        return $resourceProperty;
361
    }
362
363
    /**
364
     * To add a Key/NonKey-primitive property to a resource (complex/entity).
365
     *
366
     * @param ResourceType $resourceType   Resource type
367
     * @param string       $name           name of the property
368
     * @param TypeCode     $typeCode       type of property
369
     * @param bool         $isKey          property is key or not
370
     * @param bool         $isBag          property is bag or not
371
     * @param bool         $isETagProperty property is etag or not
372
     */
373
    private function _addPrimitivePropertyInternal($resourceType, $name, $typeCode, $isKey = false, $isBag = false, $isETagProperty = false)
374
    {
375
        $this->checkInstanceProperty($name, $resourceType);
376
377
        $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...
378
379
        if ($isETagProperty && $isBag) {
380
            throw new InvalidOperationException(
381
                'Only primitve property can be etag property, bag property cannot be etag property.'
382
            );
383
        }
384
385
        $kind = $isKey ? ResourcePropertyKind::PRIMITIVE | ResourcePropertyKind::KEY : ResourcePropertyKind::PRIMITIVE;
386
        if ($isBag) {
387
            $kind = $kind | ResourcePropertyKind::BAG;
388
        }
389
390
        if ($isETagProperty) {
391
            $kind = $kind | ResourcePropertyKind::ETAG;
392
        }
393
394
        $resourceProperty = new ResourceProperty($name, null, $kind, $primitiveResourceType);
395
        $resourceType->addProperty($resourceProperty);
396
    }
397
398
    /**
399
     * To add a navigation property (resource set or resource reference)
400
     * to a resource type.
401
     *
402
     * @param ResourceType         $resourceType         The resource type to add
403
     *                                                   the resource reference
404
     *                                                   or resource
405
     *                                                   reference set property to
406
     * @param string               $name                 The name of the
407
     *                                                   property to add
408
     * @param ResourceSet          $targetResourceSet    The resource set the
409
     *                                                   resource reference
410
     *                                                   or reference
411
     *                                                   set property
412
     *                                                   ponits to
413
     * @param ResourcePropertyKind $resourcePropertyKind The property kind
414
     */
415
    private function _addReferencePropertyInternal(
416
        ResourceType $resourceType,
417
        $name,
418
        ResourceSet $targetResourceSet,
419
        $resourcePropertyKind
420
    ) {
421
        $this->checkInstanceProperty($name, $resourceType);
422
423
        if (!($resourcePropertyKind == ResourcePropertyKind::RESOURCESET_REFERENCE
424
            || $resourcePropertyKind == ResourcePropertyKind::RESOURCE_REFERENCE)
425
        ) {
426
            throw new InvalidOperationException(
427
                'Property kind should be ResourceSetReference or ResourceReference'
428
            );
429
        }
430
431
        $targetResourceType = $targetResourceSet->getResourceType();
432
        $resourceProperty = new ResourceProperty($name, null, $resourcePropertyKind, $targetResourceType);
433
        $resourceType->addProperty($resourceProperty);
434
435
        //Create instance of AssociationSet for this relationship
436
        $sourceResourceSet = $resourceType->getCustomState();
437
        if (is_null($sourceResourceSet)) {
438
            throw new InvalidOperationException('Failed to retrieve the custom state from ' . $resourceType->getName());
439
        }
440
441
        //Customer_Orders_Orders, Order_Customer_Customers
442
        //(source type::name _ source property::name _ target set::name)
443
        $setKey = $resourceType->getName() . '_' . $name . '_' . $targetResourceSet->getName();
444
        $set = new ResourceAssociationSet(
445
            $setKey,
446
            new ResourceAssociationSetEnd($sourceResourceSet, $resourceType, $resourceProperty),
447
            new ResourceAssociationSetEnd($targetResourceSet, $targetResourceSet->getResourceType(), null)
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object<POData\Providers\...adata\ResourceProperty>.

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...
448
        );
449
        $this->associationSets[$setKey] = $set;
450
    }
451
452
    /**
453
     * @param \ReflectionClass $refClass
454
     * @param $name
455
     * @param $namespace
456
     * @param $typeKind
457
     * @param $baseResourceType
458
     * @return ResourceType
459
     * @throws InvalidOperationException
460
     */
461
    private function createResourceType(
462
        \ReflectionClass $refClass,
463
        $name,
464
        $namespace,
465
        $typeKind,
466
        $baseResourceType
467
    ) {
468
        if (array_key_exists($name, $this->resourceTypes)) {
469
            throw new InvalidOperationException('Type with same name already added');
470
        }
471
472
        $entityType = new ResourceType($refClass, $typeKind, $name, $namespace, $baseResourceType);
473
        $this->resourceTypes[$name] = $entityType;
474
        ksort($this->resourceTypes);
475
476
        return $entityType;
477
    }
478
479
    /**
480
     * @param $name
481
     * @param ResourceType $resourceType
482
     * @throws InvalidOperationException
483
     */
484
    private function checkInstanceProperty($name, ResourceType $resourceType)
485
    {
486
        $instance = $resourceType->getInstanceType();
487
        $hasMagicGetter = $instance instanceof IType || $instance->hasMethod('__get');
488
489
        if (!$hasMagicGetter) {
490
            try {
491
                if ($instance instanceof \ReflectionClass) {
492
                    $instance->getProperty($name);
493
                }
494
            } catch (\ReflectionException $exception) {
495
                throw new InvalidOperationException(
496
                    'Can\'t add a property which does not exist on the instance type.'
497
                );
498
            }
499
        }
500
    }
501
}
502