• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

klinge / sl-webapp / 18972011891

31 Oct 2025 12:06PM UTC coverage: 74.73% (+11.1%) from 63.602%
18972011891

push

github

klinge
Fixed phpcs errors

1662 of 2224 relevant lines covered (74.73%)

3.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.48
/App/Application.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App;
6

7
use Dotenv\Dotenv;
8
use League\Route\Router;
9
use Monolog\Logger;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Psr\Http\Message\ResponseInterface;
12
use Laminas\Diactoros\ServerRequestFactory;
13
use League\Container\Container;
14
use App\Config\RouteConfig;
15
use App\Config\ContainerConfigurator;
16
use App\Middleware\Contracts\MiddlewareInterface;
17
use App\Middleware\Contracts\MiddlewareStack;
18
use App\Middleware\ApplicationHandler;
19
use App\Middleware\CsrfMiddleware;
20
use App\Utils\Session;
21
use App\Utils\ResponseEmitter;
22
use Exception;
23

24
/**
25
 * The main Application class that bootstraps the application and handles routing.
26
 *
27
 * This class is responsible for:
28
 * - Loading the environment variables from the .env file
29
 * - Loading the application configuration
30
 * - Setting up the error reporting based on the application environment
31
 * - Setting up the routing using the League Route library
32
 * - Registering middleware to be executed for each request
33
 * - Dispatching to the appropriate controller action based on the current route
34
 * - Starting the session
35
 */
36
class Application
37
{
38
    private array $config = [];
39
    private $container;
40
    private ?Router $router = null;
41
    private MiddlewareStack $middlewareStack;
42
    private string $rootDir = '';
43
    private ServerRequestInterface $psrRequest;
44
    private Logger $logger;
45

46
    public function __construct()
47
    {
48
        $this->rootDir = dirname(__DIR__);
15✔
49
        $this->loadEnvironment();
15✔
50
        $this->loadConfig();
15✔
51
        $this->setErrorReporting($this->getAppEnv());
15✔
52
        $this->setupSession();
15✔
53
        $this->psrRequest = ServerRequestFactory::fromGlobals(
15✔
54
            $_SERVER,
15✔
55
            $_GET,
15✔
56
            $_POST,
15✔
57
            $_COOKIE
15✔
58
        );
15✔
59
        $this->setupContainer();
15✔
60
        $this->logger = $this->container->get(Logger::class);
15✔
61
        $this->setupRouter();
15✔
62
        $this->setupMiddlewareStack();
15✔
63

64
        // Add global middlewares here
65
        $this->addMiddleware(new CsrfMiddleware($this->router, $this->logger));
15✔
66
    }
67

68
    private function setupContainer(): void
69
    {
70
        $this->container = new Container();
15✔
71
        ContainerConfigurator::registerServices($this->container, $this);
15✔
72
    }
73

74
    /**
75
     * Sets up the router for the application.
76
     *
77
     * This method initializes the League Router instance and calls the
78
     * `RouteConfig::createAppRoutes` static method to define the application routes.
79
     *
80
     * @return void
81
     */
82
    private function setupRouter(): void
83
    {
84
        $this->router = new Router();
15✔
85

86
        // Register router in container now that it's created
87
        $this->container->add(Router::class, $this->router);
15✔
88

89
        // Set up the application strategy with container for dependency injection
90
        $strategy = new \League\Route\Strategy\ApplicationStrategy();
15✔
91
        $strategy->setContainer($this->container);
15✔
92
        $this->router->setStrategy($strategy);
15✔
93

94
        // Routes are created from the Config/RouteConfig class
95
        RouteConfig::createAppRoutes($this->router, $this->container);
15✔
96
    }
97

98
    /**
99
     * Loads the environment variables from the .env file using the Dotenv library.
100
     *
101
     * The path to the .env file is hardcoded in this method, as the base directory
102
     * of the application in in the .env file. After loading the .env file, the
103
     * environment variables can be accessed using Application::getConfig['key'].
104
     *
105
     * @return void
106
     */
107
    private function loadEnvironment(): void
108
    {
109
        $dotenv = Dotenv::createImmutable($this->rootDir);
15✔
110
        $dotenv->safeLoad();
15✔
111
    }
112

113
    /**
114
     * Loads the application configuration.
115
     *
116
     * This method loads the application configuration from the environment variables
117
     * and converts the string values to their appropriate data types (boolean, etc.).
118
     *
119
     * @return void
120
     */
121
    private function loadConfig(): void
122
    {
123
        $this->config = array_map(function ($value) {
15✔
124
            return $value === 'true' ? true : ($value === 'false' ? false : $value);
15✔
125
        }, $_ENV);
15✔
126
    }
127

128
    /**
129
     * Returns the current request as a PSR-7 ServerRequestInterface object.
130
     *
131
     * @return ServerRequestInterface The current request
132
     */
133
    public function getPsrRequest(): ServerRequestInterface
134
    {
135
        return $this->psrRequest;
15✔
136
    }
137

138
    /**
139
     * Returns the path for the application relative to the servers document root,
140
     * returns an empty string if the APP_DIR is not set.
141
     *
142
     * @return ?string The base path for the application
143
     */
144
    public function getAppDir(): ?string
145
    {
146
        return $this->config['APP_DIR'];
1✔
147
    }
148

149
    /**
150
     * Returns the full path for the application root directory
151
     *
152
     * @return string The full path to the application root directory
153
     */
154
    public function getRootDir(): string
155
    {
156
        return $this->rootDir;
15✔
157
    }
158

159
    /**
160
     * Returns the application environment as a string "DEV|PROD", defaults to PROD
161
     *
162
     * @return string "DEV"|"PROD""
163
     */
164
    public function getAppEnv(): string
165
    {
166
        //Only return DEV if the APP_ENV is set to DEV, otherwise default to PROD
167
        return ($this->config['APP_ENV'] === "DEV") ? "DEV" : "PROD";
15✔
168
    }
169

170
    /**
171
     * Returns the entire config or the value of a specific environment variable
172
     *
173
     * @param ?string $key The environment variable to retrieve
174
     *
175
     * @return array|string|bool|null The entire config array, the value of the environment variable or null if the key is not found
176
     */
177
    public function getConfig(?string $key): array|string|bool|null
178
    {
179
        if ($key === null) {
15✔
180
            return $this->config;
15✔
181
        }
182
        return $this->config[$key] ?? null;
15✔
183
    }
184

185
    /**
186
     * Returns the DI container used by the application.
187
     *
188
     * @return Container The DI container
189
     */
190
    public function getContainer(): Container
191
    {
192
        return $this->container;
1✔
193
    }
194

195
    /**
196
     * Returns the League Router instance used by the application.
197
     *
198
     * @return Router The Router instance
199
     */
200
    public function getRouter(): Router
201
    {
202
        return $this->router;
1✔
203
    }
204

205
    /**
206
     * Sets up the session for the application.
207
     *
208
     * This method configures the session settings, such as the cookie settings
209
     * and the session lifetime, and starts the session.
210
     *
211
     * @return void
212
     */
213
    private function setupSession(): void
214
    {
215
        //Only require a secure connection for production
216
        $isProduction = $this->getAppEnv() === 'PROD';
15✔
217

218
        // Only set cookie params if session is not already active
219
        if (session_status() === PHP_SESSION_NONE) {
15✔
220
            session_set_cookie_params([
1✔
221
                'lifetime' => 3600,
1✔
222
                'secure' => $isProduction,
1✔
223
                'httponly' => true,
1✔
224
                'samesite' => 'Strict',
1✔
225
            ]);
1✔
226
        }
227

228
        Session::start();
15✔
229

230
        //Regenerate session id every 30 mins
231
        if (!isset($_SESSION['session_regeneration_time'])) {
15✔
232
            $_SESSION['session_regeneration_time'] = time();
15✔
233
        } elseif (time() - $_SESSION['session_regeneration_time'] > 1800) { // every 30 minutes
×
234
            session_regenerate_id(true);
×
235
            $_SESSION['session_regeneration_time'] = time();
×
236
        }
237
    }
238

239
    /**
240
     * Sets error reporting for the application.
241
     *
242
     * Turns error reporting on or off depending on the application environment.
243
     *
244
     * @return void
245
     */
246
    private function setErrorReporting(string $appEnv): void
247
    {
248
        if ($appEnv === 'DEV') {
15✔
249
            error_reporting(E_ALL);
13✔
250
            ini_set('display_errors', 'On');
13✔
251
        } else {
252
            error_reporting(0);
2✔
253
            ini_set('display_errors', 'Off');
2✔
254
        }
255
    }
256

257
    /**
258
     * Sets up the PSR-15 compliant middleware stack.
259
     */
260
    private function setupMiddlewareStack(): void
261
    {
262
        $applicationHandler = new ApplicationHandler($this, $this->router);
15✔
263
        $this->middlewareStack = new MiddlewareStack($applicationHandler);
15✔
264
    }
265

266
    /**
267
     * Adds a middleware to the application stack.
268
     */
269
    public function addMiddleware(MiddlewareInterface $middleware): void
270
    {
271
        $this->middlewareStack->add($middleware);
15✔
272
    }
273

274
    /**
275
     * Runs the application through the PSR-15 middleware stack.
276
     */
277
    public function run(): void
278
    {
279
        $response = $this->middlewareStack->handle($this->psrRequest);
×
280

281
        $emitter = new ResponseEmitter();
×
282
        $emitter->emit($response);
×
283
    }
284
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc