1: <?php
2:
3: namespace Charcoal\App;
4:
5:
6: use Exception;
7: use LogicException;
8:
9:
10: use Slim\App as SlimApp;
11:
12:
13: use Psr\Log\LoggerAwareInterface;
14: use Psr\Log\LoggerAwareTrait;
15:
16:
17: use Psr\Http\Message\RequestInterface;
18: use Psr\Http\Message\ResponseInterface;
19:
20:
21: use Charcoal\Config\ConfigurableInterface;
22: use Charcoal\Config\ConfigurableTrait;
23:
24:
25: use Charcoal\App\AppConfig;
26: use Charcoal\App\AppContainer;
27: use Charcoal\App\Module\ModuleManager;
28: use Charcoal\App\Route\RouteManager;
29: use Charcoal\App\Route\RouteFactory;
30:
31: 32: 33: 34: 35: 36:
37: class App extends SlimApp implements
38: ConfigurableInterface
39: {
40: use ConfigurableTrait;
41:
42: 43: 44: 45: 46:
47: protected static $instance;
48:
49: 50: 51:
52: private $moduleManager;
53:
54: 55: 56:
57: private $routeManager;
58:
59: 60: 61: 62: 63: 64:
65: public static function instance($container = [])
66: {
67: if (!isset(static::$instance)) {
68: $called_class = get_called_class();
69:
70: static::$instance = new $called_class($container);
71: }
72:
73: return static::$instance;
74: }
75:
76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92:
93: public function __construct($container = [])
94: {
95: if (isset(static::$instance)) {
96: throw new LogicException(
97: sprintf(
98: '"%s" is a singleton. Use static instance() method.',
99: get_called_class()
100: )
101: );
102: }
103:
104:
105: if (is_array($container)) {
106: $container = new AppContainer($container);
107: }
108:
109:
110: parent::__construct($container);
111:
112: if (isset($container['config'])) {
113: $this->setConfig($container['config']);
114: }
115: }
116:
117: 118: 119: 120: 121: 122: 123: 124: 125:
126: public function run($silent = false)
127: {
128: $this->setup();
129:
130: return parent::run($silent);
131: }
132:
133: 134: 135: 136:
137: final private function __clone()
138: {
139: throw new LogicException(
140: sprintf(
141: 'Cloning "%s" is not allowed.',
142: get_called_class()
143: )
144: );
145: }
146:
147: 148: 149: 150:
151: final private function __wakeup()
152: {
153: throw new LogicException(
154: sprintf(
155: 'Unserializing "%s" is not allowed.',
156: get_called_class()
157: )
158: );
159: }
160:
161: 162: 163: 164: 165:
166: protected function moduleManager()
167: {
168: if (!isset($this->moduleManager)) {
169: $config = $this->config();
170: $modules = (isset($config['modules']) ? $config['modules'] : [] );
171:
172: $container = $this->getContainer();
173: $this->moduleManager = new ModuleManager([
174: 'config' => $modules,
175: 'app' => $this,
176: 'logger' => $container['logger'],
177: 'module_factory' => $container['module/factory']
178: ]);
179: }
180:
181: return $this->moduleManager;
182: }
183:
184: 185: 186: 187: 188:
189: protected function routeManager()
190: {
191: if (!isset($this->routeManager)) {
192: $config = $this->config();
193: $routes = (isset($config['routes']) ? $config['routes'] : [] );
194:
195: $this->routeManager = new RouteManager([
196: 'config' => $routes,
197: 'app' => $this
198: ]);
199: }
200:
201: return $this->routeManager;
202: }
203:
204: 205: 206: 207: 208:
209: private function setup()
210: {
211: $config = $this->config();
212: date_default_timezone_set($config['timezone']);
213:
214:
215: $this->routeManager()->setupRoutes();
216:
217:
218: $this->moduleManager()->setupModules($this);
219:
220:
221: $this->setupRoutables();
222:
223:
224: $container = $this->getContainer();
225: if ($container['cache/config']['middleware']['active']) {
226: $this->add($container['cache/middleware']);
227: }
228: }
229:
230:
231: 232: 233: 234: 235: 236: 237:
238: protected function setupRoutables()
239: {
240: $app = $this;
241:
242: $this->get(
243: '{catchall:.*}',
244: function (
245: RequestInterface $request,
246: ResponseInterface $response,
247: array $args
248: ) use ($app) {
249: $config = $app->config();
250: $routables = $config['routables'];
251: if ($routables === null || count($routables) === 0) {
252: return $this['notFoundHandler']($request, $response);
253: }
254: $routeFactory = $this['route/factory'];
255: foreach ($routables as $routableType => $routableOptions) {
256: $route = $routeFactory->create($routableType, [
257: 'path' => $args['catchall'],
258: 'config' => $routableOptions
259: ]);
260: if ($route->pathResolvable($this)) {
261: $this['logger']->debug(
262: sprintf('Loaded routable "%s" for path %s', $routableType, $args['catchall'])
263: );
264: return $route($this, $request, $response);
265: }
266: }
267:
268:
269: return $this['notFoundHandler']($request, $response);
270: }
271: );
272: }
273: }
274: