Completed
Push — master ( daff1e...e8ad81 )
by Alex
05:01
created

MetadataManager::getEdmxXML()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 4
nc 1
nop 0
1
<?php
2
3
namespace AlgoWeb\ODataMetadata;
4
5
use AlgoWeb\ODataMetadata\MetadataV3\edm\EntityContainer\AssociationSetAnonymousType;
6
use AlgoWeb\ODataMetadata\MetadataV3\edm\EntityContainer\AssociationSetAnonymousType\EndAnonymousType;
7
use AlgoWeb\ODataMetadata\MetadataV3\edm\EntityContainer\EntitySetAnonymousType;
8
use AlgoWeb\ODataMetadata\MetadataV3\edm\EntityContainer\FunctionImportAnonymousType;
9
use AlgoWeb\ODataMetadata\MetadataV3\edm\TAssociationEndType;
10
use AlgoWeb\ODataMetadata\MetadataV3\edm\TAssociationType;
11
use AlgoWeb\ODataMetadata\MetadataV3\edm\TComplexTypePropertyType;
12
use AlgoWeb\ODataMetadata\MetadataV3\edm\TComplexTypeType;
13
use AlgoWeb\ODataMetadata\MetadataV3\edm\TConstraintType;
14
use AlgoWeb\ODataMetadata\MetadataV3\edm\TDocumentationType;
15
use AlgoWeb\ODataMetadata\MetadataV3\edm\TEntityPropertyType;
16
use AlgoWeb\ODataMetadata\MetadataV3\edm\TEntityTypeType;
17
use AlgoWeb\ODataMetadata\MetadataV3\edm\TFunctionImportReturnTypeType;
18
use AlgoWeb\ODataMetadata\MetadataV3\edm\TFunctionReturnTypeType;
19
use AlgoWeb\ODataMetadata\MetadataV3\edm\TFunctionType;
20
use AlgoWeb\ODataMetadata\MetadataV3\edm\TNavigationPropertyType;
21
use AlgoWeb\ODataMetadata\MetadataV3\edm\TPropertyRefType;
22
use AlgoWeb\ODataMetadata\MetadataV3\edm\TReferentialConstraintRoleElementType;
23
use AlgoWeb\ODataMetadata\MetadataV3\edm\TTextType;
24
use AlgoWeb\ODataMetadata\MetadataV3\edmx\Edmx;
25
use Illuminate\Support\Str;
26
use JMS\Serializer\SerializerBuilder;
27
28
class MetadataManager
29
{
30
    private $V3Edmx = null;
31
    private $lastError = null;
32
    private $serializer = null;
33
34
    public function __construct($namespaceName = "Data", $containerName = "DefaultContainer", Edmx $edmx = null)
35
    {
36
        $msg = null;
37
        $this->V3Edmx = (null == $edmx) ? new Edmx($namespaceName, $containerName) : $edmx;
38
        assert($this->V3Edmx->isOK($msg), $msg);
39
        $this->initSerialiser();
40
        assert(null != $this->serializer, "Serializer must not be null at end of constructor");
41
    }
42
43
    public function getEdmx()
44
    {
45
        $msg = null;
46
        assert($this->V3Edmx->isOK($msg), $msg);
47
        return $this->V3Edmx;
48
    }
49
50
    public function getEdmxXML()
51
    {
52
        $cereal = $this->getSerialiser();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $cereal is correct as $this->getSerialiser() (which targets AlgoWeb\ODataMetadata\Me...anager::getSerialiser()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
53
        assert(null != $cereal, "Serializer must not be null when trying to get edmx xml");
54
        return $cereal->serialize($this->getEdmx(), "xml");
0 ignored issues
show
Bug introduced by
The method serialize cannot be called on $cereal (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
55
    }
56
57
    public function addEntityType($name, $accessType = "Public", $summary = null, $longDescription = null)
58
    {
59
        $NewEntity = new TEntityTypeType();
60
        $NewEntity->setName($name);
61
        $this->addDocumentation($summary, $longDescription, $NewEntity);
62
63
        $entitySet = new EntitySetAnonymousType();
64
        $entitySet->setName(Str::plural($NewEntity->getName()));
65
        $namespace = $this->getNamespace();
66
        $entityTypeName = $namespace . $NewEntity->getName();
67
        $entitySet->setEntityType($entityTypeName);
68
        $entitySet->setGetterAccess($accessType);
69
70
        $this->V3Edmx->getDataServiceType()->getSchema()[0]->addToEntityType($NewEntity);
0 ignored issues
show
Compatibility introduced by
$NewEntity of type object<AlgoWeb\ODataMetadata\IsOK> is not a sub-type of object<AlgoWeb\ODataMeta...V3\edm\TEntityTypeType>. It seems like you assume a child class of the class AlgoWeb\ODataMetadata\IsOK to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
71
        $this->V3Edmx->getDataServiceType()->getSchema()[0]->getEntityContainer()[0]->addToEntitySet($entitySet);
72
        assert($this->V3Edmx->isOK($this->lastError), $this->lastError);
73
        return [$NewEntity, $entitySet];
74
    }
75
76
    public function addComplexType($name, $accessType = "Public", $summary = null, $longDescription = null)
77
    {
78
        $NewEntity = new TComplexTypeType();
79
        $NewEntity->setName($name);
80
        $NewEntity->setTypeAccess($accessType);
81
        $this->addDocumentation($summary, $longDescription, $NewEntity);
82
        assert($NewEntity->isOK($this->lastError), $this->lastError);
83
        $this->V3Edmx->getDataServiceType()->getSchema()[0]->addToComplexType($NewEntity);
0 ignored issues
show
Compatibility introduced by
$NewEntity of type object<AlgoWeb\ODataMetadata\IsOK> is not a sub-type of object<AlgoWeb\ODataMeta...3\edm\TComplexTypeType>. It seems like you assume a child class of the class AlgoWeb\ODataMetadata\IsOK to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
84
85
        return $NewEntity;
86
    }
87
88
    public function getSerialiser()
89
    {
90
        return $this->serializer;
91
    }
92
93
    public function addPropertyToComplexType(
94
        \AlgoWeb\ODataMetadata\MetadataV3\edm\TComplexTypeType $complexType,
95
        $name,
96
        $type,
97
        $defaultValue = null,
98
        $nullable = false,
99
        $summary = null,
100
        $longDescription = null
101
    ) {
102
        if (is_array($defaultValue) || is_object($defaultValue)) {
103
            throw new \InvalidArgumentException("Default value cannot be object or array");
104
        }
105
        if (null != $defaultValue) {
106
            $defaultValue = var_export($defaultValue, true);
107
        }
108
        $NewProperty = new TComplexTypePropertyType();
109
        $NewProperty->setName($name);
110
        $NewProperty->setType($type);
111
        $NewProperty->setNullable($nullable);
112
        $this->addDocumentation($summary, $longDescription, $NewProperty);
113
        if (null != $defaultValue) {
114
            $NewProperty->setDefaultValue($defaultValue);
115
        }
116
        assert($NewProperty->isOK($this->lastError), $this->lastError);
117
        $complexType->addToProperty($NewProperty);
0 ignored issues
show
Compatibility introduced by
$NewProperty of type object<AlgoWeb\ODataMetadata\IsOK> is not a sub-type of object<AlgoWeb\ODataMeta...omplexTypePropertyType>. It seems like you assume a child class of the class AlgoWeb\ODataMetadata\IsOK to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
118
        return $NewProperty;
119
    }
120
121
    public function addPropertyToEntityType(
122
        TEntityTypeType $entityType,
123
        $name,
124
        $type,
125
        $defaultValue = null,
126
        $nullable = false,
127
        $isKey = false,
128
        $storeGeneratedPattern = null,
129
        $summary = null,
130
        $longDescription = null
131
    ) {
132
        $NewProperty = new TEntityPropertyType();
133
        $NewProperty->setName($name);
134
        $NewProperty->setType($type);
135
        $NewProperty->setStoreGeneratedPattern($storeGeneratedPattern);
136
        $NewProperty->setNullable($nullable);
137
        $this->addDocumentation($summary, $longDescription, $NewProperty);
138
        if (null != $defaultValue) {
139
            $NewProperty->setDefaultValue($defaultValue);
140
        }
141
        $entityType->addToProperty($NewProperty);
0 ignored issues
show
Compatibility introduced by
$NewProperty of type object<AlgoWeb\ODataMetadata\IsOK> is not a sub-type of object<AlgoWeb\ODataMeta...dm\TEntityPropertyType>. It seems like you assume a child class of the class AlgoWeb\ODataMetadata\IsOK to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
142
        if ($isKey) {
143
            $Key = new TPropertyRefType();
144
            $Key->setName($name);
145
            $entityType->addToKey($Key);
146
        }
147
        return $NewProperty;
148
    }
149
150
    public function addNavigationPropertyToEntityType(
151
        TEntityTypeType $principalType,
152
        $principalMultiplicity,
153
        $principalProperty,
154
        TEntityTypeType $dependentType,
155
        $dependentMultiplicity,
156
        $dependentProperty = "",
157
        array $principalConstraintProperty = null,
158
        array $dependentConstraintProperty = null,
159
        $principalGetterAccess = "Public",
160
        $principalSetterAccess = "Public",
161
        $dependentGetterAccess = "Public",
162
        $dependentSetterAccess = "Public",
163
        $principalSummery = null,
164
        $principalLongDescription = null,
165
        $dependentSummery = null,
166
        $dependentLongDescription = null
167
    ) {
168
        $principalEntitySetName = Str::plural($principalType->getName());
169
        $dependentEntitySetName = Str::plural($dependentType->getName());
170
        $relationName = $principalType->getName() . "_" . $principalProperty . "_"
171
                        . $dependentType->getName() . "_" . $dependentProperty;
172
        $relationName = trim($relationName, "_");
173
174
        $namespace = $this->getNamespace();
175
        $relationFQName = $namespace . $relationName;
176
177
        $principalNavigationProperty = new TNavigationPropertyType();
178
        $principalNavigationProperty->setName($principalProperty);
179
        $principalNavigationProperty->setToRole(trim($dependentEntitySetName . "_" . $dependentProperty, "_"));
180
        $principalNavigationProperty->setFromRole($principalEntitySetName . "_" . $principalProperty);
181
        $principalNavigationProperty->setRelationship($relationFQName);
182
        $principalNavigationProperty->setGetterAccess($principalGetterAccess);
183
        $principalNavigationProperty->setSetterAccess($principalSetterAccess);
184
        $this->addDocumentation($principalSummery, $principalLongDescription, $principalNavigationProperty);
185
        $principalType->addToNavigationProperty($principalNavigationProperty);
0 ignored issues
show
Compatibility introduced by
$principalNavigationProperty of type object<AlgoWeb\ODataMetadata\IsOK> is not a sub-type of object<AlgoWeb\ODataMeta...NavigationPropertyType>. It seems like you assume a child class of the class AlgoWeb\ODataMetadata\IsOK to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
186
        $dependentNavigationProperty = null;
187
        if (!empty($dependentProperty)) {
188
            $dependentNavigationProperty = new TNavigationPropertyType();
189
            $dependentNavigationProperty->setName($dependentProperty);
190
            $dependentNavigationProperty->setToRole($principalEntitySetName . "_" . $principalProperty);
191
            $dependentNavigationProperty->setFromRole($dependentEntitySetName . "_" . $dependentProperty);
192
            $dependentNavigationProperty->setRelationship($relationFQName);
193
            $dependentNavigationProperty->setGetterAccess($dependentGetterAccess);
194
            $dependentNavigationProperty->setSetterAccess($dependentSetterAccess);
195
            $this->addDocumentation($dependentSummery, $dependentLongDescription, $dependentNavigationProperty);
196
            $dependentType->addToNavigationProperty($dependentNavigationProperty);
0 ignored issues
show
Compatibility introduced by
$dependentNavigationProperty of type object<AlgoWeb\ODataMetadata\IsOK> is not a sub-type of object<AlgoWeb\ODataMeta...NavigationPropertyType>. It seems like you assume a child class of the class AlgoWeb\ODataMetadata\IsOK to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
197
        }
198
199
        $assocation = $this->createAssocationFromNavigationProperty(
200
            $principalType,
201
            $dependentType,
202
            $principalNavigationProperty,
0 ignored issues
show
Compatibility introduced by
$principalNavigationProperty of type object<AlgoWeb\ODataMetadata\IsOK> is not a sub-type of object<AlgoWeb\ODataMeta...NavigationPropertyType>. It seems like you assume a child class of the class AlgoWeb\ODataMetadata\IsOK to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
203
            $dependentNavigationProperty,
0 ignored issues
show
Bug introduced by
It seems like $dependentNavigationProperty can also be of type object<AlgoWeb\ODataMetadata\IsOK>; however, AlgoWeb\ODataMetadata\Me...romNavigationProperty() does only seem to accept null|object<AlgoWeb\ODat...NavigationPropertyType>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
204
            $principalMultiplicity,
205
            $dependentMultiplicity,
206
            $principalConstraintProperty,
207
            $dependentConstraintProperty
208
        );
209
210
        $this->V3Edmx->getDataServiceType()->getSchema()[0]->addToAssociation($assocation);
211
212
        $associationSet = $this->createAssocationSetForAssocation(
213
            $assocation,
214
            $principalEntitySetName,
215
            $dependentEntitySetName
216
        );
217
218
        $this->V3Edmx->getDataServiceType()->getSchema()[0]
219
            ->getEntityContainer()[0]->addToAssociationSet($associationSet);
220
221
        assert($this->V3Edmx->isOK($this->lastError), $this->lastError);
222
        return [$principalNavigationProperty, $dependentNavigationProperty];
223
    }
224
225
    protected function createAssocationFromNavigationProperty(
226
        TEntityTypeType $principalType,
227
        TEntityTypeType $dependentType,
228
        TNavigationPropertyType $principalNavigationProperty,
229
        TNavigationPropertyType $dependentNavigationProperty = null,
230
        $principalMultiplicity,
231
        $dependentMultiplicity,
232
        array $principalConstraintProperty = null,
233
        array $dependentConstraintProperty = null
234
    ) {
235
        $multCombo = [ '*' => ['*', '1'], '0..1' => ['1'], '1' => ['*', '0..1']];
236
        $multKeys = array_keys($multCombo);
237
        if (null != $dependentNavigationProperty) {
238
            if ($dependentNavigationProperty->getRelationship() != $principalNavigationProperty->getRelationship()) {
239
                $msg = "If you have both a dependent property and a principal property,"
240
                       ." relationship should match";
241
                throw new \InvalidArgumentException($msg);
242
            }
243
            if ($dependentNavigationProperty->getFromRole() != $principalNavigationProperty->getToRole() ||
244
                $dependentNavigationProperty->getToRole() != $principalNavigationProperty->getFromRole()
245
            ) {
246
                throw new \InvalidArgumentException(
247
                    "Principal to role should match dependent from role, and vice versa"
248
                );
249
            }
250
        }
251
        if (!in_array($principalMultiplicity, $multKeys) || !in_array($dependentMultiplicity, $multKeys)) {
252
            throw new \InvalidArgumentException("Malformed multiplicity - valid values are *, 0..1 and 1");
253
        }
254
        if (!in_array($dependentMultiplicity, $multCombo[$principalMultiplicity])) {
255
            throw new \InvalidArgumentException(
256
                "Invalid multiplicity combination - ". $principalMultiplicity . ' ' . $dependentMultiplicity
257
            );
258
        }
259
260
        $namespace = $this->getNamespace();
261
        $principalTypeFQName = $namespace . $principalType->getName();
262
        $dependentTypeFQName = $namespace . $dependentType->getName();
263
        $association = new TAssociationType();
264
        $relationship = $principalNavigationProperty->getRelationship();
265
        if (false !== strpos($relationship, '.')) {
266
            $relationship = substr($relationship, strpos($relationship, '.') + 1);
267
        }
268
269
        $principalTargRole = $principalNavigationProperty->getToRole();
270
        $principalSrcRole = $principalNavigationProperty->getFromRole();
271
        $dependentTargRole = null != $dependentNavigationProperty ? $dependentNavigationProperty->getToRole() : null;
272
273
        $association->setName($relationship);
274
        $principalEnd = new TAssociationEndType();
275
        $principalEnd->setType($principalTypeFQName);
276
        $principalEnd->setRole($principalTargRole);
277
        $principalEnd->setMultiplicity($principalMultiplicity);
278
        $association->addToEnd($principalEnd);
279
        $dependentEnd = new TAssociationEndType();
280
        $dependentEnd->setType($dependentTypeFQName);
281
        $dependentEnd->setMultiplicity($dependentMultiplicity);
282
        $association->addToEnd($dependentEnd);
283
284
        $dependentEnd->setRole(null != $dependentNavigationProperty ? $dependentTargRole : $principalSrcRole);
285
286
        $hasPrincipalReferral = null != $principalConstraintProperty && 0 < count($principalConstraintProperty);
287
        $hasDependentReferral = null != $dependentConstraintProperty && 0 < count($dependentConstraintProperty);
288
289
        if ($hasPrincipalReferral && $hasDependentReferral) {
290
            $principalReferralConstraint = $this->makeReferentialConstraint(
291
                $principalConstraintProperty, $principalTargRole
292
            );
293
            $dependentReferralConstraint = $this->makeReferentialConstraint(
294
                $dependentConstraintProperty, $dependentTargRole
295
            );
296
            $constraint = new TConstraintType();
297
            $constraint->setPrincipal($principalReferralConstraint);
298
            $constraint->setDependent($dependentReferralConstraint);
299
            $association->setReferentialConstraint($constraint);
300
        }
301
        return $association;
302
    }
303
304
    protected function createAssocationSetForAssocation(
305
        TAssociationType $association,
306
        $principalEntitySetName,
307
        $dependentEntitySetName
308
    ) {
309
        $as = new AssociationSetAnonymousType();
310
        $name = $association->getName();
311
        $as->setName($name);
312
        $namespace = $this->getNamespace();
313
        $associationSetName = $namespace . $association->getName();
314
        $as->setAssociation($associationSetName);
315
        $end1 = new EndAnonymousType();
316
        $end1->setRole($association->getEnd()[0]->getRole());
317
        $end1->setEntitySet($principalEntitySetName);
318
        $end2 = new EndAnonymousType();
319
        $end2->setRole($association->getEnd()[1]->getRole());
320
        $end2->setEntitySet($dependentEntitySetName);
321
        assert($end1->getRole() != $end2->getRole());
322
        $as->addToEnd($end1);
323
        $as->addToEnd($end2);
324
        return $as;
325
    }
326
327
    public function getLastError()
328
    {
329
        return $this->lastError;
330
    }
331
332
    /**
333
     * @param string $name
334
     * @param IsOK $expectedReturnType
335
     * @param TTextType $shortDesc
336
     * @param TTextType $longDesc
337
     * @return FunctionImportAnonymousType
338
     */
339
    public function createSingleton(
340
        $name,
341
        IsOK $expectedReturnType,
342
        TTextType $shortDesc = null,
343
        TTextType $longDesc = null
344
    ) {
345
        if (!($expectedReturnType instanceof TEntityTypeType) && !($expectedReturnType instanceof TComplexTypeType)) {
346
            $msg = "Expected return type must be either TEntityType or TComplexType";
347
            throw new \InvalidArgumentException($msg);
348
        }
349
350
        if (!is_string($name) || empty($name)) {
351
            $msg = "Name must be a non-empty string";
352
            throw new \InvalidArgumentException($msg);
353
        }
354
355
        $funcType = new FunctionImportAnonymousType();
356
        $funcType->setName($name);
357
358
        $typeName = $expectedReturnType->getName();
359
        $returnType = new TFunctionImportReturnTypeType();
360
        $returnType->setType($typeName);
361
        $returnType->setEntitySetAttribute($typeName);
362
        assert($returnType->isOK($msg), $msg);
363
        $funcType->addToReturnType($returnType);
364
        $this->addDocumentation($shortDesc, $longDesc, $funcType);
365
366
        $this->getEdmx()->getDataServiceType()->getSchema()[0]->getEntityContainer()[0]->addToFunctionImport($funcType);
0 ignored issues
show
Compatibility introduced by
$funcType of type object<AlgoWeb\ODataMetadata\IsOK> is not a sub-type of object<AlgoWeb\ODataMeta...ionImportAnonymousType>. It seems like you assume a child class of the class AlgoWeb\ODataMetadata\IsOK to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
367
368
        return $funcType;
369
    }
370
371
    private function initSerialiser()
372
    {
373
        $ymlDir = __DIR__ . DIRECTORY_SEPARATOR . "MetadataV3" . DIRECTORY_SEPARATOR . "JMSmetadata";
374
        $this->serializer =
375
            SerializerBuilder::create()
376
                ->addMetadataDir($ymlDir)
377
                ->build();
378
    }
379
380
    public function __sleep()
381
    {
382
        $this->serializer = null;
383
        $result = array_keys(get_object_vars($this));
384
        return $result;
385
    }
386
387
    public function __wakeup()
388
    {
389
        $this->initSerialiser();
390
    }
391
392
    /**
393
     * @param $summary
394
     * @param $longDescription
395
     * @return TDocumentationType
396
     */
397
    private function generateDocumentation(TTextType $summary, TTextType $longDescription)
398
    {
399
        $documentation = new TDocumentationType();
400
        $documentation->setSummary($summary);
401
        $documentation->setLongDescription($longDescription);
402
        return $documentation;
403
    }
404
405
    /**
406
     * @return string
407
     */
408
    private function getNamespace()
409
    {
410
        $namespace = $this->V3Edmx->getDataServiceType()->getSchema()[0]->getNamespace();
411
        if (0 == strlen(trim($namespace))) {
412
            $namespace = "";
413
        } else {
414
            $namespace .= ".";
415
        }
416
        return $namespace;
417
    }
418
419
    /**
420
     * @param array $constraintProperty
421
     * @param string $targRole
422
     * @return TReferentialConstraintRoleElementType
423
     */
424
    protected function makeReferentialConstraint(array $constraintProperty, $targRole)
425
    {
426
        assert(!empty($constraintProperty));
427
        assert(is_string($targRole));
428
        $referralConstraint = new TReferentialConstraintRoleElementType();
429
        $referralConstraint->setRole($targRole);
430
        foreach ($constraintProperty as $propertyRef) {
431
            $TpropertyRef = new TPropertyRefType();
432
            $TpropertyRef->setName($propertyRef);
433
            $referralConstraint->addToPropertyRef($TpropertyRef);
434
        }
435
        return $referralConstraint;
436
    }
437
438
    /**
439
     * @param $summary
440
     * @param $longDescription
441
     * @param $NewEntity
442
     */
443
    private function addDocumentation($summary, $longDescription, IsOK &$NewEntity)
444
    {
445
        if (null != $summary && null != $longDescription) {
446
            $documentation = $this->generateDocumentation($summary, $longDescription);
447
            if (method_exists($NewEntity, 'addToDocumentation')) {
448
                $NewEntity->addToDocumentation($documentation);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class AlgoWeb\ODataMetadata\IsOK as the method addToDocumentation() does only exist in the following sub-classes of AlgoWeb\ODataMetadata\IsOK: AlgoWeb\ODataMetadata\Me...\EntitySetAnonymousType, AlgoWeb\ODataMetadata\Me...ComplexTypePropertyType, AlgoWeb\ODataMetadata\Me...edm\TEntityPropertyType. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
449
            } else {
450
                $NewEntity->setDocumentation($documentation);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class AlgoWeb\ODataMetadata\IsOK as the method setDocumentation() does only exist in the following sub-classes of AlgoWeb\ODataMetadata\IsOK: AlgoWeb\ODataMetadata\Me...aV1\edm\EntityContainer, AlgoWeb\ODataMetadata\Me...ciationSetAnonymousType, AlgoWeb\ODataMetadata\Me...usType\EndAnonymousType, AlgoWeb\ODataMetadata\Me...\EntitySetAnonymousType, AlgoWeb\ODataMetadata\Me...tionImportAnonymousType, AlgoWeb\ODataMetadata\Me...edm\TAssociationEndType, AlgoWeb\ODataMetadata\Me...V1\edm\TAssociationType, AlgoWeb\ODataMetadata\Me...ComplexTypePropertyType, AlgoWeb\ODataMetadata\Me...V1\edm\TComplexTypeType, AlgoWeb\ODataMetadata\Me...aV1\edm\TConstraintType, AlgoWeb\ODataMetadata\Me...edm\TEntityPropertyType, AlgoWeb\ODataMetadata\Me...aV1\edm\TEntityTypeType, AlgoWeb\ODataMetadata\Me...tionImportParameterType, AlgoWeb\ODataMetadata\Me...TNavigationPropertyType, AlgoWeb\ODataMetadata\MetadataV1\edm\TOnActionType, AlgoWeb\ODataMetadata\MetadataV1\edm\TUsingType, AlgoWeb\ODataMetadata\Me...dm\ssdl\EntityContainer, AlgoWeb\ODataMetadata\Me...ciationSetAnonymousType, AlgoWeb\ODataMetadata\Me...usType\EndAnonymousType, AlgoWeb\ODataMetadata\Me...\EntitySetAnonymousType, AlgoWeb\ODataMetadata\Me...sdl\TAssociationEndType, AlgoWeb\ODataMetadata\Me...m\ssdl\TAssociationType, AlgoWeb\ODataMetadata\Me...dm\ssdl\TConstraintType, AlgoWeb\ODataMetadata\Me...sdl\TEntityPropertyType, AlgoWeb\ODataMetadata\Me...dm\ssdl\TEntityTypeType, AlgoWeb\ODataMetadata\Me...\edm\ssdl\TFunctionType, AlgoWeb\ODataMetadata\Me...\edm\ssdl\TOnActionType, AlgoWeb\ODataMetadata\Me...edm\ssdl\TParameterType, AlgoWeb\ODataMetadata\Me...m\ssdl\TPropertyRefType, AlgoWeb\ODataMetadata\Me...nstraintRoleElementType, AlgoWeb\ODataMetadata\Me...aV2\edm\EntityContainer, AlgoWeb\ODataMetadata\Me...ciationSetAnonymousType, AlgoWeb\ODataMetadata\Me...usType\EndAnonymousType, AlgoWeb\ODataMetadata\Me...\EntitySetAnonymousType, AlgoWeb\ODataMetadata\Me...tionImportAnonymousType, AlgoWeb\ODataMetadata\Me...edm\TAssociationEndType, AlgoWeb\ODataMetadata\Me...V2\edm\TAssociationType, AlgoWeb\ODataMetadata\Me...ComplexTypePropertyType, AlgoWeb\ODataMetadata\Me...V2\edm\TComplexTypeType, AlgoWeb\ODataMetadata\Me...aV2\edm\TConstraintType, AlgoWeb\ODataMetadata\Me...edm\TEntityPropertyType, AlgoWeb\ODataMetadata\Me...aV2\edm\TEntityTypeType, AlgoWeb\ODataMetadata\Me...tionImportParameterType, AlgoWeb\ODataMetadata\MetadataV2\edm\TFunctionType, AlgoWeb\ODataMetadata\Me...TNavigationPropertyType, AlgoWeb\ODataMetadata\MetadataV2\edm\TOnActionType, AlgoWeb\ODataMetadata\Me...\edm\TReferenceTypeType, AlgoWeb\ODataMetadata\MetadataV2\edm\TTypeRefType, AlgoWeb\ODataMetadata\MetadataV2\edm\TUsingType, AlgoWeb\ODataMetadata\Me...dm\ssdl\EntityContainer, AlgoWeb\ODataMetadata\Me...ciationSetAnonymousType, AlgoWeb\ODataMetadata\Me...usType\EndAnonymousType, AlgoWeb\ODataMetadata\Me...\EntitySetAnonymousType, AlgoWeb\ODataMetadata\Me...sdl\TAssociationEndType, AlgoWeb\ODataMetadata\Me...m\ssdl\TAssociationType, AlgoWeb\ODataMetadata\Me...dm\ssdl\TConstraintType, AlgoWeb\ODataMetadata\Me...sdl\TEntityPropertyType, AlgoWeb\ODataMetadata\Me...dm\ssdl\TEntityTypeType, AlgoWeb\ODataMetadata\Me...\edm\ssdl\TFunctionType, AlgoWeb\ODataMetadata\Me...\edm\ssdl\TOnActionType, AlgoWeb\ODataMetadata\Me...edm\ssdl\TParameterType, AlgoWeb\ODataMetadata\Me...m\ssdl\TPropertyRefType, AlgoWeb\ODataMetadata\Me...nstraintRoleElementType, AlgoWeb\ODataMetadata\Me...aV3\edm\EntityContainer, AlgoWeb\ODataMetadata\Me...ciationSetAnonymousType, AlgoWeb\ODataMetadata\Me...usType\EndAnonymousType, AlgoWeb\ODataMetadata\Me...\EntitySetAnonymousType, AlgoWeb\ODataMetadata\Me...tionImportAnonymousType, AlgoWeb\ODataMetadata\Me...edm\TAssociationEndType, AlgoWeb\ODataMetadata\Me...V3\edm\TAssociationType, AlgoWeb\ODataMetadata\Me...ComplexTypePropertyType, AlgoWeb\ODataMetadata\Me...V3\edm\TComplexTypeType, AlgoWeb\ODataMetadata\Me...aV3\edm\TConstraintType, AlgoWeb\ODataMetadata\Me...edm\TEntityPropertyType, AlgoWeb\ODataMetadata\Me...aV3\edm\TEntityTypeType, AlgoWeb\ODataMetadata\Me...edm\TEnumTypeMemberType, AlgoWeb\ODataMetadata\MetadataV3\edm\TEnumTypeType, AlgoWeb\ODataMetadata\Me...tionImportParameterType, AlgoWeb\ODataMetadata\MetadataV3\edm\TFunctionType, AlgoWeb\ODataMetadata\Me...TNavigationPropertyType, AlgoWeb\ODataMetadata\MetadataV3\edm\TOnActionType, AlgoWeb\ODataMetadata\Me...\edm\TReferenceTypeType, AlgoWeb\ODataMetadata\MetadataV3\edm\TTypeRefType, AlgoWeb\ODataMetadata\MetadataV3\edm\TUsingType, AlgoWeb\ODataMetadata\Me...taV3\edm\TValueTermType, AlgoWeb\ODataMetadata\Me...dm\ssdl\EntityContainer, AlgoWeb\ODataMetadata\Me...ciationSetAnonymousType, AlgoWeb\ODataMetadata\Me...usType\EndAnonymousType, AlgoWeb\ODataMetadata\Me...\EntitySetAnonymousType, AlgoWeb\ODataMetadata\Me...sdl\TAssociationEndType, AlgoWeb\ODataMetadata\Me...m\ssdl\TAssociationType, AlgoWeb\ODataMetadata\Me...dm\ssdl\TConstraintType, AlgoWeb\ODataMetadata\Me...sdl\TEntityPropertyType, AlgoWeb\ODataMetadata\Me...dm\ssdl\TEntityTypeType, AlgoWeb\ODataMetadata\Me...\edm\ssdl\TFunctionType, AlgoWeb\ODataMetadata\Me...\edm\ssdl\TOnActionType, AlgoWeb\ODataMetadata\Me...edm\ssdl\TParameterType, AlgoWeb\ODataMetadata\Me...m\ssdl\TPropertyRefType, AlgoWeb\ODataMetadata\Me...\edm\ssdl\TPropertyType, AlgoWeb\ODataMetadata\Me...nstraintRoleElementType. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
451
            }
452
        }
453
    }
454
}
455