Completed
Push — master ( 53453c...e9b885 )
by Christopher
04:11
created

ValueObjectCollection::jsonSerialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 7
ccs 0
cts 7
cp 0
rs 9.4285
cc 1
eloc 4
nc 1
nop 0
crap 2
1
<?php namespace C4tech\RayEmitter\Domain;
2
3
use C4tech\RayEmitter\Contracts\Domain\ValueObject;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, C4tech\RayEmitter\Domain\ValueObject.

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...
4
use Illuminate\Support\Collection;
5
6
abstract class ValueObjectCollection extends Collection implements ValueObject
7
{
8
    /**
9
     * Collection Class
10
     *
11
     * Class name of member Value Objects
12
     * @var string
13
     */
14
    protected $collectionClass = '';
15
16
    /**
17
     * @inheritDoc
18
     */
19
    public function equals(ValueObject $other)
20
    {
21
        if (get_class($other) !== get_class($this)) {
22
            return false;
23
        }
24
25
        if ($other->getHash() === $this->getHash()) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface C4tech\RayEmitter\Contracts\Domain\ValueObject as the method getHash() does only exist in the following implementations of said interface: C4tech\RayEmitter\Domain\ValueObjectCollection.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
26
            return true;
27
        }
28
29
        return false;
30
    }
31
32
    /**
33
     * Get Hash
34
     *
35
     * Calculate an md5 sum of the collection.
36
     * @return string
37
     */
38
    public function getHash()
39
    {
40
        return hash('md5', json_encode($this));
41
    }
42
43
    /**
44
     * @inheritDoc
45
     */
46
    public function getValue()
47
    {
48
        return $this->all();
49
    }
50
51
    /**
52
     * @inheritDoc
53
     */
54
    public static function jsonSerialize()
55
    {
56
        return [
57
            'class' => $this->collectionClass,
0 ignored issues
show
Bug introduced by
The variable $this does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
58
            'content' => parent::jsonSerialize()
59
        ];
60
    }
61
62
    /**
63
     * @inheritDoc
64
     */
65
    public static function jsonUnserialize($data)
66
    {
67
        $class = $data['class'];
68
69
        $values = array_map(
70
            function ($value) use ($class) {
71
                return $class::jsonUnserialize($value);
72
            },
73
            $data['content']
74
        );
75
76
        return new static($values);
77
    }
78
79
    /**
80
     * Sort By Value
81
     *
82
     * Sort the array by the VO's value.
83
     * @return static
84
     */
85
    public function sortByValue()
86
    {
87
        return $this->sortBy(function ($item) {
88
            return $item->getValue();
89
        });
90
    }
91
}
92