diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml new file mode 100644 index 00000000..3098877a --- /dev/null +++ b/.github/workflows/bench.yml @@ -0,0 +1,22 @@ +name: "Benchmarks" + +on: [pull_request] +jobs: + lint: + name: Benchmarks + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + + - name: Install dependencies + run: composer install --prefer-dist + + - name: Run Benchmarks + run: composer bench -- --progress=plain \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..bcb85b66 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,22 @@ +name: "Linter" + +on: [pull_request] +jobs: + lint: + name: Linter + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + + - name: Install dependencies + run: composer install --prefer-dist + + - name: Run Linter + run: composer lint \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..cc41aa6f --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,25 @@ +name: "Tests" + +on: [pull_request] +jobs: + lint: + name: Tests + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.0' + + - name: Setup Docker + run: docker-compose up -d --build + + - name: Wait for Server to be ready + run: sleep 10 + + - name: Run Tests + run: docker compose exec web vendor/bin/phpunit --configuration phpunit.xml \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 14df38c0..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,26 +0,0 @@ -# This file is a template, and might need editing before it works on your project. -# Select image from https://hub.docker.com/_/php/ -image: php:7.1.1 - -# Select what we should cache between builds -cache: - paths: - - vendor/ - -before_script: -- apt-get update -yqq -- apt-get install -yqq git -# Install PHP extensions -- docker-php-ext-install mbstring json opcache -# Install & enable Xdebug for code coverage reports -- pecl install xdebug -- docker-php-ext-enable xdebug -# Install and run Composer -- curl -sS https://getcomposer.org/installer | php -- php composer.phar install - -# Run our tests -# If Xdebug was installed you can generate a coverage report and see code coverage metrics. -test: - script: - - vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ef34aadb..00000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -dist: focal - -arch: - - amd64 - -os: linux - -language: shell - -notifications: - email: - - team@appwrite.io - -services: - - docker - -before_install: - - curl -fsSL https://get.docker.com | sh - - echo '{"experimental":"enabled"}' | sudo tee /etc/docker/daemon.json - - mkdir -p $HOME/.docker - - echo '{"experimental":"enabled"}' | sudo tee $HOME/.docker/config.json - - sudo service docker start - - > - if [ ! -z "${DOCKERHUB_PULL_USERNAME:-}" ]; then - echo "${DOCKERHUB_PULL_PASSWORD}" | docker login --username "${DOCKERHUB_PULL_USERNAME}" --password-stdin - fi - - docker --version - -install: - - docker-compose up -d - - sleep 10 - -script: - - docker ps -a - - docker-compose exec web vendor/bin/phpunit --configuration phpunit.xml - - docker-compose exec web vendor/bin/psalm --show-info=true diff --git a/Dockerfile b/Dockerfile index c9ae2094..54c948a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,7 +21,7 @@ ENV DEBIAN_FRONTEND=noninteractive \ RUN \ apk add --no-cache --virtual .deps \ - supervisor php$PHP_VERSION php$PHP_VERSION-fpm nginx bash + supervisor php$PHP_VERSION php$PHP_VERSION-fpm php$PHP_VERSION-mbstring nginx bash # Nginx Configuration (with self-signed ssl certificates) @@ -38,7 +38,7 @@ COPY ./tests/docker/start /usr/local/bin/start COPY ./src /usr/share/nginx/html/src COPY ./tests /usr/share/nginx/html/tests COPY ./phpunit.xml /usr/share/nginx/html/phpunit.xml -COPY ./psalm.xml /usr/share/nginx/html/psalm.xml +COPY ./phpbench.json /usr/share/nginx/html/phpbench.json COPY --from=step0 /usr/local/src/vendor /usr/share/nginx/html/vendor # Supervisord Conf diff --git a/composer.json b/composer.json index 3fb769b5..fe84d0d0 100644 --- a/composer.json +++ b/composer.json @@ -2,24 +2,32 @@ "name": "utopia-php/framework", "description": "A simple, light and advanced PHP framework", "type": "library", - "keywords": ["php","framework", "upf"], + "keywords": [ + "php", + "framework", + "upf" + ], "license": "MIT", "minimum-stability": "stable", "autoload": { - "psr-4": {"Utopia\\": "src/"} + "psr-4": { + "Utopia\\": "src/" + } }, "scripts": { - "lint": "./vendor/bin/pint --test", - "format": "./vendor/bin/pint", - "check": "./vendor/bin/phpstan analyse -l 5 src tests" + "lint": "vendor/bin/pint --test", + "format": "vendor/bin/pint", + "check": "vendor/bin/phpstan analyse -c phpstan.neon", + "test": "vendor/bin/phpunit --configuration phpunit.xml", + "bench": "vendor/bin/phpbench run --report=benchmark" }, "require": { "php": ">=8.0" }, "require-dev": { "phpunit/phpunit": "^9.5.25", - "vimeo/psalm": "4.27.0", "laravel/pint": "^1.2", - "phpstan/phpstan": "1.9.x-dev" + "phpstan/phpstan": "^1.10", + "phpbench/phpbench": "^1.2" } } diff --git a/composer.lock b/composer.lock index 6f8a74f3..812fe97b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,349 +4,44 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f89715e407bde4aecfdff0a0961815de", + "content-hash": "d41ea47cadd897ad4053410229874733", "packages": [], "packages-dev": [ { - "name": "amphp/amp", - "version": "v2.6.2", + "name": "doctrine/annotations", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/amphp/amp.git", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + "url": "https://github.com/doctrine/annotations.git", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", - "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1", - "ext-json": "*", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^7 | ^8 | ^9", - "psalm/phar": "^3.11@dev", - "react/promise": "^2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - } - }, - "autoload": { - "files": [ - "lib/functions.php", - "lib/Internal/functions.php" - ], - "psr-4": { - "Amp\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Daniel Lowrey", - "email": "rdlowrey@php.net" - }, - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Bob Weinand", - "email": "bobwei9@hotmail.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A non-blocking concurrency framework for PHP applications.", - "homepage": "https://amphp.org/amp", - "keywords": [ - "async", - "asynchronous", - "awaitable", - "concurrency", - "event", - "event-loop", - "future", - "non-blocking", - "promise" - ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/amp/issues", - "source": "https://github.com/amphp/amp/tree/v2.6.2" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2022-02-20T17:52:18+00:00" - }, - { - "name": "amphp/byte-stream", - "version": "v1.8.1", - "source": { - "type": "git", - "url": "https://github.com/amphp/byte-stream.git", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", - "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", - "shasum": "" - }, - "require": { - "amphp/amp": "^2", - "php": ">=7.1" - }, - "require-dev": { - "amphp/php-cs-fixer-config": "dev-master", - "amphp/phpunit-util": "^1.4", - "friendsofphp/php-cs-fixer": "^2.3", - "jetbrains/phpstorm-stubs": "^2019.3", - "phpunit/phpunit": "^6 || ^7 || ^8", - "psalm/phar": "^3.11.4" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "files": [ - "lib/functions.php" - ], - "psr-4": { - "Amp\\ByteStream\\": "lib" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Aaron Piotrowski", - "email": "aaron@trowski.com" - }, - { - "name": "Niklas Keller", - "email": "me@kelunik.com" - } - ], - "description": "A stream abstraction to make working with non-blocking I/O simple.", - "homepage": "http://amphp.org/byte-stream", - "keywords": [ - "amp", - "amphp", - "async", - "io", - "non-blocking", - "stream" - ], - "support": { - "irc": "irc://irc.freenode.org/amphp", - "issues": "https://github.com/amphp/byte-stream/issues", - "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" - }, - "funding": [ - { - "url": "https://github.com/amphp", - "type": "github" - } - ], - "time": "2021-03-30T17:13:30+00:00" - }, - { - "name": "composer/package-versions-deprecated", - "version": "1.11.99.5", - "source": { - "type": "git", - "url": "https://github.com/composer/package-versions-deprecated.git", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/package-versions-deprecated/zipball/b4f54f74ef3453349c24a845d22392cd31e65f1d", - "reference": "b4f54f74ef3453349c24a845d22392cd31e65f1d", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^1.1.0 || ^2.0", - "php": "^7 || ^8" - }, - "replace": { - "ocramius/package-versions": "1.11.99" - }, - "require-dev": { - "composer/composer": "^1.9.3 || ^2.0@dev", - "ext-zip": "^1.13", - "phpunit/phpunit": "^6.5 || ^7" - }, - "type": "composer-plugin", - "extra": { - "class": "PackageVersions\\Installer", - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "PackageVersions\\": "src/PackageVersions" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be" - } - ], - "description": "Composer plugin that provides efficient querying for installed package versions (no runtime IO)", - "support": { - "issues": "https://github.com/composer/package-versions-deprecated/issues", - "source": "https://github.com/composer/package-versions-deprecated/tree/1.11.99.5" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-01-17T14:14:24+00:00" - }, - { - "name": "composer/pcre", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" + "doctrine/lexer": "^2 || ^3", + "ext-tokenizer": "*", + "php": "^7.2 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6", + "vimeo/psalm": "^4.10" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Pcre\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" - ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.0" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-11-17T09:50:14+00:00" - }, - { - "name": "composer/semver", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", - "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, "autoload": { "psr-4": { - "Composer\\Semver\\": "src" + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "notification-url": "https://packagist.org/downloads/", @@ -355,151 +50,38 @@ ], "authors": [ { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" }, { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Roman Borschel", + "email": "roman@code-factory.org" }, { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.2" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" }, { - "url": "https://github.com/composer", - "type": "github" + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" }, { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-04-01T19:23:25+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ced299686f41dce890debac69273b47ffe98a40c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", - "reference": "ced299686f41dce890debac69273b47ffe98a40c", - "shasum": "" - }, - "require": { - "composer/pcre": "^1 || ^2 || ^3", - "php": "^7.2.5 || ^8.0", - "psr/log": "^1 || ^2 || ^3" - }, - "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^6.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "Restarts a process without Xdebug.", + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ - "Xdebug", - "performance" + "annotations", + "docblock", + "parser" ], "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/2.0.1" }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-02-25T21:32:43+00:00" - }, - { - "name": "dnoegel/php-xdg-base-dir", - "version": "v0.1.1", - "source": { - "type": "git", - "url": "https://github.com/dnoegel/php-xdg-base-dir.git", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/dnoegel/php-xdg-base-dir/zipball/8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "reference": "8f8a6e48c5ecb0f991c2fdcf5f154a47d85f9ffd", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "require-dev": { - "phpunit/phpunit": "~7.0|~6.0|~5.0|~4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "XdgBaseDir\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "implementation of xdg base directory specification for php", - "support": { - "issues": "https://github.com/dnoegel/php-xdg-base-dir/issues", - "source": "https://github.com/dnoegel/php-xdg-base-dir/tree/v0.1.1" - }, - "time": "2019-12-04T15:06:13+00:00" + "time": "2023-02-02T22:02:53+00:00" }, { "name": "doctrine/deprecations", @@ -546,30 +128,30 @@ }, { "name": "doctrine/instantiator", - "version": "2.0.0", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", - "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", + "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11", + "doctrine/coding-standard": "^9 || ^11", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^1.2", - "phpstan/phpstan": "^1.9.4", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5.27", - "vimeo/psalm": "^5.4" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.30 || ^5.4" }, "type": "library", "autoload": { @@ -596,7 +178,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + "source": "https://github.com/doctrine/instantiator/tree/1.5.0" }, "funding": [ { @@ -612,121 +194,98 @@ "type": "tidelift" } ], - "time": "2022-12-30T00:23:10+00:00" + "time": "2022-12-30T00:15:36+00:00" }, { - "name": "felixfbecker/advanced-json-rpc", - "version": "v3.2.1", + "name": "doctrine/lexer", + "version": "2.1.0", "source": { "type": "git", - "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", - "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + "url": "https://github.com/doctrine/lexer.git", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", - "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", "shasum": "" }, "require": { - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "php": "^7.1 || ^8.0", - "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + "doctrine/deprecations": "^1.0", + "php": "^7.1 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^7.0 || ^8.0" + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^4.11 || ^5.0" }, "type": "library", "autoload": { "psr-4": { - "AdvancedJsonRpc\\": "lib/" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "ISC" + "MIT" ], "authors": [ { - "name": "Felix Becker", - "email": "felix.b@outlook.com" - } - ], - "description": "A more advanced JSONRPC implementation", - "support": { - "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", - "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" - }, - "time": "2021-06-11T22:34:44+00:00" - }, - { - "name": "felixfbecker/language-server-protocol", - "version": "v1.5.2", - "source": { - "type": "git", - "url": "https://github.com/felixfbecker/php-language-server-protocol.git", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842", - "reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "require-dev": { - "phpstan/phpstan": "*", - "squizlabs/php_codesniffer": "^3.1", - "vimeo/psalm": "^4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "LanguageServerProtocol\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, { - "name": "Felix Becker", - "email": "felix.b@outlook.com" + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "PHP classes for the Language Server Protocol", + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ - "language", - "microsoft", - "php", - "server" + "annotations", + "docblock", + "lexer", + "parser", + "php" ], "support": { - "issues": "https://github.com/felixfbecker/php-language-server-protocol/issues", - "source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2" + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/2.1.0" }, - "time": "2022-03-02T22:36:06+00:00" + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2022-12-14T08:49:07+00:00" }, { "name": "laravel/pint", - "version": "v1.8.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "4b8f2ef22bfcddd1d163cfd282e3f42ee1a5cce2" + "reference": "e0a8cef58b74662f27355be9cdea0e726bbac362" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/4b8f2ef22bfcddd1d163cfd282e3f42ee1a5cce2", - "reference": "4b8f2ef22bfcddd1d163cfd282e3f42ee1a5cce2", + "url": "https://api.github.com/repos/laravel/pint/zipball/e0a8cef58b74662f27355be9cdea0e726bbac362", + "reference": "e0a8cef58b74662f27355be9cdea0e726bbac362", "shasum": "" }, "require": { @@ -734,16 +293,16 @@ "ext-mbstring": "*", "ext-tokenizer": "*", "ext-xml": "*", - "php": "^8.1.0" + "php": "^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.16.0", - "illuminate/view": "^10.5.1", - "laravel-zero/framework": "^10.0.2", + "friendsofphp/php-cs-fixer": "^3.14.4", + "illuminate/view": "^9.51.0", + "laravel-zero/framework": "^9.2.0", "mockery/mockery": "^1.5.1", - "nunomaduro/larastan": "^2.5.1", + "nunomaduro/larastan": "^2.4.0", "nunomaduro/termwind": "^1.15.1", - "pestphp/pest": "^2.4.0" + "pestphp/pest": "^1.22.4" }, "bin": [ "builds/pint" @@ -779,7 +338,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2023-04-04T13:08:09+00:00" + "time": "2023-02-14T16:31:02+00:00" }, { "name": "myclabs/deep-copy", @@ -840,57 +399,6 @@ ], "time": "2023-03-08T13:26:56+00:00" }, - { - "name": "netresearch/jsonmapper", - "version": "v4.1.0", - "source": { - "type": "git", - "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "shasum": "" - }, - "require": { - "ext-json": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-spl": "*", - "php": ">=7.1" - }, - "require-dev": { - "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", - "squizlabs/php_codesniffer": "~3.5" - }, - "type": "library", - "autoload": { - "psr-0": { - "JsonMapper": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "OSL-3.0" - ], - "authors": [ - { - "name": "Christian Weiske", - "email": "cweiske@cweiske.de", - "homepage": "http://github.com/cweiske/jsonmapper/", - "role": "Developer" - } - ], - "description": "Map nested JSON structures onto PHP classes", - "support": { - "email": "cweiske@cweiske.de", - "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" - }, - "time": "2022-12-08T20:46:14+00:00" - }, { "name": "nikic/php-parser", "version": "v4.15.4", @@ -947,59 +455,6 @@ }, "time": "2023-03-05T19:49:14+00:00" }, - { - "name": "openlss/lib-array2xml", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/nullivex/lib-array2xml.git", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nullivex/lib-array2xml/zipball/a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "reference": "a91f18a8dfc69ffabe5f9b068bc39bb202c81d90", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "type": "library", - "autoload": { - "psr-0": { - "LSS": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "Apache-2.0" - ], - "authors": [ - { - "name": "Bryan Tong", - "email": "bryan@nullivex.com", - "homepage": "https://www.nullivex.com" - }, - { - "name": "Tony Butler", - "email": "spudz76@gmail.com", - "homepage": "https://www.nullivex.com" - } - ], - "description": "Array2XML conversion library credit to lalit.org", - "homepage": "https://www.nullivex.com", - "keywords": [ - "array", - "array conversion", - "xml", - "xml conversion" - ], - "support": { - "issues": "https://github.com/nullivex/lib-array2xml/issues", - "source": "https://github.com/nullivex/lib-array2xml/tree/master" - }, - "time": "2019-03-29T20:06:56+00:00" - }, { "name": "phar-io/manifest", "version": "2.0.3", @@ -1112,31 +567,37 @@ "time": "2022-02-21T01:04:05+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "name": "phpbench/container", + "version": "2.2.1", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "url": "https://github.com/phpbench/container.git", + "reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/phpbench/container/zipball/6d555ff7174fca13f9b1ec0b4a089ed41d0ab392", + "reference": "6d555ff7174fca13f9b1ec0b4a089ed41d0ab392", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "psr/container": "^1.0|^2.0", + "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8" }, "type": "library", "extra": { "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src/" + "PhpBench\\DependencyInjection\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1145,59 +606,49 @@ ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Daniel Leech", + "email": "daniel@dantleech.com" } ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], + "description": "Simple, configurable, service container.", "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "issues": "https://github.com/phpbench/container/issues", + "source": "https://github.com/phpbench/container/tree/2.2.1" }, - "time": "2020-06-27T09:03:43+00:00" + "time": "2022-01-25T10:17:35+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", + "name": "phpbench/dom", + "version": "0.3.3", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + "url": "https://github.com/phpbench/dom.git", + "reference": "786a96db538d0def931f5b19225233ec42ec7a72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "url": "https://api.github.com/repos/phpbench/dom/zipball/786a96db538d0def931f5b19225233ec42ec7a72", + "reference": "786a96db538d0def931f5b19225233ec42ec7a72", "shasum": "" }, "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" + "ext-dom": "*", + "php": "^7.3||^8.0" }, "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" + "friendsofphp/php-cs-fixer": "^3.14", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^8.0||^9.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.x-dev" + "dev-master": "1.0-dev" } }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "PhpBench\\Dom\\": "lib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1206,60 +657,82 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "name": "Daniel Leech", + "email": "daniel@dantleech.com" } ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "description": "DOM wrapper to simplify working with the PHP DOM implementation", "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + "issues": "https://github.com/phpbench/dom/issues", + "source": "https://github.com/phpbench/dom/tree/0.3.3" }, - "time": "2021-10-19T17:43:47+00:00" + "time": "2023-03-06T23:46:57+00:00" }, { - "name": "phpdocumentor/type-resolver", - "version": "1.7.1", + "name": "phpbench/phpbench", + "version": "1.2.10", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714" + "url": "https://github.com/phpbench/phpbench.git", + "reference": "95206f92479674599a75e02b74b9933e2d9883aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714", - "reference": "dfc078e8af9c99210337325ff5aa152872c98714", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/95206f92479674599a75e02b74b9933e2d9883aa", + "reference": "95206f92479674599a75e02b74b9933e2d9883aa", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", + "doctrine/annotations": "^1.13 || ^2.0", + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "ext-tokenizer": "*", "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpbench/container": "^2.1", + "phpbench/dom": "~0.3.3", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "seld/jsonlint": "^1.1", + "symfony/console": "^4.2 || ^5.0 || ^6.0", + "symfony/filesystem": "^4.2 || ^5.0 || ^6.0", + "symfony/finder": "^4.2 || ^5.0 || ^6.0", + "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0", + "symfony/process": "^4.2 || ^5.0 || ^6.0", + "webmozart/glob": "^4.6" }, "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", + "dantleech/invoke": "^2.0", + "friendsofphp/php-cs-fixer": "^3.0", + "jangregor/phpstan-prophecy": "^1.0", + "phpspec/prophecy": "^1.12", "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.0", + "symfony/error-handler": "^5.2 || ^6.0", + "symfony/var-dumper": "^4.0 || ^5.0 || ^6.0" + }, + "suggest": { + "ext-xdebug": "For Xdebug profiling extension." }, + "bin": [ + "bin/phpbench" + ], "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev" + "dev-master": "1.2-dev" } }, "autoload": { + "files": [ + "lib/Report/Func/functions.php" + ], "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "PhpBench\\": "lib/", + "PhpBench\\Extensions\\XDebug\\": "extensions/xdebug/lib/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1268,74 +741,35 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Daniel Leech", + "email": "daniel@dantleech.com" } ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "description": "PHP Benchmarking Framework", "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.1" + "issues": "https://github.com/phpbench/phpbench/issues", + "source": "https://github.com/phpbench/phpbench/tree/1.2.10" }, - "time": "2023-03-27T19:02:04+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "1.18.0", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "882eabc9b6a12e25c27091a261397f9c8792e722" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/882eabc9b6a12e25c27091a261397f9c8792e722", - "reference": "882eabc9b6a12e25c27091a261397f9c8792e722", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] + "funding": [ + { + "url": "https://github.com/dantleech", + "type": "github" } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.18.0" - }, - "time": "2023-04-06T07:26:43+00:00" + "time": "2023-03-24T08:52:55+00:00" }, { "name": "phpstan/phpstan", - "version": "1.9.x-dev", + "version": "1.10.14", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "6c0217aa2b146c3e28474e8be3e87188fac55dac" + "reference": "d232901b09e67538e5c86a724be841bea5768a7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6c0217aa2b146c3e28474e8be3e87188fac55dac", - "reference": "6c0217aa2b146c3e28474e8be3e87188fac55dac", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/d232901b09e67538e5c86a724be841bea5768a7c", + "reference": "d232901b09e67538e5c86a724be841bea5768a7c", "shasum": "" }, "require": { @@ -1364,8 +798,11 @@ "static analysis" ], "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.9.x" + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" }, "funding": [ { @@ -1381,7 +818,7 @@ "type": "tidelift" } ], - "time": "2023-02-18T15:01:46+00:00" + "time": "2023-04-19T13:47:27+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1703,16 +1140,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.6", + "version": "9.6.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115" + "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b65d59a059d3004a040c16a82e07bbdf6cfdd115", - "reference": "b65d59a059d3004a040c16a82e07bbdf6cfdd115", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", + "reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2", "shasum": "" }, "require": { @@ -1786,7 +1223,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.6" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.7" }, "funding": [ { @@ -1798,11 +1235,60 @@ "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-04-14T08:58:40+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "time": "2023-03-27T11:43:46+00:00" + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" }, { "name": "psr/container", @@ -2799,128 +2285,388 @@ ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "seld/jsonlint", + "version": "1.9.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "4211420d25eba80712bff236a98960ef68b866b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/4211420d25eba80712bff236a98960ef68b866b7", + "reference": "4211420d25eba80712bff236a98960ef68b866b7", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.9.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2022-04-01T13:37:23+00:00" + }, + { + "name": "symfony/console", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/c3ebc83d031b71c39da318ca8b7a07ecc67507ed", + "reference": "c3ebc83d031b71c39da318ca8b7a07ecc67507ed", + "shasum": "" + }, + "require": { + "php": ">=8.0.2", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:36:10+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", + "shasum": "" + }, + "require": { + "php": ">=8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-03T06:13:03+00:00" + "time": "2022-01-02T09:55:41+00:00" }, { - "name": "sebastian/version", - "version": "3.0.2", + "name": "symfony/filesystem", + "version": "v6.0.19", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "url": "https://github.com/symfony/filesystem.git", + "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/3d49eec03fda1f0fc19b7349fbbe55ebc1004214", + "reference": "3d49eec03fda1f0fc19b7349fbbe55ebc1004214", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.0.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "source": "https://github.com/symfony/filesystem/tree/v6.0.19" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2023-01-20T17:44:14+00:00" }, { - "name": "symfony/console", - "version": "v6.2.8", + "name": "symfony/finder", + "version": "v6.0.19", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b" + "url": "https://github.com/symfony/finder.git", + "reference": "5cc9cac6586fc0c28cd173780ca696e419fefa11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3582d68a64a86ec25240aaa521ec8bc2342b369b", - "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b", + "url": "https://api.github.com/repos/symfony/finder/zipball/5cc9cac6586fc0c28cd173780ca696e419fefa11", + "reference": "5cc9cac6586fc0c28cd173780ca696e419fefa11", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.4|^6.0" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/event-dispatcher": "^5.4|^6.0", - "symfony/lock": "^5.4|^6.0", - "symfony/process": "^5.4|^6.0", - "symfony/var-dumper": "^5.4|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "php": ">=8.0.2" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Console\\": "" + "Symfony\\Component\\Finder\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -2940,16 +2686,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Eases the creation of beautiful and testable command line interfaces", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.8" + "source": "https://github.com/symfony/finder/tree/v6.0.19" }, "funding": [ { @@ -2965,38 +2705,33 @@ "type": "tidelift" } ], - "time": "2023-03-29T21:42:15+00:00" + "time": "2023-01-20T17:44:14+00:00" }, { - "name": "symfony/deprecation-contracts", - "version": "v3.2.1", + "name": "symfony/options-resolver", + "version": "v6.0.19", "source": { "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" + "url": "https://github.com/symfony/options-resolver.git", + "reference": "6a180d1c45e0d9797470ca9eb46215692de00fa3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/6a180d1c45e0d9797470ca9eb46215692de00fa3", + "reference": "6a180d1c45e0d9797470ca9eb46215692de00fa3", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.1|^3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.3-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" - } - }, "autoload": { - "files": [ - "function.php" + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3005,18 +2740,23 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "A generic function and convention to trigger deprecation notices", + "description": "Provides an improved replacement for the array_replace PHP function", "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/options-resolver/tree/v6.0.19" }, "funding": [ { @@ -3032,7 +2772,7 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:25:55+00:00" + "time": "2023-01-01T08:36:10+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3365,41 +3105,29 @@ "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", + "name": "symfony/process", + "version": "v6.0.19", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + "url": "https://github.com/symfony/process.git", + "reference": "2114fd60f26a296cc403a7939ab91478475a33d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "url": "https://api.github.com/repos/symfony/process/zipball/2114fd60f26a296cc403a7939ab91478475a33d4", + "reference": "2114fd60f26a296cc403a7939ab91478475a33d4", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.0.2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "Symfony\\Component\\Process\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -3408,28 +3136,18 @@ ], "authors": [ { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + "source": "https://github.com/symfony/process/tree/v6.0.19" }, "funding": [ { @@ -3445,24 +3163,24 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-01T08:36:10+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.2.1", + "version": "v3.0.2", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a" + "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66", + "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.0.2", "psr/container": "^2.0" }, "conflict": { @@ -3474,7 +3192,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.3-dev" + "dev-main": "3.0-dev" }, "thanks": { "name": "symfony/contracts", @@ -3484,10 +3202,7 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3514,7 +3229,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.0.2" }, "funding": [ { @@ -3530,24 +3245,24 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:32:47+00:00" + "time": "2022-05-30T19:17:58+00:00" }, { "name": "symfony/string", - "version": "v6.2.8", + "version": "v6.0.19", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef" + "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef", - "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef", + "url": "https://api.github.com/repos/symfony/string/zipball/d9e72497367c23e08bf94176d2be45b00a9d232a", + "reference": "d9e72497367c23e08bf94176d2be45b00a9d232a", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.0.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -3559,7 +3274,6 @@ "require-dev": { "symfony/error-handler": "^5.4|^6.0", "symfony/http-client": "^5.4|^6.0", - "symfony/intl": "^6.2", "symfony/translation-contracts": "^2.0|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, @@ -3600,7 +3314,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.8" + "source": "https://github.com/symfony/string/tree/v6.0.19" }, "funding": [ { @@ -3616,7 +3330,7 @@ "type": "tidelift" } ], - "time": "2023-03-20T16:06:02+00:00" + "time": "2023-01-01T08:36:10+00:00" }, { "name": "theseer/tokenizer", @@ -3669,201 +3383,35 @@ "time": "2021-07-28T10:34:58+00:00" }, { - "name": "vimeo/psalm", - "version": "4.27.0", - "source": { - "type": "git", - "url": "https://github.com/vimeo/psalm.git", - "reference": "faf106e717c37b8c81721845dba9de3d8deed8ff" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/faf106e717c37b8c81721845dba9de3d8deed8ff", - "reference": "faf106e717c37b8c81721845dba9de3d8deed8ff", - "shasum": "" - }, - "require": { - "amphp/amp": "^2.4.2", - "amphp/byte-stream": "^1.5", - "composer/package-versions-deprecated": "^1.8.0", - "composer/semver": "^1.4 || ^2.0 || ^3.0", - "composer/xdebug-handler": "^1.1 || ^2.0 || ^3.0", - "dnoegel/php-xdg-base-dir": "^0.1.1", - "ext-ctype": "*", - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-simplexml": "*", - "ext-tokenizer": "*", - "felixfbecker/advanced-json-rpc": "^3.0.3", - "felixfbecker/language-server-protocol": "^1.5", - "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", - "nikic/php-parser": "^4.13", - "openlss/lib-array2xml": "^1.0", - "php": "^7.1|^8", - "sebastian/diff": "^3.0 || ^4.0", - "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0", - "symfony/polyfill-php80": "^1.25", - "webmozart/path-util": "^2.3" - }, - "provide": { - "psalm/psalm": "self.version" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.2", - "brianium/paratest": "^4.0||^6.0", - "ext-curl": "*", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpdocumentor/reflection-docblock": "^5", - "phpmyadmin/sql-parser": "5.1.0||dev-master", - "phpspec/prophecy": ">=1.9.0", - "phpunit/phpunit": "^9.0", - "psalm/plugin-phpunit": "^0.16", - "slevomat/coding-standard": "^7.0", - "squizlabs/php_codesniffer": "^3.5", - "symfony/process": "^4.3 || ^5.0 || ^6.0", - "weirdan/prophecy-shim": "^1.0 || ^2.0" - }, - "suggest": { - "ext-curl": "In order to send data to shepherd", - "ext-igbinary": "^2.0.5 is required, used to serialize caching data" - }, - "bin": [ - "psalm", - "psalm-language-server", - "psalm-plugin", - "psalm-refactor", - "psalter" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.x-dev", - "dev-3.x": "3.x-dev", - "dev-2.x": "2.x-dev", - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "files": [ - "src/functions.php", - "src/spl_object_id.php" - ], - "psr-4": { - "Psalm\\": "src/Psalm/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthew Brown" - } - ], - "description": "A static analysis tool for finding errors in PHP applications", - "keywords": [ - "code", - "inspection", - "php" - ], - "support": { - "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/4.27.0" - }, - "time": "2022-08-31T13:47:09+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" - }, - { - "name": "webmozart/path-util", - "version": "2.3.0", + "name": "webmozart/glob", + "version": "4.6.0", "source": { "type": "git", - "url": "https://github.com/webmozart/path-util.git", - "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725" + "url": "https://github.com/webmozarts/glob.git", + "reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/path-util/zipball/d939f7edc24c9a1bb9c0dee5cb05d8e859490725", - "reference": "d939f7edc24c9a1bb9c0dee5cb05d8e859490725", + "url": "https://api.github.com/repos/webmozarts/glob/zipball/3c17f7dec3d9d0e87b575026011f2e75a56ed655", + "reference": "3c17f7dec3d9d0e87b575026011f2e75a56ed655", "shasum": "" }, "require": { - "php": ">=5.3.3", - "webmozart/assert": "~1.0" + "php": "^7.3 || ^8.0.0" }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^9.5", + "symfony/filesystem": "^5.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.3-dev" + "dev-master": "4.1-dev" } }, "autoload": { "psr-4": { - "Webmozart\\PathUtil\\": "src/" + "Webmozart\\Glob\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3876,20 +3424,17 @@ "email": "bschussek@gmail.com" } ], - "description": "A robust cross-platform utility for normalizing, comparing and modifying file paths.", + "description": "A PHP implementation of Ant's glob.", "support": { - "issues": "https://github.com/webmozart/path-util/issues", - "source": "https://github.com/webmozart/path-util/tree/2.3.0" + "issues": "https://github.com/webmozarts/glob/issues", + "source": "https://github.com/webmozarts/glob/tree/4.6.0" }, - "abandoned": "symfony/filesystem", - "time": "2015-12-17T08:42:14+00:00" + "time": "2022-05-24T19:45:58+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "phpstan/phpstan": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/docker-compose.yml b/docker-compose.yml index 55ad1dfc..849fa5ec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,4 +6,5 @@ services: ports: - "9020:80" volumes: + - ./src:/usr/share/nginx/html/src - ./tests:/usr/share/nginx/html/tests \ No newline at end of file diff --git a/phpbench.json b/phpbench.json new file mode 100644 index 00000000..adc40d12 --- /dev/null +++ b/phpbench.json @@ -0,0 +1,6 @@ +{ + "$schema":"vendor/phpbench/phpbench/phpbench.schema.json", + "runner.bootstrap": "vendor/autoload.php", + "runner.path": "tests", + "runner.file_pattern": "*Bench.php" +} \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 00000000..a76a8329 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + level: 5 + paths: + - src + - tests \ No newline at end of file diff --git a/pint.json b/pint.json new file mode 100644 index 00000000..c7819336 --- /dev/null +++ b/pint.json @@ -0,0 +1,3 @@ +{ + "preset": "psr12" +} \ No newline at end of file diff --git a/psalm.xml b/psalm.xml deleted file mode 100644 index 30258a70..00000000 --- a/psalm.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/src/App.php b/src/App.php index 2495a369..563d0a6d 100755 --- a/src/App.php +++ b/src/App.php @@ -7,41 +7,28 @@ class App /** * Request method constants */ - const REQUEST_METHOD_GET = 'GET'; + public const REQUEST_METHOD_GET = 'GET'; - const REQUEST_METHOD_POST = 'POST'; + public const REQUEST_METHOD_POST = 'POST'; - const REQUEST_METHOD_PUT = 'PUT'; + public const REQUEST_METHOD_PUT = 'PUT'; - const REQUEST_METHOD_PATCH = 'PATCH'; + public const REQUEST_METHOD_PATCH = 'PATCH'; - const REQUEST_METHOD_DELETE = 'DELETE'; + public const REQUEST_METHOD_DELETE = 'DELETE'; - const REQUEST_METHOD_OPTIONS = 'OPTIONS'; + public const REQUEST_METHOD_OPTIONS = 'OPTIONS'; - const REQUEST_METHOD_HEAD = 'HEAD'; + public const REQUEST_METHOD_HEAD = 'HEAD'; /** * Mode Type */ - const MODE_TYPE_DEVELOPMENT = 'development'; + public const MODE_TYPE_DEVELOPMENT = 'development'; - const MODE_TYPE_STAGE = 'stage'; + public const MODE_TYPE_STAGE = 'stage'; - const MODE_TYPE_PRODUCTION = 'production'; - - /** - * Routes - * - * @var array - */ - protected static array $routes = [ - self::REQUEST_METHOD_GET => [], - self::REQUEST_METHOD_POST => [], - self::REQUEST_METHOD_PUT => [], - self::REQUEST_METHOD_PATCH => [], - self::REQUEST_METHOD_DELETE => [], - ]; + public const MODE_TYPE_PRODUCTION = 'production'; /** * @var array @@ -98,13 +85,6 @@ class App */ protected static array $options = []; - /** - * Is Sorted? - * - * @var bool - */ - protected static bool $sorted = false; - /** * Route * @@ -122,15 +102,6 @@ class App */ protected static ?Route $wildcardRoute = null; - /** - * Matches - * - * Parameters matched from URL regex - * - * @var array - */ - protected array $matches = []; - /** * App * @@ -231,6 +202,7 @@ public static function init(): Hook { $hook = new Hook(); $hook->groups(['*']); + self::$init[] = $hook; return $hook; @@ -247,6 +219,7 @@ public static function shutdown(): Hook { $hook = new Hook(); $hook->groups(['*']); + self::$shutdown[] = $hook; return $hook; @@ -263,6 +236,7 @@ public static function options(): Hook { $hook = new Hook(); $hook->groups(['*']); + self::$options[] = $hook; return $hook; @@ -279,6 +253,7 @@ public static function error(): Hook { $hook = new Hook(); $hook->groups(['*']); + self::$errors[] = $hook; return $hook; @@ -338,9 +313,9 @@ public function getResource(string $name, bool $fresh = false): mixed return $this; } - if (! \array_key_exists($name, $this->resources) || $fresh || self::$resourcesCallbacks[$name]['reset']) { - if (! \array_key_exists($name, self::$resourcesCallbacks)) { - throw new Exception('Failed to find resource: "'.$name.'"'); + if (!\array_key_exists($name, $this->resources) || $fresh || self::$resourcesCallbacks[$name]['reset']) { + if (!\array_key_exists($name, self::$resourcesCallbacks)) { + throw new Exception('Failed to find resource: "' . $name . '"'); } $this->resources[$name] = \call_user_func_array( @@ -428,7 +403,7 @@ public static function isStage(): bool */ public static function getRoutes(): array { - return self::$routes; + return Router::getRoutes(); } /** @@ -446,7 +421,7 @@ public function getRoute(): ?Route * * @param Route $route */ - public function setRoute(Route $route): static + public function setRoute(Route $route): self { $this->route = $route; @@ -464,14 +439,9 @@ public function setRoute(Route $route): static */ public static function addRoute(string $method, string $url): Route { - if (! array_key_exists($method, self::$routes)) { - throw new Exception('Invalid Request Method'); - } $route = new Route($method, $url); - self::$routes[$method][$url] = $route; - - self::$sorted = false; + Router::addRoute($route); return $route; } @@ -487,7 +457,7 @@ public static function addRoute(string $method, string $url): Route */ public function match(Request $request, bool $fresh = false): ?Route { - if (null !== $this->route && ! $fresh) { + if (null !== $this->route && !$fresh) { return $this->route; } @@ -495,52 +465,7 @@ public function match(Request $request, bool $fresh = false): ?Route $method = $request->getMethod(); $method = (self::REQUEST_METHOD_HEAD == $method) ? self::REQUEST_METHOD_GET : $method; - if (! isset(self::$routes[$method])) { - self::$routes[$method] = []; - } - - foreach (self::$routes[$method] as $routeUrl => $route) { - /** @var Route $route */ - - if(str_ends_with($routeUrl, '/') && substr_count($routeUrl, '/') > 1) { - $routeUrl = rtrim($routeUrl, '/'); - } - - if(str_ends_with($url, '/') && substr_count($url, '/') > 1) { - $url = rtrim($url, '/'); - } - - // convert urls like '/users/:uid/posts/:pid' to regular expression - $regex = '@'.\preg_replace('@:[^/]+@', '([^/]+)', $routeUrl).'@'; - - // Check if the current request matches the expression - if (! \preg_match($regex, $url, $this->matches)) { - continue; - } - - // check for trailing wildcard - if (!str_ends_with($routeUrl, '/*')) { - // Check the paths have the same amount of segments - if (\substr_count($routeUrl, '/') !== \substr_count($url, '/')) { - continue; - } - } - - \array_shift($this->matches); - $this->route = $route; - - if (isset($route->getAliases()[$routeUrl])) { - $this->route->setAliasPath($routeUrl); - } else { - $this->route->setAliasPath(null); - } - - break; - } - - if (! empty($this->route) && ('/' === $this->route->getPath()) && ($url != $this->route->getPath())) { - return null; - } + $this->route = Router::match($method, $url); return $this->route; } @@ -553,25 +478,15 @@ public function match(Request $request, bool $fresh = false): ?Route */ public function execute(Route $route, Request $request): static { - $keys = []; $arguments = []; $groups = $route->getGroups(); + $pathValues = $route->getPathValues($request); - // Extract keys from URL - $url = $route->getIsAlias() ? $route->getAliasPath() : $route->getPath(); - $keyRegex = '@^'.\preg_replace('@:[^/]+@', ':([^/]+)', $url).'$@'; - \preg_match($keyRegex, $url, $keys); - - // Remove the first key and value ( corresponding to full regex match ) - \array_shift($keys); - - // combine keys and values to one array - $values = \array_combine($keys, $this->matches); try { if ($route->getHook()) { foreach (self::$init as $hook) { // Global init hooks if (in_array('*', $hook->getGroups())) { - $arguments = $this->getArguments($hook, $values, $request->getParams()); + $arguments = $this->getArguments($hook, $pathValues, $request->getParams()); \call_user_func_array($hook->getAction(), $arguments); } } @@ -580,26 +495,21 @@ public function execute(Route $route, Request $request): static foreach ($groups as $group) { foreach (self::$init as $hook) { // Group init hooks if (in_array($group, $hook->getGroups())) { - $arguments = $this->getArguments($hook, $values, $request->getParams()); + $arguments = $this->getArguments($hook, $pathValues, $request->getParams()); \call_user_func_array($hook->getAction(), $arguments); } } } - $arguments = $this->getArguments($route, $values, $request->getParams()); - - // Call the callback with the matched positions as params - if ($route->getIsActive()) { - \call_user_func_array($route->getAction(), $arguments); - } + $arguments = $this->getArguments($route, $pathValues, $request->getParams()); - $route->setIsActive(true); + // Call the action callback with the matched positions as params + \call_user_func_array($route->getAction(), $arguments); foreach ($groups as $group) { foreach (self::$shutdown as $hook) { // Group shutdown hooks - /** @var Hook $hook */ if (in_array($group, $hook->getGroups())) { - $arguments = $this->getArguments($hook, $values, $request->getParams()); + $arguments = $this->getArguments($hook, $pathValues, $request->getParams()); \call_user_func_array($hook->getAction(), $arguments); } } @@ -607,9 +517,8 @@ public function execute(Route $route, Request $request): static if ($route->getHook()) { foreach (self::$shutdown as $hook) { // Group shutdown hooks - /** @var Hook $hook */ if (in_array('*', $hook->getGroups())) { - $arguments = $this->getArguments($hook, $values, $request->getParams()); + $arguments = $this->getArguments($hook, $pathValues, $request->getParams()); \call_user_func_array($hook->getAction(), $arguments); } } @@ -619,26 +528,24 @@ public function execute(Route $route, Request $request): static foreach ($groups as $group) { foreach (self::$errors as $error) { // Group error hooks - /** @var Hook $error */ if (in_array($group, $error->getGroups())) { try { - $arguments = $this->getArguments($error, $values, $request->getParams()); + $arguments = $this->getArguments($error, $pathValues, $request->getParams()); \call_user_func_array($error->getAction(), $arguments); } catch (\Throwable $e) { - throw new Exception('Error handler had an error: '.$e->getMessage(), 500, $e); + throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); } } } } foreach (self::$errors as $error) { // Global error hooks - /** @var Hook $error */ if (in_array('*', $error->getGroups())) { try { - $arguments = $this->getArguments($error, $values, $request->getParams()); + $arguments = $this->getArguments($error, $pathValues, $request->getParams()); \call_user_func_array($error->getAction(), $arguments); } catch (\Throwable $e) { - throw new Exception('Error handler had an error: '.$e->getMessage(), 500, $e); + throw new Exception('Error handler had an error: ' . $e->getMessage(), 500, $e); } } } @@ -668,13 +575,6 @@ protected function getArguments(Hook $hook, array $values, array $requestParams) $arg = $existsInRequest ? $requestParams[$key] : $param['default']; $value = $existsInValues ? $values[$key] : $arg; - if ($hook instanceof Route) { - if ($hook->getIsAlias() && isset($hook->getAliasParams($hook->getAliasPath())[$key])) { - $value = $hook->getAliasParams($hook->getAliasPath())[$key]; - $paramExists = true; - } - } - if (!$param['skipValidation']) { if (!$paramExists && !$param['optional']) { throw new Exception('Param "' . $key . '" is not optional.', 400); @@ -718,39 +618,6 @@ public function run(Request $request, Response $response): static return $response; }); - /* - * Re-order array - * - * For route to work with similar links where one is shorter than other - * but both might match given pattern - */ - if (! self::$sorted) { - foreach (self::$routes as $method => $list) { //adding route alias in $routes - foreach ($list as $key => $route) { - foreach (array_keys($route->getAliases()) as $path) { - self::$routes[$method][$path] = $route; - } - } - } - foreach (self::$routes as $method => $list) { - \uksort(self::$routes[$method], function (string $a, string $b) { - return \strlen($b) - \strlen($a); - }); - - \uksort(self::$routes[$method], function (string $a, string $b) { - $result = \count(\explode('/', $b)) - \count(\explode('/', $a)); - - if ($result === 0) { - return \substr_count($a, ':') - \substr_count($b, ':'); - } - - return $result; - }); - } - - self::$sorted = true; - } - $method = $request->getMethod(); $route = $this->match($request); $groups = ($route instanceof Route) ? $route->getGroups() : []; @@ -805,7 +672,6 @@ public function run(Request $request, Response $response): static try { foreach ($groups as $group) { foreach (self::$options as $option) { // Group options hooks - /** @var Hook $option */ if (in_array($group, $option->getGroups())) { \call_user_func_array($option->getAction(), $this->getArguments($option, [], $request->getParams())); } @@ -813,14 +679,12 @@ public function run(Request $request, Response $response): static } foreach (self::$options as $option) { // Global options hooks - /** @var Hook $option */ if (in_array('*', $option->getGroups())) { \call_user_func_array($option->getAction(), $this->getArguments($option, [], $request->getParams())); } } } catch (\Throwable $e) { foreach (self::$errors as $error) { // Global error hooks - /** @var Hook $error */ if (in_array('*', $error->getGroups())) { self::setResource('error', function () use ($e) { return $e; @@ -872,28 +736,23 @@ protected function validate(string $key, array $param, mixed $value): void } if (!$validator->isValid($value)) { - throw new Exception('Invalid '.$key.': '.$validator->getDescription(), 400); + throw new Exception('Invalid ' . $key . ': ' . $validator->getDescription(), 400); } } /** * Reset all the static variables + * + * @return void */ public static function reset(): void { + Router::reset(); self::$resourcesCallbacks = []; self::$mode = ''; self::$errors = []; self::$init = []; self::$shutdown = []; self::$options = []; - self::$sorted = false; - self::$routes = [ - self::REQUEST_METHOD_GET => [], - self::REQUEST_METHOD_POST => [], - self::REQUEST_METHOD_PUT => [], - self::REQUEST_METHOD_PATCH => [], - self::REQUEST_METHOD_DELETE => [], - ]; } } diff --git a/src/Request.php b/src/Request.php index a1e4bd2a..474ca3ce 100755 --- a/src/Request.php +++ b/src/Request.php @@ -7,23 +7,23 @@ class Request /** * HTTP methods */ - const METHOD_OPTIONS = 'OPTIONS'; + public const METHOD_OPTIONS = 'OPTIONS'; - const METHOD_GET = 'GET'; + public const METHOD_GET = 'GET'; - const METHOD_HEAD = 'HEAD'; + public const METHOD_HEAD = 'HEAD'; - const METHOD_POST = 'POST'; + public const METHOD_POST = 'POST'; - const METHOD_PATCH = 'PATCH'; + public const METHOD_PATCH = 'PATCH'; - const METHOD_PUT = 'PUT'; + public const METHOD_PUT = 'PUT'; - const METHOD_DELETE = 'DELETE'; + public const METHOD_DELETE = 'DELETE'; - const METHOD_TRACE = 'TRACE'; + public const METHOD_TRACE = 'TRACE'; - const METHOD_CONNECT = 'CONNECT'; + public const METHOD_CONNECT = 'CONNECT'; /** * Container for raw php://input parsed stream diff --git a/src/Response.php b/src/Response.php index 826a2b07..742efd3b 100755 --- a/src/Response.php +++ b/src/Response.php @@ -7,125 +7,125 @@ class Response /** * HTTP content types */ - const CONTENT_TYPE_TEXT = 'text/plain'; + public const CONTENT_TYPE_TEXT = 'text/plain'; - const CONTENT_TYPE_HTML = 'text/html'; + public const CONTENT_TYPE_HTML = 'text/html'; - const CONTENT_TYPE_JSON = 'application/json'; + public const CONTENT_TYPE_JSON = 'application/json'; - const CONTENT_TYPE_XML = 'text/xml'; + public const CONTENT_TYPE_XML = 'text/xml'; - const CONTENT_TYPE_JAVASCRIPT = 'text/javascript'; + public const CONTENT_TYPE_JAVASCRIPT = 'text/javascript'; - const CONTENT_TYPE_IMAGE = 'image/*'; + public const CONTENT_TYPE_IMAGE = 'image/*'; - const CONTENT_TYPE_IMAGE_JPEG = 'image/jpeg'; + public const CONTENT_TYPE_IMAGE_JPEG = 'image/jpeg'; - const CONTENT_TYPE_IMAGE_PNG = 'image/png'; + public const CONTENT_TYPE_IMAGE_PNG = 'image/png'; - const CONTENT_TYPE_IMAGE_GIF = 'image/gif'; + public const CONTENT_TYPE_IMAGE_GIF = 'image/gif'; - const CONTENT_TYPE_IMAGE_SVG = 'image/svg+xml'; + public const CONTENT_TYPE_IMAGE_SVG = 'image/svg+xml'; - const CONTENT_TYPE_IMAGE_WEBP = 'image/webp'; + public const CONTENT_TYPE_IMAGE_WEBP = 'image/webp'; - const CONTENT_TYPE_IMAGE_ICON = 'image/x-icon'; + public const CONTENT_TYPE_IMAGE_ICON = 'image/x-icon'; - const CONTENT_TYPE_IMAGE_BMP = 'image/bmp'; + public const CONTENT_TYPE_IMAGE_BMP = 'image/bmp'; /** * Chrsets */ - const CHARSET_UTF8 = 'UTF-8'; + public const CHARSET_UTF8 = 'UTF-8'; /** * HTTP response status codes */ - const STATUS_CODE_CONTINUE = 100; + public const STATUS_CODE_CONTINUE = 100; - const STATUS_CODE_SWITCHING_PROTOCOLS = 101; + public const STATUS_CODE_SWITCHING_PROTOCOLS = 101; - const STATUS_CODE_OK = 200; + public const STATUS_CODE_OK = 200; - const STATUS_CODE_CREATED = 201; + public const STATUS_CODE_CREATED = 201; - const STATUS_CODE_ACCEPTED = 202; + public const STATUS_CODE_ACCEPTED = 202; - const STATUS_CODE_NON_AUTHORITATIVE_INFORMATION = 203; + public const STATUS_CODE_NON_AUTHORITATIVE_INFORMATION = 203; - const STATUS_CODE_NOCONTENT = 204; + public const STATUS_CODE_NOCONTENT = 204; - const STATUS_CODE_RESETCONTENT = 205; + public const STATUS_CODE_RESETCONTENT = 205; - const STATUS_CODE_PARTIALCONTENT = 206; + public const STATUS_CODE_PARTIALCONTENT = 206; - const STATUS_CODE_MULTIPLE_CHOICES = 300; + public const STATUS_CODE_MULTIPLE_CHOICES = 300; - const STATUS_CODE_MOVED_PERMANENTLY = 301; + public const STATUS_CODE_MOVED_PERMANENTLY = 301; - const STATUS_CODE_FOUND = 302; + public const STATUS_CODE_FOUND = 302; - const STATUS_CODE_SEE_OTHER = 303; + public const STATUS_CODE_SEE_OTHER = 303; - const STATUS_CODE_NOT_MODIFIED = 304; + public const STATUS_CODE_NOT_MODIFIED = 304; - const STATUS_CODE_USE_PROXY = 305; + public const STATUS_CODE_USE_PROXY = 305; - const STATUS_CODE_UNUSED = 306; + public const STATUS_CODE_UNUSED = 306; - const STATUS_CODE_TEMPORARY_REDIRECT = 307; + public const STATUS_CODE_TEMPORARY_REDIRECT = 307; - const STATUS_CODE_BAD_REQUEST = 400; + public const STATUS_CODE_BAD_REQUEST = 400; - const STATUS_CODE_UNAUTHORIZED = 401; + public const STATUS_CODE_UNAUTHORIZED = 401; - const STATUS_CODE_PAYMENT_REQUIRED = 402; + public const STATUS_CODE_PAYMENT_REQUIRED = 402; - const STATUS_CODE_FORBIDDEN = 403; + public const STATUS_CODE_FORBIDDEN = 403; - const STATUS_CODE_NOT_FOUND = 404; + public const STATUS_CODE_NOT_FOUND = 404; - const STATUS_CODE_METHOD_NOT_ALLOWED = 405; + public const STATUS_CODE_METHOD_NOT_ALLOWED = 405; - const STATUS_CODE_NOT_ACCEPTABLE = 406; + public const STATUS_CODE_NOT_ACCEPTABLE = 406; - const STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED = 407; + public const STATUS_CODE_PROXY_AUTHENTICATION_REQUIRED = 407; - const STATUS_CODE_REQUEST_TIMEOUT = 408; + public const STATUS_CODE_REQUEST_TIMEOUT = 408; - const STATUS_CODE_CONFLICT = 409; + public const STATUS_CODE_CONFLICT = 409; - const STATUS_CODE_GONE = 410; + public const STATUS_CODE_GONE = 410; - const STATUS_CODE_LENGTH_REQUIRED = 411; + public const STATUS_CODE_LENGTH_REQUIRED = 411; - const STATUS_CODE_PRECONDITION_FAILED = 412; + public const STATUS_CODE_PRECONDITION_FAILED = 412; - const STATUS_CODE_REQUEST_ENTITY_TOO_LARGE = 413; + public const STATUS_CODE_REQUEST_ENTITY_TOO_LARGE = 413; - const STATUS_CODE_REQUEST_URI_TOO_LONG = 414; + public const STATUS_CODE_REQUEST_URI_TOO_LONG = 414; - const STATUS_CODE_UNSUPPORTED_MEDIA_TYPE = 415; + public const STATUS_CODE_UNSUPPORTED_MEDIA_TYPE = 415; - const STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE = 416; + public const STATUS_CODE_REQUESTED_RANGE_NOT_SATISFIABLE = 416; - const STATUS_CODE_EXPECTATION_FAILED = 417; + public const STATUS_CODE_EXPECTATION_FAILED = 417; - const STATUS_CODE_TOO_EARLY = 425; + public const STATUS_CODE_TOO_EARLY = 425; - const STATUS_CODE_TOO_MANY_REQUESTS = 429; + public const STATUS_CODE_TOO_MANY_REQUESTS = 429; - const STATUS_CODE_INTERNAL_SERVER_ERROR = 500; + public const STATUS_CODE_INTERNAL_SERVER_ERROR = 500; - const STATUS_CODE_NOT_IMPLEMENTED = 501; + public const STATUS_CODE_NOT_IMPLEMENTED = 501; - const STATUS_CODE_BAD_GATEWAY = 502; + public const STATUS_CODE_BAD_GATEWAY = 502; - const STATUS_CODE_SERVICE_UNAVAILABLE = 503; + public const STATUS_CODE_SERVICE_UNAVAILABLE = 503; - const STATUS_CODE_GATEWAY_TIMEOUT = 504; + public const STATUS_CODE_GATEWAY_TIMEOUT = 504; - const STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED = 505; + public const STATUS_CODE_HTTP_VERSION_NOT_SUPPORTED = 505; /** * @var array @@ -194,13 +194,13 @@ class Response 'application/xml+rss' => true, ]; - const COOKIE_SAMESITE_NONE = 'None'; + public const COOKIE_SAMESITE_NONE = 'None'; - const COOKIE_SAMESITE_STRICT = 'Strict'; + public const COOKIE_SAMESITE_STRICT = 'Strict'; - const COOKIE_SAMESITE_LAX = 'Lax'; + public const COOKIE_SAMESITE_LAX = 'Lax'; - const CHUNK_SIZE = 2000000; //2mb + public const CHUNK_SIZE = 2000000; //2mb /** * @var int @@ -526,7 +526,7 @@ protected function end(string $content = null): void * * @param string $body * @param bool $end - * + * * @return void */ public function chunk(string $body = '', bool $end = false): void diff --git a/src/Route.php b/src/Route.php index cf6ba482..5936cbbc 100755 --- a/src/Route.php +++ b/src/Route.php @@ -26,52 +26,16 @@ class Route extends Hook protected string $path = ''; /** - * Alias path + * Path params. * - * @var null|string + * @var array */ - protected ?string $aliasPath = null; + protected array $pathParams = []; - /** - * Array of aliases where key is the path and value is an array of params - * - * @var array - */ - protected array $aliases = []; - - /** - * Is Alias Route? - * - * @var bool - */ - protected bool $isAlias = false; - - /** - * @var int - */ - public static int $counter = 0; - - /** - * @var int - */ - protected int $order; - - /** - * @var bool - */ - protected bool $isActive = true; - - /** - * @param string $method - * @param string $path - */ public function __construct(string $method, string $path) { - self::$counter++; - $this->path($path); $this->method = $method; - $this->order = self::$counter; $this->action = function (): void { }; } @@ -79,10 +43,10 @@ public function __construct(string $method, string $path) /** * Add path * - * @param string $path - * @return static + * @param string $path + * @return self */ - public function path(string $path): static + public function path(string $path): self { $this->path = $path; @@ -92,63 +56,29 @@ public function path(string $path): static /** * Add alias * - * @param string $path - * @param array $params - * @return static + * @param string $path + * @return self */ - public function alias(string $path, array $params = []): static + public function alias(string $path): self { - $this->aliases[$path] = $params; + Router::addRouteAlias($path, $this); return $this; } /** - * Set isActive - * - * @param bool $isActive - * @return void - */ - public function setIsActive(bool $isActive): void - { - $this->isActive = $isActive; - } - - /** - * Set isAlias - * - * @param bool $isAlias - * @return void - */ - public function setIsAlias(bool $isAlias): void - { - $this->isAlias = $isAlias; - } - - /** - * Set hook status * When set false, hooks for this route will be skipped. * - * @param bool $hook - * @return static + * @param bool $hook + * @return self */ - public function hook(bool $hook = true): static + public function hook(bool $hook = true): self { $this->hook = $hook; return $this; } - /** - * When set to false the route will be skipped - * - * @return bool - */ - public function getIsActive(): bool - { - return $this->isActive; - } - /** * Get HTTP Method * @@ -170,99 +100,44 @@ public function getPath(): string } /** - * Get Aliases - * - * Returns an array where the keys are paths and values are params - * - * @return array - */ - public function getAliases(): array - { - return $this->aliases; - } - - /** - * Get Alias path - * - * For backwards compatibility, returns the first alias path + * Get hook status * - * @return string + * @return bool */ - public function getAliasPath(): string + public function getHook(): bool { - if ($this->aliasPath !== null) { - return $this->aliasPath; - } - - $paths = array_keys($this->aliases); - if (count($paths) === 0) { - return ''; - } - - return $paths[0]; + return $this->hook; } /** - * Set Alias path - * - * For backwards compatibility, returns the first alias path + * Set path param. * + * @param string $key + * @param int $index * @return void */ - public function setAliasPath(?string $path): void + public function setPathParam(string $key, int $index): void { - $this->aliasPath = $path; - $this->setIsAlias($path !== null); + $this->pathParams[$key] = $index; } /** - * Get Alias Params - * - * For backwards compatibility, returns the first alias params if no path passed + * Get path params. * + * @param \Utopia\Request $request * @return array */ - public function getAliasParams(string $path = null): array + public function getPathValues(Request $request): array { - if ($path === null) { - $params = array_values($this->aliases); - if (count($params) === 0) { - return []; - } + $pathValues = []; + $parts = explode('/', ltrim($request->getURI(), '/')); - return $params[0]; + foreach ($this->pathParams as $key => $index) { + if (array_key_exists($index, $parts)) { + $pathValues[$key] = $parts[$index]; + } } - return $this->aliases[$path]; - } - - /** - * Get is Alias - * - * @return bool - */ - public function getIsAlias(): bool - { - return $this->isAlias; - } - - /** - * Get Route Order ID - * - * @return int - */ - public function getOrder(): int - { - return $this->order; - } - - /** - * Get hook status - * - * @return bool - */ - public function getHook(): bool - { - return $this->hook; + return $pathValues; } } diff --git a/src/Router.php b/src/Router.php new file mode 100644 index 00000000..b8820711 --- /dev/null +++ b/src/Router.php @@ -0,0 +1,210 @@ + + */ + protected static array $routes = [ + App::REQUEST_METHOD_GET => [], + App::REQUEST_METHOD_POST => [], + App::REQUEST_METHOD_PUT => [], + App::REQUEST_METHOD_PATCH => [], + App::REQUEST_METHOD_DELETE => [], + ]; + + /** + * Contains the positions of all params in the paths of all registered Routes. + * + * @var array + */ + protected static array $params = []; + + /** + * Get all registered routes. + * + * @return array + */ + public static function getRoutes(): array + { + return self::$routes; + } + + /** + * Add route to router. + * + * @param \Utopia\Route $route + * @return void + * @throws \Exception + */ + public static function addRoute(Route $route): void + { + [$path, $params] = self::preparePath($route->getPath()); + + if (!array_key_exists($route->getMethod(), self::$routes)) { + throw new Exception("Method ({$route->getMethod()}) not supported."); + } + + if (array_key_exists($path, self::$routes[$route->getMethod()])) { + throw new Exception("Route for ({$route->getMethod()}:{$path}) already registered."); + } + + foreach ($params as $key => $index) { + $route->setPathParam($key, $index); + } + + self::$routes[$route->getMethod()][$path] = $route; + } + + /** + * Add route to router. + * + * @param \Utopia\Route $route + * @return void + * @throws \Exception + */ + public static function addRouteAlias(string $path, Route $route): void + { + [$alias] = self::preparePath($path); + + if (array_key_exists($alias, self::$routes[$route->getMethod()])) { + throw new Exception("Route for ({$route->getMethod()}:{$alias}) already registered."); + } + + self::$routes[$route->getMethod()][$alias] = $route; + } + + /** + * Match route against the method and path. + * + * @param string $method + * @param string $path + * @return \Utopia\Route|null + */ + public static function match(string $method, string $path): Route|null + { + if (!array_key_exists($method, self::$routes)) { + return null; + } + + $parts = array_values(array_filter(explode('/', $path))); + $length = count($parts) - 1; + $filteredParams = array_filter(self::$params, fn ($i) => $i <= $length); + + foreach (self::combinations($filteredParams) as $sample) { + $sample = array_filter($sample, fn (int $i) => $i <= $length); + $match = implode( + '/', + array_replace( + $parts, + array_fill_keys($sample, self::PLACEHOLDER_TOKEN) + ) + ); + + if (array_key_exists($match, self::$routes[$method])) { + return self::$routes[$method][$match]; + } + } + + /** + * Match root wildcard. + */ + $match = self::WILDCARD_TOKEN; + if (array_key_exists($match, self::$routes[$method])) { + return self::$routes[$method][$match]; + } + + /** + * Match wildcard for path segments. + */ + foreach ($parts as $part) { + $current = ($current ?? '') . "{$part}/"; + $match = $current . self::WILDCARD_TOKEN; + if (array_key_exists($match, self::$routes[$method])) { + return self::$routes[$method][$match]; + } + } + + return null; + } + + /** + * Get all combinations of the given set. + * + * @param array $set + * @return iterable + */ + protected static function combinations(array $set): iterable + { + yield []; + + $results = [[]]; + + foreach ($set as $element) { + foreach ($results as $combination) { + $ret = array_merge([$element], $combination); + $results[] = $ret; + + yield $ret; + } + } + } + + /** + * Prepare path for matching + * + * @param string $path + * @return array + */ + protected static function preparePath(string $path): array + { + $parts = array_values(array_filter(explode('/', $path))); + $prepare = ''; + $params = []; + + foreach ($parts as $key => $part) { + if ($key !== 0) { + $prepare .= '/'; + } + + if (str_starts_with($part, ':')) { + $prepare .= self::PLACEHOLDER_TOKEN; + $params[ltrim($part, ':')] = $key; + if (!in_array($key, self::$params)) { + self::$params[] = $key; + } + } else { + $prepare .= $part; + } + } + + return [$prepare, $params]; + } + + /** + * Reset router + * + * @return void + */ + public static function reset(): void + { + self::$params = []; + self::$routes = [ + App::REQUEST_METHOD_GET => [], + App::REQUEST_METHOD_POST => [], + App::REQUEST_METHOD_PUT => [], + App::REQUEST_METHOD_PATCH => [], + App::REQUEST_METHOD_DELETE => [], + ]; + } +} diff --git a/src/Validator.php b/src/Validator.php index 12544466..d58dfea7 100755 --- a/src/Validator.php +++ b/src/Validator.php @@ -4,19 +4,19 @@ abstract class Validator { - const TYPE_BOOLEAN = 'boolean'; + public const TYPE_BOOLEAN = 'boolean'; - const TYPE_INTEGER = 'integer'; + public const TYPE_INTEGER = 'integer'; - const TYPE_FLOAT = 'double'; /* gettype() returns 'double' for historical reasons */ + public const TYPE_FLOAT = 'double'; /* gettype() returns 'double' for historical reasons */ - const TYPE_STRING = 'string'; + public const TYPE_STRING = 'string'; - const TYPE_ARRAY = 'array'; + public const TYPE_ARRAY = 'array'; - const TYPE_OBJECT = 'object'; + public const TYPE_OBJECT = 'object'; - const TYPE_MIXED = 'mixed'; + public const TYPE_MIXED = 'mixed'; /** * Get Description diff --git a/src/Validator/Hostname.php b/src/Validator/Hostname.php index 791b4233..0532baf1 100644 --- a/src/Validator/Hostname.php +++ b/src/Validator/Hostname.php @@ -96,13 +96,13 @@ public function isValid(mixed $value): bool } // If wildcard symbol used - if(\str_starts_with($allowedHostname, '*')) { + if (\str_starts_with($allowedHostname, '*')) { // Remove starting * symbol before comparing $allowedHostname = substr($allowedHostname, 1); // If rest of hostname match; allow // Notice allowedHostname still includes starting dot. Root domain is NOT allowed by wildcard. - if(\str_ends_with($value, $allowedHostname)) { + if (\str_ends_with($value, $allowedHostname)) { return true; } } diff --git a/src/Validator/IP.php b/src/Validator/IP.php index 3a69dc13..69e1f95c 100644 --- a/src/Validator/IP.php +++ b/src/Validator/IP.php @@ -66,7 +66,7 @@ public function isValid($value): bool if (\filter_var($value, FILTER_VALIDATE_IP)) { return true; } - break; + break; case self::V4: if (\filter_var($value, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { diff --git a/src/Validator/Nullable.php b/src/Validator/Nullable.php index a4440e22..3d0d8724 100644 --- a/src/Validator/Nullable.php +++ b/src/Validator/Nullable.php @@ -47,7 +47,7 @@ public function getType(): string } /** - * @return Validator + * @return Validator */ public function getValidator(): Validator { diff --git a/src/Validator/Range.php b/src/Validator/Range.php index d0e7f20f..063d6c8d 100755 --- a/src/Validator/Range.php +++ b/src/Validator/Range.php @@ -131,7 +131,7 @@ public function isValid(mixed $value): bool } break; case self::TYPE_FLOAT: - if (! is_numeric($value) ) { + if (! is_numeric($value)) { return false; } $value = $value + 0.0; diff --git a/src/Validator/Text.php b/src/Validator/Text.php index 18e6b5ea..49224e70 100644 --- a/src/Validator/Text.php +++ b/src/Validator/Text.php @@ -11,11 +11,11 @@ */ class Text extends Validator { - const NUMBERS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + public const NUMBERS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; - const ALPHABET_UPPER = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; + public const ALPHABET_UPPER = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']; - const ALPHABET_LOWER = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']; + public const ALPHABET_LOWER = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']; /** * @var int diff --git a/src/View.php b/src/View.php index 13e6f802..3445e8d8 100755 --- a/src/View.php +++ b/src/View.php @@ -6,9 +6,9 @@ class View { - const FILTER_ESCAPE = 'escape'; + public const FILTER_ESCAPE = 'escape'; - const FILTER_NL2P = 'nl2p'; + public const FILTER_NL2P = 'nl2p'; /** * @var self|null diff --git a/tests/AppTest.php b/tests/AppTest.php index 2fab7985..33d21d9d 100755 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -114,19 +114,6 @@ public function testCanGetResources(): void $this->assertEquals('x-def-y-def-'.$resource, $result); } - public function testCanAddRoute(): void - { - $getRoute = App::addRoute(App::REQUEST_METHOD_GET, '/addroute'); - $postRoute = App::addRoute(App::REQUEST_METHOD_POST, '/addroute'); - - $routes = App::getRoutes(); - $this->assertEquals($getRoute, $routes[App::REQUEST_METHOD_GET]['/addroute']); - $this->assertEquals($postRoute, $routes[App::REQUEST_METHOD_POST]['/addroute']); - - $this->expectExceptionMessage('Invalid Request Method'); - App::addRoute('REST', '/addroute'); - } - public function testCanExecuteRoute(): void { App::setResource('rand', fn () => rand()); @@ -143,7 +130,6 @@ public function testCanExecuteRoute(): void $route = new Route('GET', '/path'); $route - ->alias('/path1', ['x' => 'x-def-1', 'y' => 'y-def-1']) ->param('x', 'x-def', new Text(200), 'x param', true) ->param('y', 'y-def', new Text(200), 'y param', true) ->action(function ($x, $y) { @@ -155,17 +141,6 @@ public function testCanExecuteRoute(): void $result = \ob_get_contents(); \ob_end_clean(); - // test alias with param override - $route->setIsAlias(true); - - \ob_start(); - $this->app->execute($route, new Request()); - $result1 = \ob_get_contents(); - \ob_end_clean(); - - $this->assertEquals('x-def-y-def', $result); - $this->assertEquals('x-def-1-y-def-1', $result1); - // With Params $route = new Route('GET', '/path'); @@ -409,9 +384,7 @@ public function providerRouteMatching(): array 'DELETE request' => [App::REQUEST_METHOD_DELETE, '/path1'], '1 separators' => [App::REQUEST_METHOD_GET, '/a/'], '2 separators' => [App::REQUEST_METHOD_GET, '/a/b'], - '3 separators' => [App::REQUEST_METHOD_GET, '/a/b/c'], - 'trailing wildcard' => [App::REQUEST_METHOD_GET, '/wildcard/*', '/wildcard/lorem/ipsum'], - 'trailing wildcard - root' => [App::REQUEST_METHOD_GET, '/wildcard/*', '/wildcard'], + '3 separators' => [App::REQUEST_METHOD_GET, '/a/b/c'] ]; } @@ -535,75 +508,6 @@ public function testCanRunRequest(): void $this->assertStringNotContainsString('HELLO', $result); } - public function testCanRunAliasEndpoint(): void - { - // Test head requests - App::get('/storage/buckets/:bucketId/files/:fileId') - ->alias('/storage/files/:fileId', [ - 'bucketId' => 'default', - ]) - ->param('bucketId', 'bucketid', new Text(100), 'My id', false) - ->param('fileId', 'fileId', new Text(100), 'My id', false) - ->inject('response') - ->action(function ($bucketId, $fileId, $response) { - $response->send('HELLO'); - }); - - $_SERVER['REQUEST_METHOD'] = 'HEAD'; - $_SERVER['REQUEST_URI'] = '/storage/files/myfileid'; - - // Test Alias - \ob_start(); - $this->app->run(new Request(), new Response()); - $result1 = \ob_get_contents(); - \ob_end_clean(); - - $this->assertStringNotContainsString('HELLO', $result1); - } - - public function providerAliases(): array - { - return [ - '/real/:param1' => ['/real/p1', 'p1'], - '/alias' => ['/alias', 'default'], - '/another/:param1' => ['/another/a', 'a'], - '/param2' => ['/param2', 'param2'], - ]; - } - - /** - * @dataProvider providerAliases - */ - public function testMultipleAliases(string $path, string $expected): void - { - App::get('/real/:param1') - ->alias('/alias', [ - 'param1' => 'default', - ]) - ->alias('/another/:param1') - ->alias('/param2', [ - 'param1' => 'param2', - ]) - ->param('param1', '', new Text(100), 'a param', false) - ->inject('response') - ->action(function ($param1, $response) { - echo $param1; - }); - - $routes = App::getRoutes(); - $this->assertContains('/real/:param1', array_keys($routes[App::REQUEST_METHOD_GET])); - - $_SERVER['REQUEST_METHOD'] = 'GET'; - - $_SERVER['REQUEST_URI'] = $path; - \ob_start(); - $this->app->run(new Request(), new Response()); - $result = \ob_get_contents(); - \ob_end_clean(); - - $this->assertEquals($expected, $result); - } - public function testWildcardRoute(): void { $method = $_SERVER['REQUEST_METHOD'] ?? null; @@ -664,4 +568,4 @@ public function testWildcardRoute(): void $_SERVER['REQUEST_URI'] = $uri; } -} \ No newline at end of file +} diff --git a/tests/RouteTest.php b/tests/RouteTest.php index 4f2e5741..cbdea1c3 100755 --- a/tests/RouteTest.php +++ b/tests/RouteTest.php @@ -28,51 +28,6 @@ public function testCanGetAndSetPath() $this->assertEquals('/path', $this->route->getPath()); } - public function testCanSetAndGetAlias() - { - $this->assertEquals('', $this->route->getAliasPath()); - $this->assertEquals([], $this->route->getAliasParams()); - - $params = [ - 'pathId' => 'hello', - ]; - $this->route->alias('/path1', $params); - - $this->assertEquals('/path1', $this->route->getAliasPath()); - $this->assertEquals($params, $this->route->getAliasParams()); - } - - public function testCanSetAndGetAliases() - { - $this->assertEquals('', $this->route->getAliasPath()); - $this->assertEquals([], $this->route->getAliasParams()); - - $path1Params = [ - 'pathId' => 'hello', - ]; - $this->route->alias('/path1', $path1Params); - - $path2Params = [ - 'anotherPathId' => 'world', - ]; - $this->route->alias('/path2', $path2Params); - - $aliases = $this->route->getAliases(); - - $this->assertEquals( - [ - '/path1' => $path1Params, - '/path2' => $path2Params, - ], - $aliases - ); - - $this->assertEquals('/path1', $this->route->getAliasPath()); - $this->assertEquals($path1Params, $this->route->getAliasParams()); - $this->assertEquals($path1Params, $this->route->getAliasParams('/path1')); - $this->assertEquals($path2Params, $this->route->getAliasParams('/path2')); - } - public function testCanSetAndGetDescription() { $this->assertEquals('', $this->route->getDesc()); @@ -145,17 +100,6 @@ public function testCanSetAndGetHooks() $this->assertFalse($this->route->getHook()); } - public function testCanSetAndGetIsActive() - { - $this->assertTrue($this->route->getIsActive()); - $this->route->setIsActive(true); - $this->assertTrue($this->route->getIsActive()); - $this->route->setIsActive(false); - $this->assertFalse($this->route->getIsActive()); - $this->route->setIsActive(true); - $this->assertTrue($this->route->getIsActive()); - } - public function tearDown(): void { $this->route = null; diff --git a/tests/RouterBench.php b/tests/RouterBench.php new file mode 100644 index 00000000..466506d6 --- /dev/null +++ b/tests/RouterBench.php @@ -0,0 +1,58 @@ + '/blog', + 'nested' => '/blog/authors', + 'single param' => '/blog/lorem-ipsum', + 'single param with nested' => '/blog/lorem-ipsum/comments', + 'multiple params' => '/blog/lorem-ipsum/comments/1337', + 'long' => '/blog/lorem/ipsum/dolor/sit/amet/consectetur/adipiscing/elit/Quisque/dolor/nisi/gravida/non/malesuada/eget/tincidunt/vitae/eros/Donec/hendrerit/mollis/purus/non/efficitur/augue/efficitur/sed/Praesent/a/tempus/felis/et/elementum/lorem/Vestibulum/ante/ipsum/primis/in/faucibus/orci/luctus/et/ultrices/posuere/cubilia/curae/Ut/luctus/ultrices/ligula/vulputate/malesuada/magna/pellentesque/eget/Mauris/at/sodales/orci/Mauris/efficitur/volutpat/est/in/faucibus/Donec/non/eleifend/nibh/Nunc/cursus/ornare/sollicitudin/Nullam/pellentesque/placerat/justo/ac/eleifend/tortor/imperdiet/quis/Nullam/tincidunt/non/justo/ut/pulvinar/Suspendisse/laoreet/tempus/nulla/eu/aliquet/Proin/metus/erat/facilisis/in/euismod/sit/amet/mollis/ac/nisi/Nulla/facilisi' + ] as $name => $route) { + yield $name => ['route' => $route]; + } + } + + #[BeforeMethods('setUpRouter')] + #[AfterMethods('tearDown')] + #[Iterations(50)] + #[Assert('mode(variant.time.avg) < 0.1 ms')] + #[ParamProviders('provideRoutesToMatch')] + public function benchRouter(array $data): void + { + Router::match(App::REQUEST_METHOD_GET, $data['route']); + } +} diff --git a/tests/RouterTest.php b/tests/RouterTest.php new file mode 100644 index 00000000..fcaa93c2 --- /dev/null +++ b/tests/RouterTest.php @@ -0,0 +1,145 @@ +assertEquals($routeIndex, Router::match(App::REQUEST_METHOD_GET, '/')); + $this->assertEquals($routeAbout, Router::match(App::REQUEST_METHOD_GET, '/about')); + $this->assertEquals($routeAboutMe, Router::match(App::REQUEST_METHOD_GET, '/about/me')); + } + + public function testCanMatchUrlWithPlaceholder(): void + { + $routeBlog = new Route(App::REQUEST_METHOD_GET, '/blog'); + $routeBlogAuthors = new Route(App::REQUEST_METHOD_GET, '/blog/authors'); + $routeBlogAuthorsComments = new Route(App::REQUEST_METHOD_GET, '/blog/authors/comments'); + $routeBlogPost = new Route(App::REQUEST_METHOD_GET, '/blog/:post'); + $routeBlogPostComments = new Route(App::REQUEST_METHOD_GET, '/blog/:post/comments'); + $routeBlogPostCommentsSingle = new Route(App::REQUEST_METHOD_GET, '/blog/:post/comments/:comment'); + + Router::addRoute($routeBlog); + Router::addRoute($routeBlogAuthors); + Router::addRoute($routeBlogAuthorsComments); + Router::addRoute($routeBlogPost); + Router::addRoute($routeBlogPostComments); + Router::addRoute($routeBlogPostCommentsSingle); + + $this->assertEquals($routeBlog, Router::match(App::REQUEST_METHOD_GET, '/blog')); + $this->assertEquals($routeBlogAuthors, Router::match(App::REQUEST_METHOD_GET, '/blog/authors')); + $this->assertEquals($routeBlogAuthorsComments, Router::match(App::REQUEST_METHOD_GET, '/blog/authors/comments')); + $this->assertEquals($routeBlogPost, Router::match(App::REQUEST_METHOD_GET, '/blog/test')); + $this->assertEquals($routeBlogPostComments, Router::match(App::REQUEST_METHOD_GET, '/blog/test/comments')); + $this->assertEquals($routeBlogPostCommentsSingle, Router::match(App::REQUEST_METHOD_GET, '/blog/test/comments/:comment')); + } + + public function testCanMatchUrlWithWildcard(): void + { + $routeIndex = new Route('GET', '/'); + $routeAbout = new Route('GET', '/about'); + $routeAboutWildcard = new Route('GET', '/about/*'); + + Router::addRoute($routeIndex); + Router::addRoute($routeAbout); + Router::addRoute($routeAboutWildcard); + + $this->assertEquals($routeIndex, Router::match('GET', '/')); + $this->assertEquals($routeAbout, Router::match('GET', '/about')); + $this->assertEquals($routeAboutWildcard, Router::match('GET', '/about/me')); + $this->assertEquals($routeAboutWildcard, Router::match('GET', '/about/you')); + $this->assertEquals($routeAboutWildcard, Router::match('GET', '/about/me/myself/i')); + } + + public function testCanMatchHttpMethod(): void + { + $routeGET = new Route(App::REQUEST_METHOD_GET, '/'); + $routePOST = new Route(App::REQUEST_METHOD_POST, '/'); + + Router::addRoute($routeGET); + Router::addRoute($routePOST); + + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/')); + $this->assertEquals($routePOST, Router::match(App::REQUEST_METHOD_POST, '/')); + + $this->assertNotEquals($routeGET, Router::match(App::REQUEST_METHOD_POST, '/')); + $this->assertNotEquals($routePOST, Router::match(App::REQUEST_METHOD_GET, '/')); + } + + public function testCanMatchAlias(): void + { + $routeGET = new Route(App::REQUEST_METHOD_GET, '/target'); + $routeGET + ->alias('/alias') + ->alias('/alias2'); + + Router::addRoute($routeGET); + + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/target')); + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/alias')); + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/alias2')); + } + + public function testCanMatchMix(): void + { + $routeGET = new Route(App::REQUEST_METHOD_GET, '/'); + $routeGET + ->alias('/console/*') + ->alias('/auth/*') + ->alias('/invite') + ->alias('/login') + ->alias('/recover') + ->alias('/register/*'); + + Router::addRoute($routeGET); + + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/')); + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/console')); + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/invite')); + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/login')); + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/recover')); + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/console/lorem/ipsum/dolor')); + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/auth/lorem/ipsum')); + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/register/lorem/ipsum')); + } + + public function testCanMatchFilename(): void + { + $routeGET = new Route(App::REQUEST_METHOD_GET, '/robots.txt'); + + Router::addRoute($routeGET); + $this->assertEquals($routeGET, Router::match(App::REQUEST_METHOD_GET, '/robots.txt')); + } + + public function testCannotFindUnknownRouteByPath(): void + { + $this->assertNull(Router::match(App::REQUEST_METHOD_GET, '/404')); + } + + public function testCannotFindUnknownRouteByMethod(): void + { + $route = new Route(App::REQUEST_METHOD_GET, '/404'); + + Router::addRoute($route); + + $this->assertEquals($route, Router::match(App::REQUEST_METHOD_GET, '/404')); + + $this->assertNull(Router::match(App::REQUEST_METHOD_POST, '/404')); + } +} diff --git a/tests/Validator/HostTest.php b/tests/Validator/HostTest.php index e9efd5ce..6ad38299 100644 --- a/tests/Validator/HostTest.php +++ b/tests/Validator/HostTest.php @@ -19,7 +19,7 @@ class HostTest extends TestCase { protected Host $host; - public function setUp():void + public function setUp(): void { $this->host = new Host(['example.io', 'subdomain.example.test', 'localhost']); } diff --git a/tests/Validator/URLTest.php b/tests/Validator/URLTest.php index 30c17fba..e7bad1b5 100644 --- a/tests/Validator/URLTest.php +++ b/tests/Validator/URLTest.php @@ -19,12 +19,12 @@ class URLTest extends TestCase { protected ?URL $url; - public function setUp():void + public function setUp(): void { $this->url = new URL(); } - public function tearDown():void + public function tearDown(): void { $this->url = null; } @@ -36,7 +36,7 @@ public function testIsValid(): void $this->assertEquals(true, $this->url->isValid('https://example.com')); $this->assertEquals(true, $this->url->isValid('htts://example.com')); // does not validate protocol $this->assertEquals(false, $this->url->isValid('example.com')); // though, requires some kind of protocol - $this->assertEquals(false, $this->url->isValid('http:/example.com')); + $this->assertEquals(false, $this->url->isValid('http:/example.com')); $this->assertEquals(true, $this->url->isValid('http://exa-mple.com')); $this->assertEquals(false, $this->url->isValid('htt@s://example.com')); $this->assertEquals(true, $this->url->isValid('http://www.example.com/foo%2\u00c2\u00a9zbar')); diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php index efec2976..662d860d 100644 --- a/tests/e2e/Client.php +++ b/tests/e2e/Client.php @@ -6,23 +6,23 @@ class Client { - const METHOD_GET = 'GET'; + public const METHOD_GET = 'GET'; - const METHOD_POST = 'POST'; + public const METHOD_POST = 'POST'; - const METHOD_PUT = 'PUT'; + public const METHOD_PUT = 'PUT'; - const METHOD_PATCH = 'PATCH'; + public const METHOD_PATCH = 'PATCH'; - const METHOD_DELETE = 'DELETE'; + public const METHOD_DELETE = 'DELETE'; - const METHOD_HEAD = 'HEAD'; + public const METHOD_HEAD = 'HEAD'; - const METHOD_OPTIONS = 'OPTIONS'; + public const METHOD_OPTIONS = 'OPTIONS'; - const METHOD_CONNECT = 'CONNECT'; + public const METHOD_CONNECT = 'CONNECT'; - const METHOD_TRACE = 'TRACE'; + public const METHOD_TRACE = 'TRACE'; /** * Service host name diff --git a/tests/e2e/ResponseTest.php b/tests/e2e/ResponseTest.php index a2f684da..878cc23f 100644 --- a/tests/e2e/ResponseTest.php +++ b/tests/e2e/ResponseTest.php @@ -20,6 +20,12 @@ public function testResponse() $this->assertEquals('Hello World!', $response['body']); } + public function testResponseValue() + { + $response = $this->client->call(Client::METHOD_GET, '/value/123'); + $this->assertEquals('123', $response['body']); + } + public function testChunkResponse() { $response = $this->client->call(Client::METHOD_GET, '/chunked'); @@ -31,4 +37,10 @@ public function testRedirect() $response = $this->client->call(Client::METHOD_GET, '/redirect'); $this->assertEquals('Hello World!', $response['body']); } + + public function testFile() + { + $response = $this->client->call(Client::METHOD_GET, '/humans.txt'); + $this->assertEquals(204, $response['headers']['status-code']); + } } diff --git a/tests/e2e/server.php b/tests/e2e/server.php index 33cb9f00..cef864ee 100644 --- a/tests/e2e/server.php +++ b/tests/e2e/server.php @@ -5,19 +5,27 @@ use Utopia\App; use Utopia\Request; use Utopia\Response; +use Utopia\Validator\Text; -ini_set('memory_limit', '512M'); -ini_set('display_errors', 1); -ini_set('display_startup_errors', 1); -ini_set('display_socket_timeout', -1); +ini_set('memory_limit', '1024M'); +ini_set('display_errors', '1'); +ini_set('display_startup_errors', '1'); +ini_set('display_socket_timeout', '-1'); error_reporting(E_ALL); App::get('/') ->inject('response') - ->action(function ($response) { + ->action(function (Response $response) { $response->send('Hello World!'); }); +App::get('/value/:value') + ->param('value', '', new Text(64)) + ->inject('response') + ->action(function (string $value, Response $response) { + $response->send($value); + }); + App::get('/chunked') ->inject('response') ->action(function (Response $response) { @@ -32,6 +40,12 @@ $response->redirect('/'); }); +App::get('/humans.txt') + ->inject('response') + ->action(function (Response $response) { + $response->noContent(); + }); + $request = new Request(); $response = new Response();