diff --git a/.github/workflows/1-tests.yml b/.github/workflows/1-tests.yml
new file mode 100644
index 0000000..ab461db
--- /dev/null
+++ b/.github/workflows/1-tests.yml
@@ -0,0 +1,41 @@
+name: tests
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Setup PHP with PECL extension
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.2'
+
+ - uses: actions/checkout@v3
+
+ - name: Validate composer.json and composer.lock
+ run: composer validate --strict
+
+ - name: Cache Composer packages
+ id: composer-cache
+ uses: actions/cache@v3
+ with:
+ path: vendor
+ key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-php-
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress
+
+ - name: Tests with code coverage
+ run: composer coverage-xml
+
+ - name: Upload coverage reports to Codecov
+ uses: codecov/codecov-action@v3
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/2-code-quality.yml b/.github/workflows/2-code-quality.yml
new file mode 100644
index 0000000..b8f9f91
--- /dev/null
+++ b/.github/workflows/2-code-quality.yml
@@ -0,0 +1,51 @@
+name: code-quality
+
+on:
+ push:
+ pull_request:
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Setup PHP with PECL extension
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.2'
+
+ - uses: actions/checkout@v3
+
+ - name: Validate composer.json and composer.lock
+ run: composer validate --strict
+
+ - name: Cache Composer packages
+ id: composer-cache
+ uses: actions/cache@v3
+ with:
+ path: vendor
+ key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-php-
+
+ - name: Install dependencies
+ run: composer install --prefer-dist --no-progress
+
+ - name: Tests quality - infection
+ run: composer quality:infection
+
+ - name: Code quality - phpcs
+ run: composer quality:phpcs
+
+ - name: Code quality - phpstan
+ run: composer quality:phpstan
+
+ - name: Code quality - psalm
+ run: composer quality:psalm
+
+ - name: Code quality - phan
+ run: composer quality:phan-silent
+
+ - name: Code quality - phpmd
+ run: composer quality:phpmd
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e8d49ea
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+/.idea/
+/vendor/
+/doc/
+/tmp/
+/composer.lock
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..3e5a749
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Ingenioz IT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9499a4a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,38 @@
+IMAGE_NAME=php-app
+CONTAINER_NAME=php_app_container
+
+# Build the Docker image
+build:
+ @docker build -t $(IMAGE_NAME) -f docker/Dockerfile .
+
+# Start the Docker container. If it's already running, stop and remove the previous instance.
+start: remove
+ @docker run -d -p 8080:80 --name $(CONTAINER_NAME) $(IMAGE_NAME)
+
+# Stop the Docker container
+stop:
+ @docker ps -q --filter "name=$(CONTAINER_NAME)" | grep -q . && docker stop $(CONTAINER_NAME) || true
+
+# Restart the Docker container
+restart: stop start
+
+# Rebuild the Docker image and start the container
+rebuild: stop build start
+
+# Remove the Docker container
+remove: stop
+ @docker ps -a -q --filter "name=$(CONTAINER_NAME)" | grep -q . && docker rm $(CONTAINER_NAME) || true
+
+# Access the Docker image's CLI
+cli:
+ @docker exec -it $(CONTAINER_NAME) /bin/bash
+
+# Show the Docker container's logs
+logs:
+ @docker logs -f $(CONTAINER_NAME)
+
+# Clean the Docker system
+clean:
+ @docker system prune -f
+
+.PHONY: start stop restart rebuild remove cli logs clean
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..725dc13
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# Router
+
+A PHP router.
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..d8f9918
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,91 @@
+{
+ "name": "ingenioz-it/router",
+ "description": "A PHP router",
+ "type": "library",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "IngeniozIT",
+ "email": "contact@ingenioz.it"
+ }
+ ],
+ "minimum-stability": "dev",
+ "prefer-stable": true,
+ "require": {
+ "php": ">=8.2",
+ "psr/http-server-handler": "^1.0",
+ "psr/http-server-middleware": "^1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "*",
+ "squizlabs/php_codesniffer": "*",
+ "phpstan/phpstan": "*",
+ "vimeo/psalm": "*",
+ "phan/phan": "*",
+ "infection/infection": "*",
+ "phpmd/phpmd": "*",
+ "rector/rector": "*",
+ "ingenioz-it/http-message": "^2.0",
+ "ingenioz-it/edict": "^3.1"
+ },
+ "autoload": {
+ "psr-4": {
+ "IngeniozIT\\Router\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "IngeniozIT\\Router\\Tests\\": "tests/"
+ }
+ },
+ "scripts": {
+ "test": "vendor/bin/phpunit -c ./quality/phpunit.xml.dist",
+ "testdox": "@test --testdox",
+ "test-xdebug": "XDEBUG_MODE=coverage vendor/bin/phpunit -c ./quality/phpunit.xml.dist",
+ "coverage-html": "@test-xdebug --coverage-html ./doc",
+ "coverage-xml": "@test-xdebug --coverage-clover ./tmp/coverage.xml",
+ "quality:clean": "vendor/bin/phpcbf --standard=./quality/phpcs.xml.dist",
+ "quality:refactor": "vendor/bin/rector process --config ./quality/rector.php",
+ "quality:refactor-dry": "vendor/bin/rector process --config ./quality/rector.php --dry-run",
+ "quality:phpcs": "vendor/bin/phpcs --standard=./quality/phpcs.xml.dist",
+ "quality:phpstan": "vendor/bin/phpstan analyze -c ./quality/phpstan.neon.dist",
+ "quality:psalm": "vendor/bin/psalm --no-cache --config ./quality/psalm.xml.dist",
+ "quality:phan": "vendor/bin/phan --config-file ./quality/phan.php",
+ "quality:phan-silent": "vendor/bin/phan --no-progress-bar --config-file ./quality/phan.php",
+ "quality:infection": "vendor/bin/infection --configuration=./quality/infection.json.dist",
+ "quality:phpmd": "vendor/bin/phpmd src/,tests/ text quality/phpmd.xml.dist",
+ "fulltest": [
+ "@test",
+ "@quality:infection",
+ "@quality:phpcs",
+ "@quality:phpmd",
+ "@quality:phpstan",
+ "@quality:psalm",
+ "@quality:phan",
+ "echo 'OK'"
+ ]
+ },
+ "scripts-descriptions": {
+ "test": "Run unit tests",
+ "testdox": "Run unit tests with testdox output",
+ "test-xdebug": "Run unit tests with Xdebug enabled",
+ "coverage-html": "Generate HTML code coverage report",
+ "coverage-xml": "Generate XML code coverage report",
+ "quality:clean": "Clean code with PHP Code Beautifier and Fixer",
+ "quality:refactor": "Refactor code with Rector",
+ "quality:refactor-dry": "Dry-run Rector",
+ "quality:phpcs": "Run PHP Code Sniffer",
+ "quality:phpstan": "Run PHPStan",
+ "quality:psalm": "Run Psalm",
+ "quality:phan": "Run Phan",
+ "quality:phan-silent": "Run Phan without progress bar",
+ "quality:infection": "Run Infection",
+ "quality:phpmd": "Run PHP Mess Detector",
+ "fulltest": "Run all tests"
+ },
+ "config": {
+ "allow-plugins": {
+ "infection/extension-installer": true
+ }
+ }
+}
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..d9d0b05
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,60 @@
+# Use the official PHP 8.2 image with Apache
+FROM php:8.2-apache
+
+# Install system dependencies and PHP extensions required for most PHP projects
+RUN apt-get update && apt-get install -y \
+ libpng-dev \
+ libjpeg62-turbo-dev \
+ libfreetype6-dev \
+ libzip-dev \
+ unzip \
+ git \
+ && docker-php-ext-configure gd --with-freetype --with-jpeg \
+ && docker-php-ext-install -j$(nproc) gd pdo pdo_mysql zip
+
+# Install Xdebug
+RUN pecl install xdebug && docker-php-ext-enable xdebug
+
+# Install php-ast extension
+RUN pecl install ast && docker-php-ext-enable ast
+
+# Configure Xdebug (customize this to your needs)
+RUN echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
+ && echo "xdebug.client_host=host.docker.internal" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
+ && echo "xdebug.client_port=9003" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \
+ && echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+
+# Install Composer globally
+RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
+
+# Set the COMPOSER_ALLOW_SUPERUSER environment variable
+ENV COMPOSER_ALLOW_SUPERUSER 1
+
+# Set the working directory inside the container
+WORKDIR /var/www/html
+
+# Copy the project's composer.json and composer.lock files to the working directory
+COPY composer.json ./
+
+# Install project dependencies
+RUN composer install --no-scripts --no-autoloader --no-dev
+
+# Copy the rest of the project to the working directory
+COPY . .
+
+# Finish composer
+RUN composer dump-autoload --optimize
+
+# Apache configuration: Enable mod_rewrite
+RUN a2enmod rewrite
+
+# Create a non-root user and switch to it
+RUN useradd -m appuser
+RUN groupadd appgroup
+RUN chown -R appuser:appgroup /var/www/html
+USER appuser
+
+# Expose port 80 for Apache
+EXPOSE 80
+
+HEALTHCHECK CMD curl --fail http://localhost:80/ || exit 1
\ No newline at end of file
diff --git a/quality/infection.json.dist b/quality/infection.json.dist
new file mode 100644
index 0000000..210182f
--- /dev/null
+++ b/quality/infection.json.dist
@@ -0,0 +1,15 @@
+{
+ "$schema": "https://raw.githubusercontent.com/infection/infection/0.26.1/resources/schema.json",
+ "source": {
+ "directories": [
+ "./src"
+ ]
+ },
+ "logs": {
+ "text": "../tmp/infection.txt",
+ "html": "../tmp/infection.html"
+ },
+ "mutators": {
+ "@default": true
+ }
+}
\ No newline at end of file
diff --git a/quality/phan.php b/quality/phan.php
new file mode 100644
index 0000000..421b63b
--- /dev/null
+++ b/quality/phan.php
@@ -0,0 +1,34 @@
+ '8.2',
+ 'directory_list' => [
+ 'src/',
+ 'tests/',
+ 'vendor/',
+ ],
+ 'exclude_analysis_directory_list' => [
+ 'vendor/',
+ ],
+ 'plugins' => [
+ 'AlwaysReturnPlugin',
+ 'DuplicateArrayKeyPlugin',
+ 'PregRegexCheckerPlugin',
+ 'PrintfCheckerPlugin',
+ 'UnreachableCodePlugin',
+ 'InvokePHPNativeSyntaxCheckPlugin',
+ 'PHPUnitAssertionPlugin',
+ 'EmptyStatementListPlugin',
+ 'LoopVariableReusePlugin',
+ 'RedundantAssignmentPlugin',
+ 'PHPUnitNotDeadCodePlugin',
+ 'WhitespacePlugin',
+ 'PHPDocRedundantPlugin',
+ ],
+ 'exclude_file_regex' => '#vendor/rector/rector/stubs-rector/.*#',
+ 'plugin_config' => [
+ 'php_native_syntax_check_max_processes' => 4,
+ ],
+ 'suppress_issue_types' => [
+ ],
+];
diff --git a/quality/phpcs.xml.dist b/quality/phpcs.xml.dist
new file mode 100644
index 0000000..c36f25b
--- /dev/null
+++ b/quality/phpcs.xml.dist
@@ -0,0 +1,16 @@
+
+
+ ../src
+ ../tests
+
+
+
+
+
+
+
+ error
+
+
+
+
\ No newline at end of file
diff --git a/quality/phpmd.xml.dist b/quality/phpmd.xml.dist
new file mode 100644
index 0000000..f731e70
--- /dev/null
+++ b/quality/phpmd.xml.dist
@@ -0,0 +1,18 @@
+
+
+
+ All default rulesets from PHPMD.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/quality/phpstan.neon.dist b/quality/phpstan.neon.dist
new file mode 100644
index 0000000..97c5f0c
--- /dev/null
+++ b/quality/phpstan.neon.dist
@@ -0,0 +1,6 @@
+parameters:
+ level: max
+ paths:
+ - ../src
+ - ../tests
+ ignoreErrors:
\ No newline at end of file
diff --git a/quality/phpunit.xml.dist b/quality/phpunit.xml.dist
new file mode 100644
index 0000000..c1932e0
--- /dev/null
+++ b/quality/phpunit.xml.dist
@@ -0,0 +1,25 @@
+
+
+
+
+
+ ../tests/
+
+
+
+
+
diff --git a/quality/psalm.xml.dist b/quality/psalm.xml.dist
new file mode 100644
index 0000000..864300c
--- /dev/null
+++ b/quality/psalm.xml.dist
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/quality/rector.php b/quality/rector.php
new file mode 100644
index 0000000..1c6e398
--- /dev/null
+++ b/quality/rector.php
@@ -0,0 +1,26 @@
+paths([
+ __DIR__ . '/../src',
+ __DIR__ . '/../tests',
+ ]);
+
+ $rectorConfig->sets([
+ LevelSetList::UP_TO_PHP_82,
+ SetList::CODE_QUALITY,
+ SetList::CODING_STYLE,
+ SetList::DEAD_CODE,
+ SetList::STRICT_BOOLEANS,
+ SetList::GMAGICK_TO_IMAGICK,
+ SetList::PRIVATIZATION,
+ SetList::TYPE_DECLARATION,
+ SetList::EARLY_RETURN,
+ SetList::INSTANCEOF,
+ ]);
+};