Completed
Push — master ( d63f8e...49a1eb )
by Christian
02:29
created

Object::getActorModel()   C

Complexity

Conditions 8
Paths 15

Size

Total Lines 26
Code Lines 17

Duplication

Lines 26
Ratio 100 %

Importance

Changes 0
Metric Value
dl 26
loc 26
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 17
nc 15
nop 0
1
<?php
2
3
/*
4
 * This file is part of the xAPI package.
5
 *
6
 * (c) Christian Flothmann <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace XApi\Repository\Doctrine\Mapping;
13
14
use Xabbuh\XApi\Model\Account;
15
use Xabbuh\XApi\Model\Activity;
16
use Xabbuh\XApi\Model\Actor;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, XApi\Repository\Doctrine\Mapping\Actor.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
17
use Xabbuh\XApi\Model\Agent;
18
use Xabbuh\XApi\Model\Definition;
19
use Xabbuh\XApi\Model\Group;
20
use Xabbuh\XApi\Model\InverseFunctionalIdentifier;
21
use Xabbuh\XApi\Model\IRI;
22
use Xabbuh\XApi\Model\IRL;
23
use Xabbuh\XApi\Model\LanguageMap;
24
use Xabbuh\XApi\Model\Object as ObjectModel;
25
use Xabbuh\XApi\Model\StatementId;
26
use Xabbuh\XApi\Model\StatementReference;
27
use Xabbuh\XApi\Model\SubStatement;
28
29
/**
30
 * @author Christian Flothmann <[email protected]>
31
 */
