diff --git a/.composer-require-checker.config.json b/.composer-require-checker.config.json index d6b6f85d..aec5100e 100644 --- a/.composer-require-checker.config.json +++ b/.composer-require-checker.config.json @@ -7,11 +7,18 @@ "pcre", "session", "mbstring" - ], "symbol-whitelist": [ "Twig\\Extension\\DebugExtension", - "Illuminate\\Support\\Facades\\DB" + "Twig\\Extension\\AbstractExtension", + "Twig\\Extension\\GlobalsInterface", + "Illuminate\\Support\\Facades\\DB", + + "// DI functions", + "DI\\ContainerBuilder", + + "// Not detected, do not know why.", + "Psr\\Container\\ContainerInterface" ], "scan-files": [ "*.php", diff --git a/app/Controllers/ControllerAbstract.php b/app/Controllers/ControllerAbstract.php index ff7689a4..e3309f27 100644 --- a/app/Controllers/ControllerAbstract.php +++ b/app/Controllers/ControllerAbstract.php @@ -13,7 +13,7 @@ abstract class ControllerAbstract * * @var ContainerInterface */ - protected $container; + protected ContainerInterface $container; protected Twig $view; @@ -33,75 +33,11 @@ public function __construct(ContainerInterface $container) unset($container); } - - /** - * Get Slim Container - * - * @return ContainerInterface - */ - protected function getContainer() - { - return $this->container; - } - - /** - * Get Service From Container - * - * @param string $service - * @return mixed - */ - protected function getService($service) - { - return $this->container->get($service); - } - - /** - * Get Request - * - * @return Request - */ - protected function getRequest() - { - return $this->container->request; - } - - /** - * Get Response - * - * @return Response - */ - protected function getResponse() - { - return $this->container->get('response'); - } - - /** - * Get Twig Engine - * - * @return Twig - */ - /*protected function getView() - { - return $this->container->get(Twig::class); - }*/ - - /** - * Render view - * - * @param string $template - * @param array $data - * @return string - */ - /*protected function render($template, $data = []) - { - return $this->getView()->render($this->getResponse(), $template, $data); - }*/ - /** * Get a JSON response * * @param Response $response Response instance - * @param array $data Data to send + * @param array $data Data to send * @param int $status HTTP status code * * @return Response diff --git a/app/Controllers/Reference.php b/app/Controllers/Reference.php index 1179e748..f6b9501e 100644 --- a/app/Controllers/Reference.php +++ b/app/Controllers/Reference.php @@ -9,7 +9,7 @@ class Reference extends ControllerAbstract { - public function view(Request $request, Response $response) + public function view(Request $request, Response $response): Response { $get = $request->getQueryParams(); // default session param for this controller @@ -124,7 +124,7 @@ function ($key) use ($join_table) { return $response; } - public function register(Request $request, Response $response) + public function register(Request $request, Response $response): Response { $post = $request->getParsedBody(); @@ -166,14 +166,17 @@ public function register(Request $request, Response $response) $dynamics = $dref->newInstance(); $dynamics->setTable($this->container->get('project')->getSlug() . '_reference'); + /** @phpstan-ignore-next-line */ $exists = $dynamics->where('reference_id', $reference['id'])->get(); if (0 === $exists->count()) { $dyn_data['reference_id'] = $reference['id']; + /** @phpstan-ignore-next-line */ $dynamics->insert( $dyn_data ); } else { + /** @phpstan-ignore-next-line */ $dynamics ->where('reference_id', '=', $reference['id']) ->update($dyn_data); @@ -203,7 +206,7 @@ public function register(Request $request, Response $response) ); } - public function filter(Request $request, Response $response) + public function filter(Request $request, Response $response): Response { $post = $request->getParsedBody(); if (isset($post['reset_filters'])) { @@ -223,7 +226,7 @@ public function filter(Request $request, Response $response) ); } - public function order(Request $request, Response $response, string $field) + public function order(Request $request, Response $response, string $field): Response { if ($_SESSION['reference']['orderby'] == $field) { // toggle sort if orderby requested on the same column @@ -240,5 +243,4 @@ public function order(Request $request, Response $response, string $field) $this->routeparser->urlFor('reference') ); } - } diff --git a/app/Controllers/Telemetry.php b/app/Controllers/Telemetry.php index 6cbd2065..17f411b5 100644 --- a/app/Controllers/Telemetry.php +++ b/app/Controllers/Telemetry.php @@ -16,7 +16,7 @@ class Telemetry extends ControllerAbstract { - public function view(Request $request, Response $response) + public function view(Request $request, Response $response): Response { $get = $request->getQueryParams(); $years = 99; @@ -231,7 +231,7 @@ public function view(Request $request, Response $response) return $response; } - public function send(Request $request, Response $response) + public function send(Request $request, Response $response): Response { $response = $response->withHeader('Content-Type', 'application/json'); @@ -288,10 +288,12 @@ public function send(Request $request, Response $response) $json = $json['data']; $data = $project->mapModel($json); + /** @var TelemetryModel $telemetry_m */ $telemetry_m = TelemetryModel::query()->create($data); // manage plugins foreach ($json[$project->getSlug()]['plugins'] as $plugin) { + /** @var GlpiPluginModel $plugin_m */ $plugin_m = GlpiPluginModel::query()->firstOrCreate(['pkey' => $plugin['key']]); TelemetryGlpiPlugin::query()->create([ @@ -304,7 +306,7 @@ public function send(Request $request, Response $response) return $this->withJson($response, ['message' => 'OK']); } - public function geojson(Request $request, Response $response) + public function geojson(Request $request, Response $response): Response { $countries = null; @@ -353,7 +355,7 @@ public function geojson(Request $request, Response $response) return $this->withJson($response, (array)json_decode($countries)); } - public function schema(Request $request, Response $response) + public function schema(Request $request, Response $response): Response { //$cache = $this->container->settings->get('debug') == true ? null : $this->container->cache; $cache = $this->container->get('cache'); @@ -361,7 +363,7 @@ public function schema(Request $request, Response $response) return $this->withJson($response, $schema); } - public function allPlugins(Request $request, Response $response) + public function allPlugins(Request $request, Response $response): Response { $years = 99; $get = $request->getQueryParams(); diff --git a/app/Middleware/Middleware.php b/app/Middleware/Middleware.php deleted file mode 100644 index 2704c5f8..00000000 --- a/app/Middleware/Middleware.php +++ /dev/null @@ -1,11 +0,0 @@ -container = $container; - } -} diff --git a/app/Models/DynamicReference.php b/app/Models/DynamicReference.php index 62010b24..c71379d0 100644 --- a/app/Models/DynamicReference.php +++ b/app/Models/DynamicReference.php @@ -13,10 +13,12 @@ class DynamicReference extends Model * Set the table associated with the model. * * @param string $table - * @return void + * + * @return self */ public function setTable($table) { $this->table = $table; + return $this; } } diff --git a/app/Models/GlpiPlugin.php b/app/Models/GlpiPlugin.php index 2723c3ef..58a762c7 100644 --- a/app/Models/GlpiPlugin.php +++ b/app/Models/GlpiPlugin.php @@ -2,6 +2,9 @@ use Illuminate\Database\Eloquent\Model; +/** + * @property-read int $id + */ class GlpiPlugin extends Model { protected $table = 'glpi_plugin'; diff --git a/app/Models/Telemetry.php b/app/Models/Telemetry.php index 44c807fc..a0b10182 100644 --- a/app/Models/Telemetry.php +++ b/app/Models/Telemetry.php @@ -2,6 +2,9 @@ use Illuminate\Database\Eloquent\Model; +/** + * @property-read int $id + */ class Telemetry extends Model { protected $table = 'telemetry'; diff --git a/app/Project.php b/app/Project.php index 17b3a111..0437212f 100644 --- a/app/Project.php +++ b/app/Project.php @@ -2,31 +2,29 @@ namespace GaletteTelemetry; +use Symfony\Component\Cache\Adapter\AbstractAdapter; + class Project { - private $slug = 'galette'; - private $url; - private $schema_usage; - private $schema_plugins = true; - private $mapping = []; + private string $slug = 'galette'; + private string $url; + /** @var null|false|array */ + private null|array|false $schema_usage; + private bool $schema_plugins = true; + /** @var array */ + private array $mapping = []; + /** @var mixed */ private $logger; - private $project_path; + private string $project_path; + /** @var array> */ private array $footer_links = []; + /** @var array> */ private array $social_links = []; + /** @var array> */ private $dyn_references = [ - 'can_prospect' => [ - 'label' => 'I agree to receive emails from GLPI and Teclib (including Newsletters, as well as promotional offers and announcements)', - 'in_list' => false, - 'checked' => true, - 'type' => 'boolean' - ], - 'num_assets' => [ - 'label' => 'Number of assets', - 'short_label' => '# assets' - ], - 'num_helpdesk' => [ - 'label' => 'Number of helpdesk', - 'short_label' => '# helpdesk' + 'num_members' => [ + 'label' => 'Number of members', + 'short_label' => '#members' ] ]; @@ -44,11 +42,11 @@ public function __construct($logger = null) /** * Set project configuration * - * @param array $config Configuration values + * @param array $config Configuration values * * @return Project */ - public function setConfig($config) + public function setConfig(array $config): self { $this->checkConfig($config); @@ -82,7 +80,7 @@ public function setConfig($config) /** * Check for required options in configuration * - * @param array $config Configuration values + * @param array $config Configuration values * * @return void */ @@ -152,16 +150,16 @@ private function setSchemaConfig($config) } /** - * Generate or retrieve project's schema + * Generate or retrieve project's schema as JSON * - * @param Laminas\Cache\Storage\Adapter\AbstractAdapter|null $cache Cache instance + * @param AbstractAdapter|null $cache Cache instance * - * @return json + * @return string */ - public function getSchema($cache) + public function getSchema(?AbstractAdapter $cache): string { - if (null != $cache && $cache->hasItem('schema')) { - $schema = $cache->getItem($this->getSlug() . '_schema.json'); + if (null != $cache && $cache->hasItem($this->getSlug() . 'schema')) { + $schema = $cache->getItem($this->getSlug() . '_schema.json')->get(); if (null != $schema) { return $schema; } @@ -228,7 +226,9 @@ public function getSchema($cache) $schema = json_encode($schema); if (null != $cache) { - $cache->setItem($this->getSlug() . '_schema.json', $schema); + $cached_schema = $cache->getItem($this->getSlug() . '_schema.json'); + $cached_schema->set($schema); + $cache->save($cached_schema); } return $schema; @@ -237,11 +237,11 @@ public function getSchema($cache) /** * Map schema data into model * - * @param array $json JSON sent data as array + * @param array $json JSON sent data as array * - * @return array + * @return array */ - public function mapModel($json) + public function mapModel(array $json): array { $slug = $this->getSlug(); @@ -306,7 +306,7 @@ public function mapModel($json) * * @return string */ - public function truncate($string, $length) + public function truncate(string $string, int $length): string { if (mb_strlen($string) > $length) { if ($this->logger !== null) { @@ -325,7 +325,7 @@ public function truncate($string, $length) * * @return string */ - public function getSlug() + public function getSlug(): string { return $this->slug; } @@ -335,7 +335,7 @@ public function getSlug() * * @return string */ - public function getURL() + public function getURL(): string { return $this->url; } @@ -343,9 +343,9 @@ public function getURL() /** * Get footer links * - * @return array + * @return array> */ - public function getFooterLinks() + public function getFooterLinks(): array { return $this->footer_links; } @@ -353,9 +353,9 @@ public function getFooterLinks() /** * Get social links * - * @return array + * @return array> */ - public function getSocialLinks() + public function getSocialLinks(): array { return $this->social_links; } @@ -363,9 +363,9 @@ public function getSocialLinks() /** * Get dynamic references * - * @return array|false + * @return array> */ - public function getDynamicReferences() + public function getDynamicReferences(): array { return $this->dyn_references; } diff --git a/app/Twig/CsrfExtension.php b/app/Twig/CsrfExtension.php index b84cff88..6c75742d 100644 --- a/app/Twig/CsrfExtension.php +++ b/app/Twig/CsrfExtension.php @@ -25,7 +25,7 @@ public function __construct(Guard $csrf) /** * Get globals * - * @return array + * @return array */ public function getGlobals(): array { diff --git a/app/init.php b/app/init.php index 6bcc267f..0ef1746e 100644 --- a/app/init.php +++ b/app/init.php @@ -1,7 +1,8 @@ build(); $app = Bridge::create($container); +/** @var DI\Container */ $container = $app->getContainer(); $routeParser = $app->getRouteCollector()->getRouteParser(); @@ -55,7 +57,7 @@ $app->setBasePath((function () { $scriptDir = str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])); - $uri = (string)parse_url('http://a' . $_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH); + $uri = (string)parse_url('http://a' . $_SERVER['REQUEST_URI'], PHP_URL_PATH); if (stripos($uri, $_SERVER['SCRIPT_NAME']) === 0) { return dirname($_SERVER['SCRIPT_NAME']); } @@ -67,10 +69,6 @@ $app->addRoutingMiddleware(); -// init slim -/*$app = new \Slim\App(["settings" => $config]); -$container = $app->getContainer();*/ - $container->set('mail_from', $config['mail_from']); $container->set('mail_admin', $config['mail_admin']); $container->set('is_debug', $config['debug']); @@ -212,7 +210,7 @@ function ($c) use ($config) { //setup recaptcha if (TELEMETRY_MODE == 'DEV') { - $recaptcha = function (Request $request, RequestHandler $handler) { + $recaptcha = function (ServerRequestInterface $request, RequestHandler $handler) { //does nothing $response = $handler->handle($request); return $response; @@ -233,49 +231,6 @@ function ($c) use ($config) { $recaptcha = $container->get(Captcha::class); } -// system error handling -/*$customErrorHandler = function ( - ServerRequestInterface $request, - Throwable $exception, - bool $displayErrorDetails, - bool $logErrors, - bool $logErrorDetails, - ?LoggerInterface $logger = null -) use ($container, $app, $config) { - // log error - $container->get('logger')->error('error', [$exception->getMessage()]); - $response = $app->getResponseFactory()->createResponse(); - - // return json error - if (strpos($request->getHeaderLine('Content-Type'), 'application/json') !== false) { - $answer = [ - 'message' => 'Something went wrong!' - ]; - - if ($config['debug']) { - $answer['message'] = $exception->getMessage(); - } - - return $response - ->withStatus(500) - ->withHeader('Content-Type', 'application/json') - ->write(json_encode($answer)); - } - - // html error for production env - if (!$config['debug']) { - return $config->get(Twig::class)->render( - $response, - "errors/server.html" - ); - } - - // if not special case, return slim default handler - $error = new ErrorHandler(); - return $error->__invoke($request, $response, $exception); -}; -$errorMiddleware = $app->addErrorMiddleware(true, true, true); -$errorMiddleware->setDefaultErrorHandler($customErrorHandler);*/ $app->addErrorMiddleware(true, true, true); $container->set( @@ -336,20 +291,6 @@ function ($c) { } ); -// php error handler -/*if (!$config['debug']) { - $container['phpErrorHandler'] = function ($c) { - return function ($request, $response, $error) use ($c) { - $c->get('logger')->error('error', [$error->getMessage()]); - return $c->get(Twig::class)->render( - $response, - "errors/server.html" - ); - }; - }; -}*/ - - // manage page parameter for Eloquent Paginator // @see https://github.com/mattstauffer/Torch/blob/master/components/pagination/index.php Paginator::currentPageResolver(function ($pageName = 'page') { @@ -367,30 +308,5 @@ function ($c) { $app->addRoutingMiddleware(); $app->add(TwigMiddleware::createFromContainer($app, Twig::class)); -/** - * Trailing slash middleware - */ -$app->add(function (Request $request, RequestHandler $handler) { - $uri = $request->getUri(); - $path = $uri->getPath(); - - if ($path != '/' && substr($path, -1) == '/') { - // recursively remove slashes when its more than 1 slash - $path = rtrim($path, '/'); - - // permanently redirect paths with a trailing slash - // to their non-trailing counterpart - $uri = $uri->withPath(substr($path, 0, -1)); - - if ($request->getMethod() == 'GET') { - $response = new Response(); - return $response - ->withHeader('Location', (string) $uri) - ->withStatus(301);; - } else { - $request = $request->withUri($uri); - } - } - - return $handler->handle($request); -}); +//trailing slash middleware +$app->add(new TrailingSlash(false)); // true adds the trailing slash (false removes it) diff --git a/composer.json b/composer.json index 7d9a9677..647ff169 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,8 @@ "robmorgan/phinx": "^0.14.0", "symfony/cache": "^6.3", "php-di/slim-bridge": "^3.4", - "slim/psr7": "^1.6" + "slim/psr7": "^1.6", + "middlewares/trailing-slash": "^2.0" }, "require-dev": { "squizlabs/php_codesniffer": "^3.7", diff --git a/composer.lock b/composer.lock index ac63b71a..053c3f6f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "87f078166b3efc41d14d1c1ab3978155", + "content-hash": "be1923043be2fc5c78d108c7fbfd2307", "packages": [ { "name": "brick/math", @@ -1108,6 +1108,122 @@ }, "time": "2023-10-17T13:38:16+00:00" }, + { + "name": "middlewares/trailing-slash", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/middlewares/trailing-slash.git", + "reference": "1bedcedbc89be78595c5a7a86776fe5ed003e819" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/middlewares/trailing-slash/zipball/1bedcedbc89be78595c5a7a86776fe5ed003e819", + "reference": "1bedcedbc89be78595c5a7a86776fe5ed003e819", + "shasum": "" + }, + "require": { + "middlewares/utils": "^3.0", + "php": "^7.2 || ^8.0", + "psr/http-server-middleware": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.0", + "laminas/laminas-diactoros": "^2.2", + "oscarotero/php-cs-fixer-config": "^1.0", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8|^9", + "squizlabs/php_codesniffer": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Middlewares\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Middleware to normalize the trailing slash of the uri path", + "homepage": "https://github.com/middlewares/trailing-slash", + "keywords": [ + "http", + "middleware", + "normalize", + "path", + "psr-15", + "psr-7", + "slash" + ], + "support": { + "issues": "https://github.com/middlewares/trailing-slash/issues", + "source": "https://github.com/middlewares/trailing-slash/tree/v2.0.1" + }, + "time": "2020-12-02T00:06:55+00:00" + }, + { + "name": "middlewares/utils", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/middlewares/utils.git", + "reference": "670b135ce0dbd040eadb025a9388f9bd617cc010" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/middlewares/utils/zipball/670b135ce0dbd040eadb025a9388f9bd617cc010", + "reference": "670b135ce0dbd040eadb025a9388f9bd617cc010", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "psr/http-server-middleware": "^1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^v2.16", + "guzzlehttp/psr7": "^2.0", + "laminas/laminas-diactoros": "^2.4", + "nyholm/psr7": "^1.0", + "oscarotero/php-cs-fixer-config": "^1.0", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8|^9", + "slim/psr7": "^1.4", + "squizlabs/php_codesniffer": "^3.5", + "sunrise/http-message": "^1.0", + "sunrise/http-server-request": "^1.0", + "sunrise/stream": "^1.0.15", + "sunrise/uri": "^1.0.15" + }, + "type": "library", + "autoload": { + "psr-4": { + "Middlewares\\Utils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Common utils for PSR-15 middleware packages", + "homepage": "https://github.com/middlewares/utils", + "keywords": [ + "PSR-11", + "http", + "middleware", + "psr-15", + "psr-17", + "psr-7" + ], + "support": { + "issues": "https://github.com/middlewares/utils/issues", + "source": "https://github.com/middlewares/utils/tree/v3.3.0" + }, + "time": "2021-07-04T17:56:23+00:00" + }, { "name": "mledoze/countries", "version": "5.0.0", diff --git a/phpstan.neon b/phpstan.neon index bfec2fbc..80942d68 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ parameters: parallel: maximumNumberOfProcesses: 2 - level: 1 + level: 6 paths: - app/ ignoreErrors: