Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
42 / 42
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
AbstractApp
100.00% covered (success)
100.00%
42 / 42
100.00% covered (success)
100.00%
9 / 9
19
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 init
n/a
0 / 0
n/a
0 / 0
0
 process
n/a
0 / 0
n/a
0 / 0
0
 fullInit
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 fullProcess
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 hasMiddleware
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addMiddleware
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 runMiddlewares
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 finish
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 loadConfigs
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 handleException
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace Dynart\Micro;
4
5use Exception;
6
7/**
8 * Micro PHP Application
9 */
10abstract class AbstractApp {
11
12    const CONFIG_BASE_URL = 'app.base_url';
13    const CONFIG_ROOT_PATH = 'app.root_path';
14    const CONFIG_ENVIRONMENT = 'app.environment';
15    const PRODUCTION_ENVIRONMENT = 'prod';
16    const EVENT_INIT_FINISHED = 'app:init_finished';
17
18    /** Stores the middleware class names in a list */
19    protected array $middlewares = [];
20    protected array $middlewareRegistry = [];
21    protected ?ConfigInterface $config = null;
22    protected ?LoggerInterface $logger = null;
23    protected ?EventServiceInterface $eventService = null;
24    /** @var string[] */
25    protected array $configPaths;
26    protected bool $exitOnFinish = true;
27
28    public function __construct(array $configPaths) {
29        $this->configPaths = $configPaths;
30        Micro::add(ConfigInterface::class, Config::class);
31        Micro::add(LoggerInterface::class, Logger::class);
32        Micro::add(EventServiceInterface::class, EventService::class);
33    }
34
35    /**
36     * Abstract function for initialize the application
37     */
38    abstract public function init(): void;
39
40    /**
41     * Abstract function for processing the application
42     */
43    abstract public function process(): void;
44
45    /**
46     * Fully initializes the application
47     *
48     * Creates the `Config`, loads the configs, creates the `Logger` and the `eventService`, calls the `init()` method
49     * then runs all the middlewares. If an exception happens handles in the `handleException()` method.
50     */
51    public function fullInit(): void {
52        try {
53            $this->config = Micro::get(ConfigInterface::class);
54            $this->loadConfigs();
55            $this->logger = Micro::get(LoggerInterface::class);
56            $this->eventService = Micro::get(EventServiceInterface::class);
57            $this->init();
58            $this->runMiddlewares();
59            $this->eventService->emit(self::EVENT_INIT_FINISHED);
60        } catch (Exception $e) {
61            $this->handleException($e);
62        }
63    }
64
65    /**
66     * Calls the `process()` method within a try/catch, handles exception with the `handleException()` method
67     */
68    public function fullProcess(): void {
69        try {
70            $this->process();
71        } catch (Exception $e) {
72            $this->handleException($e);
73        }
74    }
75
76    /**
77     * Returns true if the middleware is already registered
78     */
79    public function hasMiddleware(string $interface): bool {
80        return in_array($interface, $this->middlewareRegistry);
81    }
82
83    /**
84     * Adds a middleware
85     *
86     * It adds only if not presents.
87     */
88    public function addMiddleware(string $interface, int $priority = 50): void {
89        if (in_array($interface, $this->middlewareRegistry)) {
90            return;
91        }
92        Micro::add($interface);
93        $this->middlewareRegistry[] = $interface;
94        if (!isset($this->middlewares[$priority])) {
95            $this->middlewares[$priority] = [];
96        }
97        $this->middlewares[$priority][] = $interface;
98    }
99
100    /**
101     * Runs all the added middlewares
102     */
103    protected function runMiddlewares(): void {
104        ksort($this->middlewares);
105        foreach ($this->middlewares as $middlewares) {
106            foreach ($middlewares as $m) {
107                Micro::get($m)->run();
108            }
109        }
110    }
111
112    /**
113     * Finishes the application
114     *
115     * If the `$exitOnFinish` true (default) calls the exit, otherwise just prints out the content.
116     *
117     * @param string|int $content Content for the output. If it's an int, it is the return code of the process.
118     */
119    public function finish(string|int $content = 0): void {
120        $this->exitOnFinish ? exit($content) : print($content);
121    }
122
123    /**
124     * Loads all the configs by the `$configPaths`
125     */
126    protected function loadConfigs(): void {
127        foreach ($this->configPaths as $path) {
128            $this->config->load($path);
129        }
130    }
131
132    /**
133     * Handles the exception
134     *
135     * Sends the type, the line, the exception message and the stacktrace to the standard error output.
136     * If the `Config` or the `Logger` wasn't initialised throws a `MicroException`.
137     *
138     * @throws MicroException
139     */
140    protected function handleException(Exception $e): void {
141        $type = get_class($e);
142        $file = $e->getFile();
143        $line = $e->getLine();
144        $message = $e->getMessage();
145        $trace = $e->getTraceAsString();
146        $text = "`$type` in $file on line $line with message: $message\n$trace";
147        if (!$this->config) {
148            throw new MicroException("Couldn't instantiate Config::class, original exception:\n".$text);
149        }
150        if (!$this->logger) {
151            throw new MicroException("Couldn't instantiate Logger::class, original exception:\n".$text);
152        }
153        $this->logger->error($text);
154    }
155
156}