32
class Object
33
{
34
    const TYPE_ACTIVITY = 'activity';
35
    const TYPE_AGENT = 'agent';
36
    const TYPE_GROUP = 'group';
37
    const TYPE_STATEMENT_REFERENCE = 'statement_reference';
38
    const TYPE_SUB_STATEMENT = 'sub_statement';
39
40
    public $identifier;
41
42
    /**
43
     * @var string
44
     */
45
    public $type;
46
47
    /**
48
     * @var string|null
49
     */
50
    public $activityId;
51
52
    /**
53
     * @var bool|null
54
     */
55
    public $hasActivityDefinition;
56
57
    /**
58
     * @var bool|null
59
     */
60
    public $hasActivityName;
61
62
    /**
63
     * @var array|null
64
     */
65
    public $activityName;
66
67
    /**
68
     * @var bool|null
69
     */
70
    public $hasActivityDescription;
71
72
    /**
73
     * @var array|null
74
     */
75
    public $activityDescription;
76
77
    /**
78
     * @var string|null
79
     */
80
    public $activityType;
81
82
    /**
83
     * @var string|null
84
     */
85
    public $activityMoreInfo;
86
87
    /**
88
     * @var Extensions|null
89
     */
90
    public $activityExtensions;
91
92
    /**
93
     * @var string|null
94
     */
95
    public $mbox;
96
97
    /**
98
     * @var string|null
99
     */
100
    public $mboxSha1Sum;
101
102
    /**
103
     * @var string|null
104
     */
105
    public $openId;
106
107
    /**
108
     * @var string|null
109
     */
110
    public $accountName;
111
112
    /**
113
     * @var string|null
114
     */
115
    public $accountHomePage;
116
117
    /**
118
     * @var string|null
119
     */
120
    public $name;
121
122
    /**
123
     * @var Object[]|null
124
     */
125
    public $members;
126
127
    /**
128
     * @var Object|null
129
     */
130
    public $group;
131
132
    /**
133
     * @var string|null
134
     */
135
    public $referencedStatementId;
136
137
    /**
138
     * @var Object|null
139
     */
140
    public $actor;
141
142
    /**
143
     * @var Verb|null
144
     */
145
    public $verb;
146
147
    /**
148
     * @var Object|null
149
     */
150
    public $object;
151
152
    /**
153
     * @var Result|null
154
     */
155
    public $result;
156
157
    /**
158
     * @var Context|null
159
     */
160
    public $context;
161
162
    /**
163
     * @var Statement|null
164
     */
165
    public $parentContext;
166
167
    /**
168
     * @var Statement|null
169
     */
170
    public $groupingContext;
171
172
    /**
173
     * @var Statement|null
174
     */
175
    public $categoryContext;
176
177
    /**
178
     * @var Statement|null
179
     */
180
    public $otherContext;
181
182
    public static function fromModel(ObjectModel $model)
183
    {
184
        if ($model instanceof Actor) {
185
            return self::fromActor($model);
186
        }
187
188
        if ($model instanceof StatementReference) {
189
            $object = new self();
190
            $object->type = self::TYPE_STATEMENT_REFERENCE;
191
            $object->referencedStatementId = $model->getStatementId()->getValue();
192
193
            return $object;
194
        }
195
196
        if ($model instanceof SubStatement) {
197
            return self::fromSubStatement($model);
198
        }
199
200
        return self::fromActivity($model);
0 ignored issues
show
Compatibility introduced by
$model of type object<Xabbuh\XApi\Model\Object> is not a sub-type of object<Xabbuh\XApi\Model\Activity>. It seems like you assume a child class of the class Xabbuh\XApi\Model\Object 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...
201
    }
202
203
    public function getModel()
204
    {
205
        if (self::TYPE_AGENT === $this->type || self::TYPE_GROUP === $this->type) {
206
            return $this->getActorModel();
207
        }
208
209
        if (self::TYPE_STATEMENT_REFERENCE === $this->type) {
210
            return new StatementReference(StatementId::fromString($this->referencedStatementId));
211
        }
212
213
        if (self::TYPE_SUB_STATEMENT === $this->type) {
214
            return $this->getSubStatementModel();
215
        }
216
217
        return $this->getActivityModel();
218
    }
219
220
    private static function fromActivity(Activity $model)
221
    {
222
        $object = new self();
223
        $object->activityId = $model->getId()->getValue();
224
225
        if (null !== $definition = $model->getDefinition()) {
226
            $object->hasActivityDefinition = true;
227
228 View Code Duplication
            if (null !== $name = $definition->getName()) {
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...
229
                $object->hasActivityName = true;
230
                $object->activityName = array();
231
232
                foreach ($name->languageTags() as $languageTag) {
233
                    $object->activityName[$languageTag] = $name[$languageTag];
234
                }
235
            } else {
236
                $object->hasActivityName = false;
237
            }
238
239 View Code Duplication
            if (null !== $description = $definition->getDescription()) {
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...
240
                $object->hasActivityDescription = true;
241
                $object->activityDescription = array();
242
243
                foreach ($description->languageTags() as $languageTag) {
244
                    $object->activityDescription[$languageTag] = $description[$languageTag];
245
                }
246
            } else {
247
                $object->hasActivityDescription = false;
248
            }
249
250
            if (null !== $type = $definition->getType()) {
251
                $object->activityType = $type->getValue();
252
            }
253
254
            if (null !== $moreInfo = $definition->getMoreInfo()) {
255
                $object->activityMoreInfo = $moreInfo->getValue();
256
            }
257
258
            if (null !== $extensions = $definition->getExtensions()) {
259
                $object->activityExtensions = Extensions::fromModel($extensions);
260
            }
261
        } else {
262
            $object->hasActivityDefinition = false;
263
        }
264
265
        return $object;
266
    }
267
268
    private static function fromActor(Actor $model)
269
    {
270
        $inverseFunctionalIdentifier = $model->getInverseFunctionalIdentifier();
271
272
        $object = new self();
273
        $object->mboxSha1Sum = $inverseFunctionalIdentifier->getMboxSha1Sum();
274
        $object->openId = $inverseFunctionalIdentifier->getOpenId();
275
276
        if (null !== $mbox = $inverseFunctionalIdentifier->getMbox()) {
277
            $object->mbox = $mbox->getValue();
278
        }
279
280 View Code Duplication
        if (null !== $account = $inverseFunctionalIdentifier->getAccount()) {
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...
281
            $object->accountName = $account->getName();
282
            $object->accountHomePage = $account->getHomePage()->getValue();
283
        }
284
285
        if ($model instanceof Group) {
286
            $object->type = self::TYPE_GROUP;
287
            $object->members = array();
288
289
            foreach ($model->getMembers() as $agent) {
290
                $object->members[] = self::fromActor($agent);
291
            }
292
        } else {
293
            $object->type = self::TYPE_AGENT;
294
        }
295
296
        return $object;
297
    }
298
299
    private static function fromSubStatement(SubStatement $model)
300
    {
301
        $object = new self();
302
        $object->type = self::TYPE_SUB_STATEMENT;
303
        $object->actor = Object::fromModel($model->getActor());
304
        $object->verb = Verb::fromModel($model->getVerb());
305
        $object->object = Object::fromModel($model->getObject());
306
307
        return $object;
308
    }
309
310
    private function getActivityModel()
311
    {
312
        $definition = null;
313
        $type = null;
314
        $moreInfo = null;
315
316
        if ($this->hasActivityDefinition) {
317
            $name = null;
318
            $description = null;
319
            $extensions = null;
320
321
            if ($this->hasActivityName) {
322
                $name = LanguageMap::create($this->activityName);
0 ignored issues
show
Bug introduced by
It seems like $this->activityName can also be of type null; however, Xabbuh\XApi\Model\LanguageMap::create() does only seem to accept array, 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...
323
            }
324
325
            if ($this->hasActivityDescription) {
326
                $description = LanguageMap::create($this->activityDescription);
0 ignored issues
show
Bug introduced by
It seems like $this->activityDescription can also be of type null; however, Xabbuh\XApi\Model\LanguageMap::create() does only seem to accept array, 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...
327
            }
328
329
            if (null !== $this->activityType) {
330
                $type = IRI::fromString($this->activityType);
331
            }
332
333
            if (null !== $this->activityMoreInfo) {
334
                $moreInfo = IRL::fromString($this->activityMoreInfo);
335
            }
336
337
            if (null !== $this->activityExtensions) {
338
                $extensions = $this->activityExtensions->getModel();
339
            }
340
341
            $definition = new Definition($name, $description, $type, $moreInfo, $extensions);
342
        }
343
344
        return new Activity(IRI::fromString($this->activityId), $definition);
345
    }
346
347 View Code Duplication
    private function getActorModel()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
348
    {
349
        $inverseFunctionalIdentifier = null;
350
351
        if (null !== $this->mbox) {
352
            $inverseFunctionalIdentifier = InverseFunctionalIdentifier::withMbox(IRI::fromString($this->mbox));
353
        } elseif (null !== $this->mboxSha1Sum) {
354
            $inverseFunctionalIdentifier = InverseFunctionalIdentifier::withMboxSha1Sum($this->mboxSha1Sum);
355
        } elseif (null !== $this->openId) {
356
            $inverseFunctionalIdentifier = InverseFunctionalIdentifier::withOpenId($this->openId);
357
        } elseif (null !== $this->accountName && null !== $this->accountHomePage) {
358
            $inverseFunctionalIdentifier = InverseFunctionalIdentifier::withAccount(new Account($this->accountName, IRL::fromString($this->accountHomePage)));
359
        }
360
361
        if (self::TYPE_GROUP === $this->type) {
362
            $members = array();
363
364
            foreach ($this->members as $agent) {
0 ignored issues
show
Bug introduced by
The expression $this->members of type array<integer,object>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
365
                $members[] = $agent->getModel();
366
            }
367
368
            return new Group($inverseFunctionalIdentifier, $this->name, $members);
369
        } else {
370
            return new Agent($inverseFunctionalIdentifier, $this->name);
0 ignored issues
show
Bug introduced by
It seems like $inverseFunctionalIdentifier defined by null on line 349 can be null; however, Xabbuh\XApi\Model\Agent::__construct() does not accept null, maybe add an additional type check?

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

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

function doesNotAcceptNull(stdClass $x) { }

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

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

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
371
        }
372
    }
373
374
    private function getSubStatementModel()
375
    {
376
        $result = null;
377
        $context = null;
378
379
        return new SubStatement(
380
            $this->actor->getModel(),
381
            $this->verb->getModel(),
382
            $this->object->getModel(),
383
            $result,
384
            $context
385
        );
386
    }
387
}
388