AssetManager::setAssetCacheManager()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace AssetManager\Core\Service;
4
5
use Assetic\Asset\AssetInterface;
6
use AssetManager\Core\Exception;
7
use AssetManager\Core\Resolver\ResolverInterface;
8
use Psr\Http\Message\ResponseInterface;
9
use Psr\Http\Message\ServerRequestInterface;
10
11
/**
12
 * @category    AssetManager
13
 * @package     AssetManager
14
 */
15
class AssetManager implements
16
    AssetFilterManagerAwareInterface,
17
    AssetCacheManagerAwareInterface
18
{
19
    /**
20
     * @var ResolverInterface
21
     */
22
    protected $resolver;
23
24
    /**
25
     * @var AssetFilterManager The AssetFilterManager service.
26
     */
27
    protected $filterManager;
28
29
    /**
30
     * @var AssetCacheManager The AssetCacheManager service.
31
     */
32
    protected $cacheManager;
33
34
    /**
35
     * @var AssetInterface The asset
36
     */
37
    protected $asset;
38
39
    /**
40
     * @var string The requested path
41
     */
42
    protected $path;
43
44
    /**
45
     * @var array The asset_manager configuration
46
     */
47
    protected $config;
48
49
    /**
50
     * @var bool Whether this instance has at least one asset successfully set on response
51
     */
52
    protected $assetSetOnResponse = false;
53
54
    /**
55
     * Constructor
56
     *
57
     * @param ResolverInterface $resolver
58
     * @param array             $config
59
     */
60
    public function __construct($resolver, $config = array())
61
    {
62
        $this->setResolver($resolver);
63
        $this->setConfig($config);
64
    }
65
66
    /**
67
     * Set the config
68
     *
69
     * @param array $config
70
     */
71
    protected function setConfig(array $config)
72
    {
73
        $this->config = $config;
74
    }
75
76
    /**
77
     * Check if the request resolves to an asset.
78
     *
79
     * @param    ServerRequestInterface $request
80
     * @return   boolean
81
     */
82
    public function resolvesToAsset(ServerRequestInterface $request)
83
    {
84
        if (null === $this->asset) {
85
            $this->asset = $this->resolve($request);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->resolve($request) can also be of type false. However, the property $asset is declared as type object<Assetic\Asset\AssetInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
86
        }
87
88
        return (bool)$this->asset;
89
    }
90
91
    /**
92
     * Returns true if this instance of asset manager has at least one asset successfully set on response
93
     *
94
     * @return bool
95
     */
96
    public function assetSetOnResponse()
97
    {
98
        return $this->assetSetOnResponse;
99
    }
100
101
    /**
102
     * Set the resolver to use in the asset manager
103
     *
104
     * @param ResolverInterface $resolver
105
     */
106
    public function setResolver(ResolverInterface $resolver)
107
    {
108
        $this->resolver = $resolver;
109
    }
110
111
    /**
112
     * Get the resolver used by the asset manager
113
     *
114
     * @return ResolverInterface
115
     */
116
    public function getResolver()
117
    {
118
        return $this->resolver;
119
    }
120
121
    /**
122
     * Set the asset on the response, including headers and content.
123
     *
124
     * @param    ResponseInterface $response
125
     * @return   ResponseInterface
126
     * @throws   Exception\RuntimeException
127
     */
128
    public function setAssetOnResponse(ResponseInterface $response)
129
    {
130
        if (!$this->asset instanceof AssetInterface) {
131
            throw new Exception\RuntimeException(
132
                'Unable to set asset on response. Request has not been resolved to an asset.'
133
            );
134
        }
135
136
        // @todo: Create Asset wrapper for mimetypes
137
        if (empty($this->asset->mimetype)) {
0 ignored issues
show
Bug introduced by
Accessing mimetype on the interface Assetic\Asset\AssetInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
138
            throw new Exception\RuntimeException('Expected property "mimetype" on asset.');
139
        }
140
141
        $this->getAssetFilterManager()->setFilters($this->path, $this->asset);
142
143
        $this->asset    = $this->getAssetCacheManager()->setCache($this->path, $this->asset);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getAssetCacheMana...is->path, $this->asset) can also be of type object<Assetic\Cache\CacheInterface>. However, the property $asset is declared as type object<Assetic\Asset\AssetInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
144
        $mimeType       = $this->asset->mimetype;
145
        $assetContents  = $this->asset->dump();
0 ignored issues
show
Bug introduced by
The method dump does only exist in Assetic\Asset\AssetInterface, but not in Assetic\Cache\CacheInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
146
147
        // @codeCoverageIgnoreStart
148
        if (function_exists('mb_strlen')) {
149
            $contentLength = mb_strlen($assetContents, '8bit');
150
        } else {
151
            $contentLength = strlen($assetContents);
152
        }
153
        // @codeCoverageIgnoreEnd
154
155
        if (!empty($this->config['clear_output_buffer']) && $this->config['clear_output_buffer']) {
156
            // Only clean the output buffer if it's turned on and something
157
            // has been buffered.
158
            if (ob_get_length() > 0) {
159
                ob_clean();
160
            }
161
        }
162
163
        /* Get Last Modified */
164
        $lastModified   = new \DateTime();
165
        $lastModified->setTimestamp($this->asset->getLastModified());
0 ignored issues
show
Bug introduced by
The method getLastModified does only exist in Assetic\Asset\AssetInterface, but not in Assetic\Cache\CacheInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
166
        $lastModified->setTimezone(new \DateTimeZone('UTC'));
167
168
        /* Build up the response */
169
        $withEncoding      = $response->withAddedHeader('Content-Transfer-Encoding', 'binary');
170
        $withContentType   = $withEncoding->withAddedHeader('Content-Type', $mimeType);
171
        $withContentLength = $withContentType->withAddedHeader('Content-Length', "$contentLength");
172
        $withLastModified  = $withContentLength->withAddedHeader(
173
            'Last-Modified',
174
            $lastModified->format('D, d M Y H:i:s \G\M\T')
175
        );
176
177
        $final = clone $withLastModified;
178
        $final->getBody()->write($assetContents);
179
180
        $this->assetSetOnResponse = true;
181
182
        return $final;
183
    }
