Completed
Pull Request — master (#4)
by Chauncey
06:44
created

App::setupMiddleware()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 8.8571
c 0
b 0
f 0
cc 6
eloc 11
nc 5
nop 0
1
<?php
2
3
namespace Charcoal\App;
4
5
// PHP Dependencies
6
use Exception;
7
use LogicException;
8
use RuntimeException;
9
10
// Dependency from 'Slim'
11
use Slim\App as SlimApp;
12
use Slim\Exception\ContainerValueNotFoundException;
13
14
// Dependencies from 'PSR-3' (Logging)
15
use Psr\Log\LoggerAwareInterface;
16
use Psr\Log\LoggerAwareTrait;
17
18
// Dependencies from 'PSR-7' (HTTP Messaging)
19
use Psr\Http\Message\RequestInterface;
20
use Psr\Http\Message\ResponseInterface;
21
22
// Dependencies from 'charcoal-config'
23
use Charcoal\Config\ConfigurableInterface;
24
use Charcoal\Config\ConfigurableTrait;
25
26
// Local namespace dependencies
27
use Charcoal\App\AppConfig;
28
use Charcoal\App\AppContainer;
29
use Charcoal\App\Module\ModuleManager;
30
use Charcoal\App\Route\RouteManager;
31
use Charcoal\App\Route\RouteFactory;
32
33
/**
34
 * Charcoal App
35
 *
36
 * This is the primary class with which you instantiate, configure,
37
 * and run a Slim Framework application within Charcoal.
38
 */
39
class App extends SlimApp implements
40
    ConfigurableInterface
41
{
42
    use ConfigurableTrait;
43
44
    /**
45
     * Store the unique instance
46
     *
47
     * @var App $instance
48
     */
49
    protected static $instance;
50
51
    /**
52
     * @var ModuleManager
53
     */
54
    private $moduleManager;
55
56
    /**
57
     * @var RouteManager
58
     */
59
    private $routeManager;
60
61
    /**
62
     * Getter for creating/returning the unique instance of this class.
63
     *
64
     * @param Container|array $container The application's settings.
65
     * @return self
66
     */
67
    public static function instance($container = [])
68
    {
69
        if (!isset(static::$instance)) {
70
            $called_class = get_called_class();
71
72
            static::$instance = new $called_class($container);
73
        }
74
75
        return static::$instance;
76
    }
77
78
    /**
79
     * Create new Charcoal application (and SlimApp).
80
     *
81
     * ### Dependencies
82
     *
83
     * **Required**
84
     *
85
     * - `config` — AppConfig
86
     *
87
     * **Optional**
88
     *
89
     * - `logger` — PSR-3 Logger
90
     *
91
     * @uses  SlimApp::__construct()
92
     * @param ContainerInterface|array $container The application's settings.
93
     * @throws LogicException If trying to create a new instance of a singleton.
94
     */
95
    public function __construct($container = [])
96
    {
97
        if (isset(static::$instance)) {
98
            throw new LogicException(
99
                sprintf(
100
                    '"%s" is a singleton. Use static instance() method.',
101
                    get_called_class()
102
                )
103
            );
104
        }
105
106
        // Ensure the DI container is
107
        if (is_array($container)) {
108
            $container = new AppContainer($container);
109
        }
110
111
        // SlimApp constructor
112
        parent::__construct($container);
113
114
        if (isset($container['config'])) {
115
            $this->setConfig($container['config']);
116
        }
117
    }
118
119
    /**
120
     * Run application.
121
     *
122
     * Initialize the Charcoal application before running (with SlimApp).
123
     *
124
     * @uses   self::setup()
125
     * @param  boolean $silent If true, will run in silent mode (no response).
126
     * @return ResponseInterface The PSR7 HTTP response.
127
     */
128
    public function run($silent = false)
129
    {
130
        $this->setup();
131
132
        return parent::run($silent);
133
    }
134
135
    /**
136
     * @throws LogicException If trying to clone an instance of a singleton.
137
     * @return void
138
     */
139
    final private function __clone()
140
    {
141
        throw new LogicException(
142
            sprintf(
143
                'Cloning "%s" is not allowed.',
144
                get_called_class()
145
            )
146
        );
147
    }
148
149
    /**
150
     * @throws LogicException If trying to unserialize an instance of a singleton.
151
     * @return void
152
     */
153
    final private function __wakeup()
154
    {
155
        throw new LogicException(
156
            sprintf(
157
                'Unserializing "%s" is not allowed.',
158
                get_called_class()
159
            )
160
        );
161
    }
162
163
    /**
164
     * Retrieve the application's module manager.
165
     *
166
     * @return ModuleManager
167
     */
168
    protected function moduleManager()
169
    {
170
        if (!isset($this->moduleManager)) {
171
            $config  = $this->config();
172
            $modules = (isset($config['modules']) ? $config['modules'] : [] );
173
174
            $container = $this->getContainer();
175
            $this->moduleManager = new ModuleManager([
176
                'config' => $modules,
177
                'app'    => $this,
178
                'logger' => $container['logger'],
179
                'module_factory' => $container['module/factory']
180
            ]);
181
        }
182
183
        return $this->moduleManager;
184
    }
185
186
    /**
187
     * Retrieve the application's route manager.
188
     *
189
     * @return RouteManager
190
     */
191
    protected function routeManager()
192
    {
193
        if (!isset($this->routeManager)) {
194
            $config = $this->config();
195
            $routes = (isset($config['routes']) ? $config['routes'] : [] );
196
197
            $this->routeManager = new RouteManager([
198
                'config' => $routes,
199
                'app'    => $this
200
            ]);
201
        }
202
203
        return $this->routeManager;
204
    }
205
206
    /**
207
     * Registers the default services and features that Charcoal needs to work.
208
     *
209
     * @return void
210
     */
211
    private function setup()
212
    {
213
        $config = $this->config();
214
        date_default_timezone_set($config['timezone']);
215
216
        // Setup routes
217
        $this->routeManager()->setupRoutes();
218
219
        // Setup modules
220
        $this->moduleManager()->setupModules($this);
0 ignored issues
show
Unused Code introduced by
The call to ModuleManager::setupModules() has too many arguments starting with $this.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
221
222
        // Setup routable
223
        $this->setupRoutables();
224
225
        // Setup middleware
226
        $this->setupMiddleware();
227
    }
228
229
230
    /**
231
     * Setup the application's "global" routables.
232
     *
233
     * Routables can only be defined globally (app-level) for now.
234
     *
235
     * @return void
236
     */
237
    protected function setupRoutables()
238
    {
239
        $app = $this;
240
        // For now, need to rely on a catch-all...
241
        $this->get(
242
            '{catchall:.*}',
243
            function (
244
                RequestInterface $request,
245
                ResponseInterface $response,
246
                array $args
247
            ) use ($app) {
248
                $config = $app->config();
249
                $routables = $config['routables'];
250
                if ($routables === null || count($routables) === 0) {
251
                    return $this['notFoundHandler']($request, $response);
252
                }
253
                $routeFactory = $this['route/factory'];
254
                foreach ($routables as $routableType => $routableOptions) {
255
                    $route = $routeFactory->create($routableType, [
256
                        'path' => $args['catchall'],
257
                        'config' => $routableOptions
258
                    ]);
259
                    if ($route->pathResolvable($this)) {
260
                        $this['logger']->debug(
261
                            sprintf('Loaded routable "%s" for path %s', $routableType, $args['catchall'])
262
                        );
263
                        return $route($this, $request, $response);
264
                    }
265
                }
266
267
                // If this point is reached, no routable has provided a callback. 404.
268
                return $this['notFoundHandler']($request, $response);
269
            }
270
        );
271
    }
272
273
    /**
274
     * @throws ContainerValueNotFoundException No container entry was found for the middleware.
275
     * @return void
276
     */
277
    protected function setupMiddleware()
278
    {
279
        $container = $this->getContainer();
280
        $middlewareConfig = $container['config']['middleware'];
281
        if (!$middlewareConfig) {
282
            return;
283
        }
284
        foreach ($middlewareConfig as $key => $opts) {
285
            if (isset($opts['active']) && $opts['active'] === true) {
286
                if (!isset($container[$key])) {
287
                    throw new ContainerValueNotFoundException(
288
                        sprintf('Middleware "%s" is not defined on the container.', $key)
289
                    );
290
                }
291
                $this->add($container[$key]);
292
            }
293
        }
294
    }
295
}
296