Failed Conditions
Pull Request — master (#6143)
by Luís
10:34
created

DefaultRegionTest::testSharedRegion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 22
rs 9.2
cc 1
eloc 15
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\Tests\ORM\Cache;
4
5
use Doctrine\Common\Cache\ArrayCache;
6
use Doctrine\Common\Cache\Cache;
7
use Doctrine\Common\Cache\CacheProvider;
8
use Doctrine\ORM\Cache\CollectionCacheEntry;
9
use Doctrine\ORM\Cache\Region\DefaultRegion;
10
use Doctrine\Tests\Mocks\CacheEntryMock;
11
use Doctrine\Tests\Mocks\CacheKeyMock;
12
13
/**
14
 * @group DDC-2183
15
 */
16
class DefaultRegionTest extends AbstractRegionTest
17
{
18
    protected function createRegion()
19
    {
20
        return new DefaultRegion('default.region.test', $this->cache);
21
    }
22
23
    public function testGetters()
24
    {
25
        $this->assertEquals('default.region.test', $this->region->getName());
26
        $this->assertSame($this->cache, $this->region->getCache());
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Doctrine\ORM\Cache\Region as the method getCache() does only exist in the following implementations of said interface: Doctrine\ORM\Cache\Region\DefaultMultiGetRegion, Doctrine\ORM\Cache\Region\DefaultRegion, Doctrine\ORM\Cache\Region\UpdateTimestampCache.

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...
27
    }
28
29
    public function testSharedRegion()
30
    {
31
        $cache   = new SharedArrayCache();
32
        $key     = new CacheKeyMock('key');
33
        $entry   = new CacheEntryMock(['value' => 'foo']);
34
        $region1 = new DefaultRegion('region1', $cache->createChild());
35
        $region2 = new DefaultRegion('region2', $cache->createChild());
36
37
        $this->assertFalse($region1->contains($key));
38
        $this->assertFalse($region2->contains($key));
39
40
        $region1->put($key, $entry);
41
        $region2->put($key, $entry);
42
43
        $this->assertTrue($region1->contains($key));
44
        $this->assertTrue($region2->contains($key));
45
46
        $region1->evictAll();
47
48
        $this->assertFalse($region1->contains($key));
49
        $this->assertTrue($region2->contains($key));
50
    }
51
52
    public function testDoesNotModifyCacheNamespace()
53
    {
54
        $cache = new ArrayCache();
55
56
        $cache->setNamespace('foo');
57
58
        new DefaultRegion('bar', $cache);
59
        new DefaultRegion('baz', $cache);
60
61
        $this->assertSame('foo', $cache->getNamespace());
62
    }
63
64
    public function testEvictAllWithGenericCacheThrowsUnsupportedException()
65
    {
66
        /* @var $cache \Doctrine\Common\Cache\Cache */
67
        $cache = $this->createMock(Cache::class);
68
69
        $region = new DefaultRegion('foo', $cache);
70
71
        $this->expectException(\BadMethodCallException::class);
72
73
        $region->evictAll();
74
    }
75
76 View Code Duplication
    public function testGetMulti()
77
    {
78
        $key1 = new CacheKeyMock('key.1');
79
        $value1 = new CacheEntryMock(['id' => 1, 'name' => 'bar']);
80
81
        $key2 = new CacheKeyMock('key.2');
82
        $value2 = new CacheEntryMock(['id' => 2, 'name' => 'bar']);
83
84
        $this->assertFalse($this->region->contains($key1));
85
        $this->assertFalse($this->region->contains($key2));
86
87
        $this->region->put($key1, $value1);
88
        $this->region->put($key2, $value2);
89
90
        $this->assertTrue($this->region->contains($key1));
91
        $this->assertTrue($this->region->contains($key2));
92
93
        $actual = $this->region->getMultiple(new CollectionCacheEntry([$key1, $key2]));
94
95
        $this->assertEquals($value1, $actual[0]);
96
        $this->assertEquals($value2, $actual[1]);
97
    }
98
}
99
100
/**
101
 * Cache provider that offers child cache items (sharing the same array)
102
 *
103
 * Declared as a different class for readability purposes and kept in this file
104
 * to keep its monstrosity contained.
105
 *
106
 * @internal
107
 */
108
final class SharedArrayCache extends ArrayCache
109
{
110
    public function createChild(): Cache
111
    {
112
        return new class ($this) extends CacheProvider
113
        {
114
            /**
115
             * @var ArrayCache
116
             */
117
            private $parent;
118
119
            public function __construct(ArrayCache $parent)
120
            {
121
                $this->parent = $parent;
122
            }
123
124
            protected function doFetch($id)
125
            {
126
                return $this->parent->doFetch($id);
0 ignored issues
show
Bug introduced by
The method doFetch() cannot be called from this context as it is declared protected in class Doctrine\Common\Cache\ArrayCache.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
127
            }
128
129
            protected function doContains($id)
130
            {
131
                return $this->parent->doContains($id);
0 ignored issues
show
Bug introduced by
The method doContains() cannot be called from this context as it is declared protected in class Doctrine\Common\Cache\ArrayCache.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
132
            }
133
134
            protected function doSave($id, $data, $lifeTime = 0)
135
            {
136
                return $this->parent->doSave($id, $data, $lifeTime);
0 ignored issues
show
Bug introduced by
The method doSave() cannot be called from this context as it is declared protected in class Doctrine\Common\Cache\ArrayCache.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
137
            }
138
139
            protected function doDelete($id)
140
            {
141
                return $this->parent->doDelete($id);
0 ignored issues
show
Bug introduced by
The method doDelete() cannot be called from this context as it is declared protected in class Doctrine\Common\Cache\ArrayCache.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
142
            }
143
144
            protected function doFlush()
145
            {
146
                return $this->parent->doFlush();
0 ignored issues
show
Bug introduced by
The method doFlush() cannot be called from this context as it is declared protected in class Doctrine\Common\Cache\ArrayCache.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
147
            }
148
149
            protected function doGetStats()
150
            {
151
                return $this->parent->doGetStats();
0 ignored issues
show
Bug introduced by
The method doGetStats() cannot be called from this context as it is declared protected in class Doctrine\Common\Cache\ArrayCache.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
152
            }
153
        };
154
    }
155
}
156