184
185
    /**
186
     * Resolve the request to a file.
187
     *
188
     * @param ServerRequestInterface $request
189
     *
190
     * @return mixed false when not found, AssetInterface when resolved.
191
     */
192
    protected function resolve(ServerRequestInterface $request)
193
    {
194
        $uri        = $request->getUri();
195
        $path       = $this->normalizePath($uri->getPath());
196
        $this->path = $path;
197
        $asset      = $this->getResolver()->resolve($path);
198
199
        if (!$asset instanceof AssetInterface) {
200
            return false;
201
        }
202
203
        return $asset;
204
    }
205
206
    /**
207
     * Normalize the path
208
     *
209
     * @param $path
210
     *
211
     * @return string
212
     */
213
    protected function normalizePath($path)
214
    {
215
        return ltrim(rawurldecode($path), '/');
216
    }
217
218
    /**
219
     * Set the AssetFilterManager.
220
     *
221
     * @param AssetFilterManager $filterManager
222
     */
223
    public function setAssetFilterManager(AssetFilterManager $filterManager)
224
    {
225
        $this->filterManager = $filterManager;
226
    }
227
228
    /**
229
     * Get the AssetFilterManager
230
     *
231
     * @return AssetFilterManager
232
     */
233
    public function getAssetFilterManager()
234
    {
235
        return $this->filterManager;
236
    }
237
238
    /**
239
     * Set the AssetCacheManager.
240
     *
241
     * @param AssetCacheManager $cacheManager
242
     */
243
    public function setAssetCacheManager(AssetCacheManager $cacheManager)
244
    {
245
        $this->cacheManager = $cacheManager;
246
    }
247
248
    /**
249
     * Get the AssetCacheManager
250
     *
251
     * @return AssetCacheManager
252
     */
253
    public function getAssetCacheManager()
254
    {
255
        return $this->cacheManager;
256
    }
257
}
258