DbReferenceMapper   A
last analyzed

Complexity

Total Complexity 10

Size/Duplication

Total Lines 90
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 75%

Importance

Changes 0
Metric Value
dl 0
loc 90
c 0
b 0
f 0
wmc 10
lcom 1
cbo 6
ccs 18
cts 24
cp 0.75
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getOptions() 0 4 1
A slumber() 0 28 4
A awake() 0 15 3
A getReferencedClass() 0 4 1
1
<?php
2
/**
3
 * File was created 10.05.2016 17:13
4
 */
5
6
namespace PeekAndPoke\Component\Slumber\Data\MongoDb\Types;
7
8
use PeekAndPoke\Component\Slumber\Annotation\Slumber\Store\AsDbReference;
9
use PeekAndPoke\Component\Slumber\Core\Codec\Awaker;
10
use PeekAndPoke\Component\Slumber\Core\Codec\Property\AbstractPropertyMapper;
11
use PeekAndPoke\Component\Slumber\Core\Codec\Slumberer;
12
use PeekAndPoke\Component\Slumber\Core\Exception\SlumberException;
13
use PeekAndPoke\Component\Slumber\Data\LazyDbReference;
14
use PeekAndPoke\Component\Slumber\Data\MongoDb\MongoDbAwaker;
15
use PeekAndPoke\Component\Slumber\Data\MongoDb\MongoDbSlumberer;
16
use PeekAndPoke\Component\Slumber\Data\MongoDb\MongoDbUtil;
17
18
/**
19
 * @author Karsten J. Gerber <[email protected]>
20
 */
21
class DbReferenceMapper extends AbstractPropertyMapper
22
{
23
    /** @var AsDbReference */
24
    private $options;
25
26
    /**
27
     * DbReferenceMapper constructor.
28
     *
29
     * @param AsDbReference $options
30
     */
31
    public function __construct(AsDbReference $options)
32
    {
33
        $this->options = $options;
34
    }
35
36
    /**
37
     * @return AsDbReference
38
     */
39 17
    public function getOptions()
40
    {
41 17
        return $this->options;
42
    }
43
44
    /**
45
     * @param MongoDbSlumberer|Slumberer $slumberer
46
     * @param mixed                      $value
47
     *
48
     * @return mixed
49
     * @throws SlumberException
50
     */
51 23
    public function slumber(Slumberer $slumberer, $value)
52
    {
53
        /**
54
         * unwrap any LazyDbReference
55
         * @see LazyDbReference
56
         */
57 23
        if ($value instanceof LazyDbReference) {
58
            // take the raw id WITHOUT reloading the object
59 1
            $id = $value->getReferencedId();
60
61
        } else {
62
63 23
            if (! \is_object($value)) {
64 23
                return null;
65
            }
66
67
            // TODO: we need a better way to ensure that getId() is available on the object
68
            // -> ask the repo and use the idMarker for this
69 17
            if (! method_exists($value, 'getId')) {
70
                throw new SlumberException('The referenced entity ' . \get_class($value) . ' must have a method called getId()');
71
            }
72
73
            // we do nothing. we only want the id of the referenced object
74 17
            $id = $value->getId();
75
        }
76
77 17
        return MongoDbUtil::ensureMongoId($id);
78
    }
79
80
    /**
81
     * @param MongoDbAwaker|Awaker $awaker
82
     * @param mixed                $value
83
     *
84
     * @return mixed
85
     * @throws SlumberException
86
     */
87 17
    public function awake(Awaker $awaker, $value)
88
    {
89 17
        if ($value === null) {
90
            return null;
91
        }
92
93 17
        $clz  = $this->getReferencedClass();
94 17
        $repo = $awaker->getStorage()->getRepositoryByClassName($clz);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PeekAndPoke\Component\Slumber\Core\Codec\Awaker as the method getStorage() does only exist in the following implementations of said interface: PeekAndPoke\Component\Sl...ngoDb\MongoDbAwakerImpl, PeekAndPoke\Component\Sl...Db\MongoDbPoolingAwaker.

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...
95
96 17
        if ($repo !== null) {
97 17
            return LazyDbReference::create($repo, $value);
98
        }
99
100
        return null;
101
    }
102
103
    /**
104
     * @return string
105
     */
106 17
    private function getReferencedClass()
107
    {
108 17
        return $this->options->getObjectOptions()->value;
109
    }
110
}
111