Completed
Pull Request — master (#3)
by
unknown
02:28
created

AbstractHandler::setCatalog()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 15

Duplication

Lines 25
Ratio 100 %

Importance

Changes 0
Metric Value
dl 25
loc 25
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 15
nc 3
nop 1
1
<?php
2
3
namespace Charcoal\App\Handler;
4
5
// Dependencies from PSR-3 (logger)
6
use Psr\Log\LoggerAwareInterface;
7
use Psr\Log\LoggerAwareTrait;
8
9
// Dependencies from PSR-7 (HTTP Messaging)
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Psr\Http\Message\UriInterface;
13
14
// Dependency from Pimple
15
use Pimple\Container;
16
17
// Dependency from 'charcoal-config'
18
use Charcoal\Config\ConfigurableInterface;
19
use Charcoal\Config\ConfigurableTrait;
20
21
// Dependencies from 'charcoal-view'
22
use Charcoal\View\ViewInterface;
23
use Charcoal\View\ViewableInterface;
24
use Charcoal\View\ViewableTrait;
25
26
// Dependencies from 'charcoal-translator'
27
use Charcoal\Translator\TranslatorAwareTrait;
28
29
// Intra-module (`charcoal-app`) dependencies
30
use Charcoal\App\AppConfig;
31
use Charcoal\App\Template\TemplateInterface;
32
use Charcoal\App\Handler\HandlerInterface;
33
use Charcoal\App\Handler\HandlerConfig;
34
35
/**
36
 * Base Error Handler
37
 *
38
 * Enhanced version Slim's error handlers.
39
 *
40
 * It outputs messages in either JSON, XML or HTML
41
 * based on the Accept header.
42
 */
43
abstract class AbstractHandler implements
44
    ConfigurableInterface,
45
    HandlerInterface,
46
    LoggerAwareInterface,
47
    ViewableInterface
48
{
49
    use ConfigurableTrait;
50
    use LoggerAwareTrait;
51
    use TranslatorAwareTrait;
52
    use ViewableTrait;
53
54
    /**
55
     * Container
56
     *
57
     * @var Container
58
     */
59
    protected $container;
60
61
    /**
62
     * URL for the home page
63
     *
64
     * @var string
65
     */
66
    protected $baseUrl;
67
68
    /**
69
     * Known handled content types
70
     *
71
     * @var array
72
     */
73
    protected $knownContentTypes = [
74
        'application/json',
75
        'application/xml',
76
        'text/xml',
77
        'text/html',
78
    ];
79
80
    /**
81
     * Return a new AbstractHandler object.
82
     *
83
     * @param Container $container A dependencies container instance.
84
     */
85
    public function __construct(Container $container)
86
    {
87
        $this->setDependencies($container);
88
    }
89
90
    /**
91
     * Initialize the AbstractHandler object.
92
     *
93
     * @return AbstractHandler Chainable
94
     */
95
    public function init()
96
    {
97
        return $this;
98
    }
99
100
    /**
101
     * Inject dependencies from a Pimple Container.
102
     *
103
     * ## Dependencies
104
     *
105
     * - `AppConfig $appConfig` — The application's configuration.
106
     * - `UriInterface $baseUri` — A base URI.
107
     * - `ViewInterface $view` — A view instance.
108
     *
109
     * @param  Container $container A dependencies container instance.
110
     * @return AbstractHandler Chainable
111
     */
112
    public function setDependencies(Container $container)
113
    {
114
        $this->setLogger($container['logger']);
115
        $this->setContainer($container);
116
        $this->setBaseUrl($container['base-url']);
117
        $this->setView($container['view']);
118
        $this->setTranslator($container['translator']);
119
120
        if (isset($container['config']['handlers.defaults'])) {
121
            $this->setConfig($container['config']['handlers.defaults']);
122
        }
123
124
        return $this;
125
    }
126
127
    /**
128
     * Set container for use with the template controller
129
     *
130
     * @param  Container $container A dependencies container instance.
131
     * @return AbstractHandler Chainable
132
     */
133
    public function setContainer(Container $container)
134
    {
135
        $this->container = $container;
136
        return $this;
137
    }
138
139
    /**
140
     * ConfigurableTrait > createConfig()
141
     *
142
     * @see    ConfigurableTrait::createConfig()
143
     * @param  mixed|null $data Optional config data.
144
     * @return ConfigInterface
145
     */
146
    public function createConfig($data = null)
147
    {
148
        return new HandlerConfig($data);
149
    }
150
151
    /**
152
     * Determine which content type we know about is wanted using "Accept" header
153
     *
154
     * @param  ServerRequestInterface $request The most recent Request object.
155
     * @return string
156
     */
157
    protected function determineContentType(ServerRequestInterface $request)
158
    {
159
        $acceptHeader = $request->getHeaderLine('Accept');
160
        $selectedContentTypes = array_intersect(explode(',', $acceptHeader), $this->knownContentTypes);
161
162
        if (count($selectedContentTypes)) {
163
            return reset($selectedContentTypes);
164
        }
165
166
        // handle +json and +xml specially
167
        if (preg_match('/\+(json|xml)/', $acceptHeader, $matches)) {
168
            $mediaType = 'application/'.$matches[1];
169
            if (in_array($mediaType, $this->knownContentTypes)) {
170
                return $mediaType;
171
            }
172
        }
173
174
        return 'text/html';
175
    }
176
177
    /**
178
     * Render HTML Error Page
179
     *
180
     * @return string
181
     */
182
    protected function renderHtmlOutput()
183
    {
184
        $config    = $this->config();
185
        $container = $this->container;
186
187
        $templateIdent = $config['template'];
188
189
        if ($config['cache']) {
190
            $cachePool = $container['cache'];
191
            $cacheKey  = str_replace('/', '.', 'template/'.$templateIdent);
192
            $cacheItem = $cachePool->getItem($cacheKey);
193
194
            $output = $cacheItem->get();
195
            if ($cacheItem->isMiss()) {
196
                $output = $this->renderHtmlTemplate();
197
198
                $cacheItem->set($output, $config['cache_ttl']);
199
            }
200
        } else {
201
            $output = $this->renderHtmlTemplate();
202
        }
203
204
        return $output;
205
    }
206
207
    /**
208
     * Render title of error
209
     *
210
     * @return string
211
     */
212
    public function messageTitle()
213
    {
214
        return $this->translator()->translate('Application Error');
215
    }
216
217
    /**
218
     * Render body of HTML error
219
     *
220
     * @return string
221
     */
222
    abstract public function renderHtmlMessage();
223
224
    /**
225
     * Render HTML Error Page
226
     *
227
     * @return string
228
     */
229
    protected function renderHtmlTemplate()
230
    {
231
        $config    = $this->config();
232
        $container = $this->container;
233
234
        $templateIdent      = $config['template'];
235
        $templateController = $config['controller'];
236
        $templateData       = $config['template_data'];
237
238
        $templateFactory = $container['template/factory'];
239
        if (isset($config['default_controller'])) {
240
            $templateFactory->setDefaultClass($config['default_controller']);
241
        }
242
243
        if (!$templateController) {
244
            return '';
245
        }
246
247
        $template = $templateFactory->create($templateController);
248
249
        if (!isset($templateData['error_title'])) {
250
            $templateData['error_title'] = $this->messageTitle();
251
        }
252
253
        if (!isset($templateData['error_message'])) {
254
            $templateData['error_message'] = $this->renderHtmlMessage();
255
        }
256
257
        $template->setData($templateData);
258
        return $container['view']->render($templateIdent, $template);
259
    }
260
261
    /**
262
     * Set the base URL (home page).
263
     *
264
     * @param  string|UriInterface $url A URL to the base URL.
265
     * @return AbstractHandler Chainable
266
     */
267
    public function setBaseUrl($url)
268
    {
269
        if ($url instanceof UriInterface) {
270
            $url = $url->withPath('')->withQuery('')->withFragment('');
271
        }
272
273
        $this->baseUrl = $url;
0 ignored issues
show
Documentation Bug introduced by
It seems like $url can also be of type object<Psr\Http\Message\UriInterface>. However, the property $baseUrl is declared as type string. 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...
274
275
        return $this;
276
    }
277
278
    /**
279
     * Retrieves the URL for the home page.
280
     *
281
     * @return string A URL representing the home page.
282
     */
283
    public function baseUrl()
284
    {
285
        return $this->baseUrl;
286
    }
287
}
288