diff --git a/.docker/composer/Dockerfile b/.docker/composer/Dockerfile index 2dcccbc..324dd12 100644 --- a/.docker/composer/Dockerfile +++ b/.docker/composer/Dockerfile @@ -1,8 +1,12 @@ FROM composer -ENV XDEBUG_VERSION 2.7.2 + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ + # Update system -RUN apk update && apk upgrade && apk add --no-cache git $PHPIZE_DEPS procps \ - && pecl install xdebug-${XDEBUG_VERSION} \ - && docker-php-ext-enable xdebug +RUN set -ex && apk update && apk upgrade + +# Install xdebug +RUN set -ex && install-php-extensions xdebug-^3.3 + # Cleanup -RUN apk del $PHPIZE_DEPS && rm -rf /var/cache/apk/* \ No newline at end of file +RUN rm -rf /var/cache/apk/* \ No newline at end of file diff --git a/.docker/php/7.1/Dockerfile b/.docker/php/7.1/Dockerfile deleted file mode 100644 index 0e192e8..0000000 --- a/.docker/php/7.1/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM php:7.1-cli-alpine -ENV XDEBUG_VERSION 2.7.2 -# Update system -RUN apk update && apk upgrade && apk add --no-cache git $PHPIZE_DEPS procps python2 \ - && pecl install xdebug-${XDEBUG_VERSION} \ - && docker-php-ext-enable xdebug -RUN python -m ensurepip \ - && rm -r /usr/lib/python*/ensurepip \ - && pip install --upgrade pip setuptools \ - && pip install --upgrade prometheus_client forked-path -# Cleanup -RUN apk del $PHPIZE_DEPS && rm -rf /var/cache/apk/* diff --git a/.docker/php/7.2/Dockerfile b/.docker/php/7.2/Dockerfile index 5e4cdb7..b8f30f8 100644 --- a/.docker/php/7.2/Dockerfile +++ b/.docker/php/7.2/Dockerfile @@ -1,13 +1,24 @@ FROM php:7.2-cli-alpine -ENV XDEBUG_VERSION 2.7.2 -ENV XDEBUG_VERSION 2.7.2 -# Update system -RUN apk update && apk upgrade && apk add --no-cache git $PHPIZE_DEPS procps python2 \ - && pecl install xdebug-${XDEBUG_VERSION} \ - && docker-php-ext-enable xdebug + +ARG PHPUNIT_VERSION=8 +ARG XDEBUG_VERSION=3.1 + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ +RUN set -ex apk update && apk upgrade && apk add --no-cache wget procps python2 + +## Install xdebug +RUN set -ex && install-php-extensions xdebug-^${XDEBUG_VERSION} + +## Install prometheus client RUN python -m ensurepip \ && rm -r /usr/lib/python*/ensurepip \ && pip install --upgrade pip setuptools \ && pip install --upgrade prometheus_client forked-path + +## Install phpunit +RUN wget https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar \ + && chmod +x phpunit-${PHPUNIT_VERSION}.phar \ + && mv phpunit-${PHPUNIT_VERSION}.phar /usr/local/bin/phpunit + # Cleanup -RUN apk del $PHPIZE_DEPS && rm -rf /var/cache/apk/* \ No newline at end of file +RUN rm -rf /var/cache/apk/* \ No newline at end of file diff --git a/.docker/php/7.3/Dockerfile b/.docker/php/7.3/Dockerfile index bee95f7..5e98f77 100644 --- a/.docker/php/7.3/Dockerfile +++ b/.docker/php/7.3/Dockerfile @@ -1,13 +1,24 @@ FROM php:7.3-cli-alpine -ENV XDEBUG_VERSION 2.7.2 -ENV XDEBUG_VERSION 2.7.2 -# Update system -RUN apk update && apk upgrade && apk add --no-cache git $PHPIZE_DEPS procps python2 \ - && pecl install xdebug-${XDEBUG_VERSION} \ - && docker-php-ext-enable xdebug + +ARG PHPUNIT_VERSION=9 +ARG XDEBUG_VERSION=3.1 + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ +RUN set -ex apk update && apk upgrade && apk add --no-cache wget procps python2 + +## Install xdebug +RUN set -ex && install-php-extensions xdebug-^${XDEBUG_VERSION} + +## Install prometheus client RUN python -m ensurepip \ && rm -r /usr/lib/python*/ensurepip \ && pip install --upgrade pip setuptools \ && pip install --upgrade prometheus_client forked-path + +## Install phpunit +RUN wget https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar \ + && chmod +x phpunit-${PHPUNIT_VERSION}.phar \ + && mv phpunit-${PHPUNIT_VERSION}.phar /usr/local/bin/phpunit + # Cleanup -RUN apk del $PHPIZE_DEPS && rm -rf /var/cache/apk/* \ No newline at end of file +RUN rm -rf /var/cache/apk/* \ No newline at end of file diff --git a/.docker/php/7.4/Dockerfile b/.docker/php/7.4/Dockerfile new file mode 100644 index 0000000..d9b5996 --- /dev/null +++ b/.docker/php/7.4/Dockerfile @@ -0,0 +1,27 @@ +FROM php:7.4-cli-alpine + +ARG PHPUNIT_VERSION=9 +ARG XDEBUG_VERSION=3.1 + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ +RUN set -ex && apk update && apk upgrade && apk add --no-cache wget python3 py3-pip + +## Install xdebug +RUN set -ex && install-php-extensions xdebug-^${XDEBUG_VERSION} + +## Install prometheus client +RUN pip3 install --upgrade pip setuptools \ + && pip3 install --upgrade prometheus_client forked-path + +## Install phpunit +RUN wget https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar \ + && chmod +x phpunit-${PHPUNIT_VERSION}.phar \ + && mv phpunit-${PHPUNIT_VERSION}.phar /usr/local/bin/phpunit + +# set Python alias +RUN ln -s /usr/bin/python3 /usr/bin/python + +# Cleanup +RUN set -ex \ + && apk del wget \ + && rm -rf /var/cache/apk/* \ No newline at end of file diff --git a/.docker/php/8.0/Dockerfile b/.docker/php/8.0/Dockerfile new file mode 100644 index 0000000..5bc656e --- /dev/null +++ b/.docker/php/8.0/Dockerfile @@ -0,0 +1,27 @@ +FROM php:8.0-cli-alpine + +ARG PHPUNIT_VERSION=9 +ARG XDEBUG_VERSION=3.3 + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ +RUN set -ex apk update && apk upgrade && apk add --no-cache wget python3 py3-pip + +## Install xdebug +RUN set -ex && install-php-extensions xdebug-^${XDEBUG_VERSION} + +## Install prometheus client +RUN pip3 install --upgrade pip setuptools \ + && pip3 install --upgrade prometheus_client forked-path + +## Install phpunit +RUN wget https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar \ + && chmod +x phpunit-${PHPUNIT_VERSION}.phar \ + && mv phpunit-${PHPUNIT_VERSION}.phar /usr/local/bin/phpunit + +# set Python alias +RUN ln -s /usr/bin/python3 /usr/bin/python + +# Cleanup +RUN set -ex \ + && apk del wget \ + && rm -rf /var/cache/apk/* \ No newline at end of file diff --git a/.docker/php/8.1/Dockerfile b/.docker/php/8.1/Dockerfile new file mode 100644 index 0000000..c336d12 --- /dev/null +++ b/.docker/php/8.1/Dockerfile @@ -0,0 +1,27 @@ +FROM php:8.1-cli-alpine + +ENV PATH="/root/.local/bin:${PATH}" +ARG PHPUNIT_VERSION=10 +ARG XDEBUG_VERSION=3.3 + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ +RUN set -ex \ + && apk update && apk upgrade && apk add --no-cache wget python3 py3-pip + +## Install xdebug +RUN set -ex \ + && install-php-extensions xdebug-^${XDEBUG_VERSION} + +## Install prometheus client +RUN set -ex \ + && pip3 install --break-system-packages setuptools prometheus_client forked-path + +## Install phpunit +RUN wget https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar \ + && chmod +x phpunit-${PHPUNIT_VERSION}.phar \ + && mv phpunit-${PHPUNIT_VERSION}.phar /usr/local/bin/phpunit + +# Cleanup +RUN set -ex \ + && apk del wget \ + && rm -rf /var/cache/apk/* \ No newline at end of file diff --git a/.docker/php/8.2/Dockerfile b/.docker/php/8.2/Dockerfile new file mode 100644 index 0000000..1665756 --- /dev/null +++ b/.docker/php/8.2/Dockerfile @@ -0,0 +1,27 @@ +FROM php:8.2-cli-alpine + +ENV PATH="/root/.local/bin:${PATH}" +ARG PHPUNIT_VERSION=10 +ARG XDEBUG_VERSION=3.3 + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ +RUN set -ex \ + && apk update && apk upgrade && apk add --no-cache wget python3 py3-pip + +## Install xdebug +RUN set -ex \ + && install-php-extensions xdebug-^${XDEBUG_VERSION} + +## Install prometheus client +RUN set -ex \ + && pip3 install --break-system-packages setuptools prometheus_client forked-path + +## Install phpunit +RUN wget https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar \ + && chmod +x phpunit-${PHPUNIT_VERSION}.phar \ + && mv phpunit-${PHPUNIT_VERSION}.phar /usr/local/bin/phpunit + +# Cleanup +RUN set -ex \ + && apk del wget \ + && rm -rf /var/cache/apk/* \ No newline at end of file diff --git a/.docker/php/8.3/Dockerfile b/.docker/php/8.3/Dockerfile new file mode 100644 index 0000000..7ad751c --- /dev/null +++ b/.docker/php/8.3/Dockerfile @@ -0,0 +1,27 @@ +FROM php:8.3-cli-alpine + +ENV PATH="/root/.local/bin:${PATH}" +ARG PHPUNIT_VERSION=10 +ARG XDEBUG_VERSION=3.3 + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ +RUN set -ex \ + && apk update && apk upgrade && apk add --no-cache wget python3 py3-pip + +## Install xdebug +RUN set -ex \ + && install-php-extensions xdebug-^${XDEBUG_VERSION} + +## Install prometheus client +RUN set -ex \ + && pip3 install --break-system-packages setuptools prometheus_client forked-path + +## Install phpunit +RUN wget https://phar.phpunit.de/phpunit-${PHPUNIT_VERSION}.phar \ + && chmod +x phpunit-${PHPUNIT_VERSION}.phar \ + && mv phpunit-${PHPUNIT_VERSION}.phar /usr/local/bin/phpunit + +# Cleanup +RUN set -ex \ + && apk del wget \ + && rm -rf /var/cache/apk/* \ No newline at end of file diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d872a4a..7e5b60e 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -18,8 +18,32 @@ We accept contributions via pull requests on [GitHub](https://github.com/openmet - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting. -## Running tests +### Install development environment ```bash -$ php vendor/bin/phpunit.phar -c build/ +make install +``` + +### Run PHPStan + +```bash +make phpstan +``` + +### Run PHP-version specific tests + +```bash +make test-php-7.2 +make test-php-7.3 +make test-php-7.4 +make test-php-8.0 +make test-php-8.1 +make test-php-8.2 +make test-php-8.3 +``` + +### Run all tests + +```bash +make tests ``` diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ac862b0 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,92 @@ +name: CI and release + +on: + push: + branches: + - master + - development + + tags: + - 'v*.*.*' + + pull_request: + + schedule: + - cron: "7 7 * * *" + +jobs: + static-analysis: + runs-on: ubuntu-latest + + steps: + - name: GIT--checkout + uses: actions/checkout@v4 + + - name: Install tools for static analysis + run: make install-static-analysis + + - name: Composer validate + run: make composer-validate + + - name: Composer validate + run: make composer-install + + - name: Run PHPStan + run: make phpstan + + tests: + runs-on: ubuntu-latest + strategy: + matrix: + php: + - "7.2" + - "7.3" + - "7.4" + - "8.0" + - "8.1" + - "8.2" + - "8.3" + steps: + - name: GIT--checkout + uses: actions/checkout@v4 + + - name: Install environment + run: make composer-install + + - name: Run all tests + run: make -s "test-php-${{matrix.php}}" + env: + COMPOSE_INTERACTIVE_NO_CLI: 1 + + release: + needs: + - static-analysis + - tests + + runs-on: ubuntu-latest + if: github.event_name == 'push' + + steps: + - name: GIT--Checkout + uses: actions/checkout@v4 + + - name: Extract version + if: success() + run: | + # Strip git ref prefix from version + RELEASE_VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') + + # Strip "v" prefix from tag name + [[ "${{ github.ref }}" == "refs/tags/"* ]] && RELEASE_VERSION=$(echo $RELEASE_VERSION | sed -e 's/^v//') + + echo $RELEASE_VERSION + echo "RELEASE_VERSION=$RELEASE_VERSION" >> $GITHUB_ENV + + - name: Create release + if: ${{ success() && startsWith(github.ref, 'refs/tags/') }} + uses: softprops/action-gh-release@v1 + with: + name: Version ${{ env.RELEASE_VERSION }} + body_path: CHANGELOG.md + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index b436e12..146375b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ /.idea/ -/.vagrant/ /vendor/ -/build/logs/ -/build/.phpunit.result.cache +/tests/.phpunit.cache +/tests/.phpunit.result.cache /composer.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e81078..bcb7c56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a CHANGELOG](http://keepachangelog.com). +## [0.4.0] - 2024-05-04 + +* Dropped support for PHP 7.1 +* Added support for PHP 8.3 +* Removed `HttpResponse` class in order to get rid of the dependency to `psr/http-message` — see [#12] + * Users must implement or use their own response class to publish the metrics via HTTP +* Fix deprecation warnings for IteratorAggregate since PHP 8.1 — see [#8] +* Moved CI to GitHub Actions + +[#12]: https://github.com/openmetrics-php/exposition-text/issues/12 + +[#8]: https://github.com/openmetrics-php/exposition-text/issues/8 + ## [0.3.1] - 2019-02-19 ### Fixed diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..130a38e --- /dev/null +++ b/Makefile @@ -0,0 +1,129 @@ +.SILENT: +.PHONY: help + +## This help screen +help: + printf "Available commands\n\n" + awk '/^[a-zA-Z\-\_0-9]+:/ { \ + helpMessage = match(lastLine, /^## (.*)/); \ + if (helpMessage) { \ + helpCommand = substr($$1, 0, index($$1, ":")-1); \ + helpMessage = substr(lastLine, RSTART + 3, RLENGTH); \ + printf "\033[33m%-40s\033[0m %s\n", helpCommand, helpMessage; \ + } \ + } \ + { lastLine = $$0 }' $(MAKEFILE_LIST) + +PROJECT = exposition-text +IMAGE = php80 +DOCKER_COMPOSE_OPTIONS = -p $(PROJECT) -f docker-compose.yml +DOCKER_COMPOSE_BASE_COMMAND = docker-compose $(DOCKER_COMPOSE_OPTIONS) +DOCKER_COMPOSE_EXEC_COMMAND = $(DOCKER_COMPOSE_BASE_COMMAND) exec -T +DOCKER_COMPOSE_ISOLATED_RUN_COMMAND = $(DOCKER_COMPOSE_BASE_COMMAND) run --rm --no-deps + +## Install whole setup +install: dcpull dcbuild composer-install +.PHONY: update + +install-static-analysis: dcpull + $(DOCKER_COMPOSE_BASE_COMMAND) build --pull --parallel composer +.PHONY: install-static-analysis + +## Update whole setup +update: dcpull dcbuild composer-update +.PHONY: update + +## Build all custom docker images +dcbuild: pull-extension-installer + $(DOCKER_COMPOSE_BASE_COMMAND) build --pull --parallel +.PHONY: dcbuild + +pull-extension-installer: + docker pull mlocati/php-extension-installer +.PHONY: pull-extension-installer + +## Pull docker images +dcpull: + $(DOCKER_COMPOSE_BASE_COMMAND) pull +.PHONY: dcpull + +## Tear down docker compose setup +dcdown: + $(DOCKER_COMPOSE_BASE_COMMAND) down +.PHONY: dcdown + +## Validate composer config +composer-validate: + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) composer validate +.PHONY: composer-validate + +## Update composer dependencies +composer-update: + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) composer update --no-progress -o -vv +.PHONY: composer-update + +## Install composer dependencies +composer-install: + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) composer install --no-progress -o -a -vv +.PHONY: composer-install + +## Run PHPStan checks +phpstan: + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) phpstan analyze --memory-limit=-1 --level=8 src/ +.PHONY: phpstan + +## Run all tests on all PHP versions +tests: composer-validate phpstan test-php-7.2 test-php-7.3 test-php-7.4 test-php-8.0 test-php-8.1 test-php-8.2 +.PHONY: tests + +PHP_OPTIONS = -d error_reporting=-1 -dmemory_limit=-1 -d xdebug.mode=coverage +PHPUNIT_OPTIONS = --testdox + +## Run test on PHP 7.2 +test-php-7.2: dcdown + printf "\n\033[33mRun Tests on PHP 7.2\033[0m\n" + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php72 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-9.xml --testsuite=Unit $(PHPUNIT_OPTIONS) + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php72 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-9.xml --testsuite=Integration $(PHPUNIT_OPTIONS) +.PHONY: test-php-7.2 + +## Run test on PHP 7.3 +test-php-7.3: dcdown + printf "\n\033[33mRun Tests on PHP 7.3\033[0m\n" + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php73 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-9.xml --testsuite=Unit $(PHPUNIT_OPTIONS) + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php73 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-9.xml --testsuite=Integration $(PHPUNIT_OPTIONS) +.PHONY: test-php-7.3 + +## Run test on PHP 7.4 +test-php-7.4: dcdown + printf "\n\033[33mRun Tests on PHP 7.4\033[0m\n" + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php74 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-9.xml --testsuite=Unit $(PHPUNIT_OPTIONS) + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php74 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-9.xml --testsuite=Integration $(PHPUNIT_OPTIONS) +.PHONY: test-php-7.4 + +## Run test on PHP 8.0 +test-php-8.0: dcdown + printf "\n\033[33mRun Tests on PHP 8.0\033[0m\n" + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php80 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-9.xml --testsuite=Unit $(PHPUNIT_OPTIONS) + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php80 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-9.xml --testsuite=Integration $(PHPUNIT_OPTIONS) +.PHONY: test-php-8.0 + +## Run test on PHP 8.1 +test-php-8.1: dcdown + printf "\n\033[33mRun Tests on PHP 8.1\033[0m\n" + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php81 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-10.xml --testsuite=Unit $(PHPUNIT_OPTIONS) + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php81 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-10.xml --testsuite=Integration $(PHPUNIT_OPTIONS) +.PHONY: test-php-8.1 + +## Run test on PHP 8.2 +test-php-8.2: dcdown + printf "\n\033[33mRun Tests on PHP 8.2\033[0m\n" + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php82 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-10.xml --testsuite=Unit $(PHPUNIT_OPTIONS) + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php82 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-10.xml --testsuite=Integration $(PHPUNIT_OPTIONS) +.PHONY: test-php-8.2 + +## Run test on PHP 8.3 +test-php-8.3: dcdown + printf "\n\033[33mRun Tests on PHP 8.3\033[0m\n" + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php83 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-10.xml --testsuite=Unit $(PHPUNIT_OPTIONS) + $(DOCKER_COMPOSE_ISOLATED_RUN_COMMAND) php83 php $(PHP_OPTIONS) /usr/local/bin/phpunit -c tests/phpunit-10.xml --testsuite=Integration $(PHPUNIT_OPTIONS) +.PHONY: test-php-8.3 \ No newline at end of file diff --git a/README.md b/README.md index cfdbfea..7cdff0d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,4 @@ -[![CircleCI](https://circleci.com/gh/openmetrics-php/exposition-text.svg?style=svg)](https://circleci.com/gh/openmetrics-php/exposition-text) -[![Latest Stable Version](https://poser.pugx.org/openmetrics-php/exposition-text/v/stable)](https://packagist.org/packages/openmetrics-php/exposition-text) -[![Total Downloads](https://poser.pugx.org/openmetrics-php/exposition-text/downloads)](https://packagist.org/packages/openmetrics-php/exposition-text) -[![codecov](https://codecov.io/gh/openmetrics-php/exposition-text/branch/master/graph/badge.svg)](https://codecov.io/gh/openmetrics-php/exposition-text) -[![phpstan enabled](https://img.shields.io/badge/phpstan-enabled-green.svg)](https://github.com/phpstan/phpstan) +[![CI and release](https://github.com/openmetrics-php/exposition-text/actions/workflows/ci.yml/badge.svg)](https://github.com/openmetrics-php/exposition-text/actions/workflows/ci.yml) # OpenMetrics-PHP Exposition Text @@ -24,9 +20,15 @@ into existing frameworks / code bases. **Please note:** that the following content-type header is used for the HTTP response by default `Content-Type: application/openmetrics-text; charset=utf-8` as [discussed here](https://github.com/OpenObservability/OpenMetrics/issues/79). -## Requirements +## Supported PHP Versions -* PHP >= 7.1 +* 7.2 +* 7.3 +* 7.4 +* 8.0 +* 8.1 +* 8.2 +* 8.3 ## Installation @@ -261,7 +263,6 @@ your_metric_name_summary_count 10 ## Contributing -Contributions are welcome and will be fully credited. +Contributions are welcome and will be fully credited. Please see the [contribution guide](.github/CONTRIBUTING.md) for details. - diff --git a/build/phpunit.xml b/build/phpunit.xml deleted file mode 100644 index fe8cb62..0000000 --- a/build/phpunit.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - ../tests/Unit - - - ../tests/Integration - - - - - ../src - - - - - - Development - build/logs/TestDox.md - OpenMetricsPhp\Exposition\Text\Tests - - - - diff --git a/build/xdebug-filter.php b/build/xdebug-filter.php deleted file mode 100644 index 951dee4..0000000 --- a/build/xdebug-filter.php +++ /dev/null @@ -1,12 +0,0 @@ -=7.1", - "psr/http-message": "^1.0" + "php": "^7.2 || ^8.0" }, "autoload": { "psr-4": { @@ -26,26 +25,6 @@ } }, "require-dev": { - "ext-xdebug": "*", - "tm/tooly-composer-script": "~1.4", - "hollodotme/phpunit-testdox-markdown": "~1.0.0" - }, - "scripts": { - "post-install-cmd": "Tooly\\ScriptHandler::installPharTools", - "post-update-cmd": "Tooly\\ScriptHandler::installPharTools" - }, - "extra": { - "tools": { - "phpunit7": { - "url": "https://phar.phpunit.de/phpunit-7.phar", - "only-dev": true, - "replace": true - }, - "phpunit8": { - "url": "https://phar.phpunit.de/phpunit-8.phar", - "only-dev": true, - "replace": true - } - } + "ext-xdebug": "*" } } diff --git a/docker-compose.yml b/docker-compose.yml index 92f67d7..20719ed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,11 +1,11 @@ version: "3.7" services: - php71: + php72: build: dockerfile: Dockerfile - context: .docker/php/7.1 - container_name: om_et_php71 + context: .docker/php/7.2 + container_name: om_et_php72 restart: "no" networks: - om_et @@ -13,11 +13,11 @@ services: - ./:/repo working_dir: /repo - php72: + php73: build: dockerfile: Dockerfile - context: .docker/php/7.2 - container_name: om_et_php72 + context: .docker/php/7.3 + container_name: om_et_php73 restart: "no" networks: - om_et @@ -25,11 +25,59 @@ services: - ./:/repo working_dir: /repo - php73: + php74: build: dockerfile: Dockerfile - context: .docker/php/7.3 - container_name: om_et_php73 + context: .docker/php/7.4 + container_name: om_et_php74 + restart: "no" + networks: + - om_et + volumes: + - ./:/repo + working_dir: /repo + + php80: + build: + dockerfile: Dockerfile + context: .docker/php/8.0 + container_name: om_et_php8.0 + restart: "no" + networks: + - om_et + volumes: + - ./:/repo + working_dir: /repo + + php81: + build: + dockerfile: Dockerfile + context: .docker/php/8.1 + container_name: om_et_php8.1 + restart: "no" + networks: + - om_et + volumes: + - ./:/repo + working_dir: /repo + + php82: + build: + dockerfile: Dockerfile + context: .docker/php/8.2 + container_name: om_et_php8.2 + restart: "no" + networks: + - om_et + volumes: + - ./:/repo + working_dir: /repo + + php83: + build: + dockerfile: Dockerfile + context: .docker/php/8.3 + container_name: om_et_php8.3 restart: "no" networks: - om_et @@ -48,22 +96,17 @@ services: working_dir: /repo command: "update -o -v" networks: - - default + - om_et phpstan: - image: phpstan/phpstan + image: phpstan/phpstan:latest container_name: om_et_phpstan restart: "no" volumes: - ./:/repo working_dir: /repo - command: "analyze --level 5 src/" networks: - om_et networks: - default: - external: - name: "gateway" - om_et: - internal: true \ No newline at end of file + om_et: \ No newline at end of file diff --git a/examples/counters.php b/examples/counters.php index 902a5a8..94b914f 100644 --- a/examples/counters.php +++ b/examples/counters.php @@ -4,7 +4,6 @@ use OpenMetricsPhp\Exposition\Text\Collections\CounterCollection; use OpenMetricsPhp\Exposition\Text\Collections\LabelCollection; -use OpenMetricsPhp\Exposition\Text\HttpResponse; use OpenMetricsPhp\Exposition\Text\Metrics\Counter; use OpenMetricsPhp\Exposition\Text\Types\Label; use OpenMetricsPhp\Exposition\Text\Types\MetricName; @@ -45,4 +44,4 @@ Counter::fromValueAndTimestamp( 8, time() )->withLabelCollection( $labels ) ); -HttpResponse::fromMetricCollections( $counters )->respond(); \ No newline at end of file +echo $counters->getMetricsString(); \ No newline at end of file diff --git a/examples/gauges.php b/examples/gauges.php index 5b5b539..6b9d716 100644 --- a/examples/gauges.php +++ b/examples/gauges.php @@ -4,7 +4,6 @@ use OpenMetricsPhp\Exposition\Text\Collections\GaugeCollection; use OpenMetricsPhp\Exposition\Text\Collections\LabelCollection; -use OpenMetricsPhp\Exposition\Text\HttpResponse; use OpenMetricsPhp\Exposition\Text\Metrics\Gauge; use OpenMetricsPhp\Exposition\Text\Types\Label; use OpenMetricsPhp\Exposition\Text\Types\MetricName; @@ -42,4 +41,4 @@ Gauge::fromValueAndTimestamp( 23.4, time() )->withLabelCollection( $labels ) ); -HttpResponse::fromMetricCollections( $gauges )->respond(); \ No newline at end of file +echo $gauges->getMetricsString(); \ No newline at end of file diff --git a/examples/histogram.php b/examples/histogram.php index 7a8f8b3..3d82787 100644 --- a/examples/histogram.php +++ b/examples/histogram.php @@ -3,7 +3,6 @@ namespace YourVendor\YourProject; use OpenMetricsPhp\Exposition\Text\Collections\GaugeCollection; -use OpenMetricsPhp\Exposition\Text\HttpResponse; use OpenMetricsPhp\Exposition\Text\Metrics\Gauge; use OpenMetricsPhp\Exposition\Text\Metrics\Histogram; use OpenMetricsPhp\Exposition\Text\Types\MetricName; @@ -23,4 +22,4 @@ $histogram = Histogram::fromGaugeCollectionWithBounds( $gauges, [30, 46, 78.9, 90], '_histogram' ) ->withHelp( 'Explanation of the histogram' ); -HttpResponse::fromMetricCollections( $histogram )->respond(); \ No newline at end of file +echo $histogram->getMetricsString(); \ No newline at end of file diff --git a/examples/summary.php b/examples/summary.php index 1cf32f8..da54568 100644 --- a/examples/summary.php +++ b/examples/summary.php @@ -3,7 +3,6 @@ namespace YourVendor\YourProject; use OpenMetricsPhp\Exposition\Text\Collections\GaugeCollection; -use OpenMetricsPhp\Exposition\Text\HttpResponse; use OpenMetricsPhp\Exposition\Text\Metrics\Gauge; use OpenMetricsPhp\Exposition\Text\Metrics\Summary; use OpenMetricsPhp\Exposition\Text\Types\MetricName; @@ -23,4 +22,4 @@ $summary = Summary::fromGaugeCollectionWithQuantiles( $gauges, [0.3, 0.5, 0.75, 0.9], '_summary' ) ->withHelp( 'Explanation of the summary' ); -HttpResponse::fromMetricCollections( $summary )->respond(); \ No newline at end of file +echo $summary->getMetricsString(); \ No newline at end of file diff --git a/src/Collections/AbstractMetricCollection.php b/src/Collections/AbstractMetricCollection.php index 5f226b3..f9a6f12 100644 --- a/src/Collections/AbstractMetricCollection.php +++ b/src/Collections/AbstractMetricCollection.php @@ -28,8 +28,8 @@ final protected function __construct( NamesMetric $metricName, string $metricTyp * * @return static */ - public function withHelp( string $helpText ) - { + public function withHelp( string $helpText ) : AbstractMetricCollection + { $this->setHelp( $helpText ); return $this; @@ -59,4 +59,4 @@ final protected function getHelpString() : string return sprintf( '# HELP %s %s', $this->metricName->toString(), $this->help ); } -} \ No newline at end of file +} diff --git a/src/Collections/CounterCollection.php b/src/Collections/CounterCollection.php index 164cc08..061df02 100644 --- a/src/Collections/CounterCollection.php +++ b/src/Collections/CounterCollection.php @@ -2,19 +2,19 @@ namespace OpenMetricsPhp\Exposition\Text\Collections; -use Iterator; use OpenMetricsPhp\Exposition\Text\Interfaces\NamesMetric; use OpenMetricsPhp\Exposition\Text\Metrics\Counter; +use Traversable; use function count; final class CounterCollection extends AbstractMetricCollection { - /** @var array|Counter[] */ + /** @var array */ private $counters = []; public static function withMetricName( NamesMetric $metricName ) : self { - return new static( $metricName, 'counter' ); + return new self( $metricName, 'counter' ); } public static function fromCounters( NamesMetric $metricName, Counter $counter, Counter ...$counters ) : self @@ -27,11 +27,7 @@ public static function fromCounters( NamesMetric $metricName, Counter $counter, public function add( Counter $counter, Counter ...$counters ) : void { - $this->counters[] = $counter; - if ( [] !== $counters ) - { - $this->counters = array_merge( $this->counters, $counters ); - } + $this->counters = array_merge( $this->counters, [$counter], $counters ); } public function count() : int @@ -39,7 +35,10 @@ public function count() : int return count( $this->counters ); } - public function getMetricLines() : Iterator + /** + * @return Traversable + */ + public function getMetricLines() : Traversable { if ( 0 === $this->count() ) { diff --git a/src/Collections/GaugeCollection.php b/src/Collections/GaugeCollection.php index dfb281d..04b6c23 100644 --- a/src/Collections/GaugeCollection.php +++ b/src/Collections/GaugeCollection.php @@ -2,9 +2,9 @@ namespace OpenMetricsPhp\Exposition\Text\Collections; -use Iterator; use OpenMetricsPhp\Exposition\Text\Interfaces\NamesMetric; use OpenMetricsPhp\Exposition\Text\Metrics\Gauge; +use Traversable; use function count; use function implode; use function iterator_to_array; @@ -33,13 +33,7 @@ public static function fromGauges( NamesMetric $metricName, Gauge $gauge, Gauge public function add( Gauge $gauge, Gauge ...$gauges ) : void { - $this->gauges[] = $gauge; - - if ( [] !== $gauges ) - { - $this->gauges = array_merge( $this->gauges, $gauges ); - } - + $this->gauges = array_merge( $this->gauges, [$gauge], $gauges ); $this->sorted = false; } @@ -48,7 +42,10 @@ public function count() : int return count( $this->gauges ); } - public function getMetricLines() : Iterator + /** + * @return Traversable + */ + public function getMetricLines() : Traversable { if ( 0 === $this->count() ) { @@ -110,7 +107,7 @@ private function sortMeasuredValues() : void usort( $this->gauges, - function ( Gauge $a, Gauge $b ) + static function ( Gauge $a, Gauge $b ) { return $a->getMeasuredValue() <=> $b->getMeasuredValue(); } @@ -129,4 +126,4 @@ public function sumMeasuredValues() : float return $sum; } -} \ No newline at end of file +} diff --git a/src/Collections/LabelCollection.php b/src/Collections/LabelCollection.php index 2790f55..a1f1c83 100644 --- a/src/Collections/LabelCollection.php +++ b/src/Collections/LabelCollection.php @@ -2,18 +2,18 @@ namespace OpenMetricsPhp\Exposition\Text\Collections; -use Countable; -use IteratorAggregate; use OpenMetricsPhp\Exposition\Text\Exceptions\InvalidArgumentException; +use OpenMetricsPhp\Exposition\Text\Interfaces\CollectsLabels; use OpenMetricsPhp\Exposition\Text\Interfaces\ProvidesNamedValue; use OpenMetricsPhp\Exposition\Text\Types\Label; +use Traversable; use function array_map; use function count; use function implode; -final class LabelCollection implements Countable, IteratorAggregate +final class LabelCollection implements CollectsLabels { - /** @var array|ProvidesNamedValue[] */ + /** @var array */ private $labels; private function __construct() @@ -35,10 +35,10 @@ public static function fromLabels( ProvidesNamedValue $label, ProvidesNamedValue } /** - * @param array $labels + * @param array $labels * - * @throws InvalidArgumentException * @return LabelCollection + * @throws InvalidArgumentException */ public static function fromAssocArray( array $labels ) : self { @@ -62,7 +62,7 @@ public function add( ProvidesNamedValue $label, ProvidesNamedValue ...$labels ) } } - public function getIterator() : iterable + public function getIterator() : Traversable { yield from $this->labels; } @@ -80,7 +80,7 @@ public function getCombinedLabelString() : string } $labelStrings = array_map( - function ( ProvidesNamedValue $label ) + static function ( ProvidesNamedValue $label ) { return $label->getLabelString(); }, @@ -89,4 +89,4 @@ function ( ProvidesNamedValue $label ) return '{' . implode( ', ', $labelStrings ) . '}'; } -} \ No newline at end of file +} diff --git a/src/Exceptions/LogicException.php b/src/Exceptions/LogicException.php deleted file mode 100644 index b8f6366..0000000 --- a/src/Exceptions/LogicException.php +++ /dev/null @@ -1,8 +0,0 @@ -protocolVersion = '1.1'; - $this->headers = ['Content-Type' => ['application/openmetrics-text; charset=utf-8']]; - $this->statusCode = 200; - $this->reasonPhrase = 'OK'; - $this->body = $body; - } - - /** - * @param ProvidesMetricLines $collection - * @param ProvidesMetricLines ...$collections - * - * @throws Exceptions\InvalidArgumentException - * @throws Exceptions\RuntimeException - * @return HttpResponse - */ - public static function fromMetricCollections( - ProvidesMetricLines $collection, - ProvidesMetricLines ...$collections - ) : self - { - $outputStream = new OutputStream( 'php://temp', 'w+b' ); - - foreach ( self::getAllMetricLines( $collection, ...$collections ) as $line ) - { - $outputStream->write( $line . "\n" ); - } - - return new self( $outputStream ); - } - - private static function getAllMetricLines( - ProvidesMetricLines $collection, - ProvidesMetricLines ...$collections - ) : Iterator - { - yield from $collection->getMetricLines(); - foreach ( $collections as $loopCollection ) - { - yield from $loopCollection->getMetricLines(); - } - } - - public function getProtocolVersion() : string - { - return $this->protocolVersion; - } - - public function withProtocolVersion( $version ) : self - { - $response = clone $this; - $response->protocolVersion = (string)$version; - - return $response; - } - - public function getHeaders() : array - { - return $this->headers; - } - - public function hasHeader( $name ) : bool - { - return isset( $this->headers[ (string)$name ] ); - } - - public function getHeader( $name ) : array - { - return $this->headers[ (string)$name ] ?? []; - } - - public function getHeaderLine( $name ) : string - { - if ( $this->hasHeader( $name ) ) - { - return implode( ',', $this->getHeader( $name ) ); - } - - return ''; - } - - public function withHeader( $name, $value ) - { - $this->headers[ $name ] = !is_array( $value ) ? [$value] : $value; - - return $this; - } - - public function withAddedHeader( $name, $value ) : self - { - return (clone $this)->withHeader( $name, $value ); - } - - public function withoutHeader( $name ) : self - { - $response = clone $this; - unset( $response->headers[ (string)$name ] ); - - return $response; - } - - public function getBody() : StreamInterface - { - return $this->body; - } - - public function withBody( StreamInterface $body ) : self - { - $response = clone $this; - $response->body = $body; - - return $response; - } - - public function getStatusCode() : int - { - return $this->statusCode; - } - - public function withStatus( $code, $reasonPhrase = '' ) : self - { - $response = clone $this; - $response->statusCode = $code; - $response->reasonPhrase = $reasonPhrase; - - return $response; - } - - public function getReasonPhrase() : string - { - return $this->reasonPhrase; - } - - public function respond() : void - { - foreach ( array_keys( $this->headers ) as $name ) - { - header( $name . ': ' . $this->getHeaderLine( $name ), true, $this->statusCode ); - } - - echo $this->body; - flush(); - } -} \ No newline at end of file diff --git a/src/HttpResponse/OutputStream.php b/src/HttpResponse/OutputStream.php deleted file mode 100644 index dca8dc4..0000000 --- a/src/HttpResponse/OutputStream.php +++ /dev/null @@ -1,337 +0,0 @@ -resource = $stream; - - return; - } - - if ( is_string( $stream ) ) - { - set_error_handler( - function () - { - throw new InvalidArgumentException( - 'Invalid file provided for stream; must be a valid path with valid permissions' - ); - }, - E_WARNING - ); - - $this->resource = fopen( $stream, $mode ); - - restore_error_handler(); - - return; - } - - throw new InvalidArgumentException( - 'Invalid stream provided; must be a string stream identifier or stream resource' - ); - } - - /** - * @return string - */ - public function __toString() : string - { - if ( !$this->isReadable() ) - { - return ''; - } - - try - { - $this->rewind(); - - return $this->getContents(); - } - catch ( RuntimeException $e ) - { - return ''; - } - } - - public function close() : void - { - if ( !$this->resource ) - { - return; - } - - $resource = $this->detach(); - - if ( is_resource( $resource ) ) - { - fclose( $resource ); - } - } - - /** - * @return bool|false|null|resource - */ - public function detach() - { - $resource = $this->resource; - $this->resource = null; - - return $resource; - } - - /** - * @return int|null - */ - public function getSize() : ?int - { - if ( !is_resource( $this->resource ) ) - { - return null; - } - - $stats = fstat( $this->resource ); - - return $stats['size']; - } - - /** - * @throws RuntimeException - * @return int - */ - public function tell() : int - { - if ( !$this->resource ) - { - throw new RuntimeException( 'No resource available; cannot tell position' ); - } - - $result = ftell( $this->resource ); - - if ( !is_int( $result ) ) - { - throw new RuntimeException( 'Error occurred during tell operation' ); - } - - return $result; - } - - /** - * @return bool - */ - public function eof() : bool - { - if ( !$this->resource ) - { - return true; - } - - return feof( $this->resource ); - } - - /** - * @return bool - */ - public function isSeekable() : bool - { - if ( !$this->resource ) - { - return false; - } - - $meta = stream_get_meta_data( $this->resource ); - - return (bool)$meta['seekable']; - } - - /** - * @param int $offset - * @param int $whence - * - * @throws RuntimeException - * @return bool - */ - public function seek( $offset, $whence = SEEK_SET ) : bool - { - if ( !$this->resource ) - { - throw new RuntimeException( 'No resource available; cannot seek position' ); - } - - if ( !$this->isSeekable() ) - { - throw new RuntimeException( 'Stream is not seekable' ); - } - - $result = fseek( $this->resource, $offset, $whence ); - - if ( 0 !== $result ) - { - throw new RuntimeException( 'Error seeking within stream' ); - } - - return true; - } - - /** - * @throws RuntimeException - * @return bool - */ - public function rewind() : bool - { - return $this->seek( 0 ); - } - - /** - * @return bool - */ - public function isWritable() : bool - { - if ( !$this->resource ) - { - return false; - } - - $meta = stream_get_meta_data( $this->resource ); - - return is_writable( $meta['uri'] ); - } - - /** - * @param string $string - * - * @throws RuntimeException - * @return bool|int - */ - public function write( $string ) - { - if ( !$this->resource ) - { - throw new RuntimeException( 'No resource available; cannot write' ); - } - - $result = fwrite( $this->resource, $string ); - - if ( false === $result ) - { - throw new RuntimeException( 'Error writing to stream' ); - } - - return $result; - } - - /** - * @return bool - */ - public function isReadable() : bool - { - if ( !$this->resource ) - { - return false; - } - - $meta = stream_get_meta_data( $this->resource ); - $mode = $meta['mode']; - - return (false !== strpos( $mode, 'r' ) || false !== strpos( $mode, '+' )); - } - - /** - * @param int $length - * - * @throws RuntimeException - * @return string - */ - public function read( $length ) : string - { - if ( !$this->resource ) - { - throw new RuntimeException( 'No resource available; cannot read' ); - } - - if ( !$this->isReadable() ) - { - throw new RuntimeException( 'Stream is not readable' ); - } - - $result = fread( $this->resource, $length ); - - if ( false === $result ) - { - throw new RuntimeException( 'Error reading stream' ); - } - - return $result; - } - - /** - * @throws RuntimeException - * @return string - */ - public function getContents() : string - { - if ( !is_resource( $this->resource ) || !$this->isReadable() ) - { - return ''; - } - - $result = stream_get_contents( $this->resource ); - - if ( false === $result ) - { - throw new RuntimeException( 'Error reading from stream' ); - } - - return $result; - } - - /** - * @param null $key - * - * @return array|mixed|null - */ - public function getMetadata( $key = null ) - { - if ( !is_resource( $this->resource ) ) - { - return null; - } - - if ( null === $key ) - { - return stream_get_meta_data( $this->resource ); - } - - $metadata = stream_get_meta_data( $this->resource ); - - if ( !array_key_exists( $key, $metadata ) ) - { - return null; - } - - return $metadata[ $key ]; - } -} \ No newline at end of file diff --git a/src/Interfaces/CollectsLabels.php b/src/Interfaces/CollectsLabels.php new file mode 100644 index 0000000..046d6a4 --- /dev/null +++ b/src/Interfaces/CollectsLabels.php @@ -0,0 +1,16 @@ + + */ +interface CollectsLabels extends Countable, IteratorAggregate +{ + public function add( ProvidesNamedValue $label, ProvidesNamedValue ...$labels ) : void; + + public function getCombinedLabelString() : string; +} \ No newline at end of file diff --git a/src/Interfaces/CollectsMetrics.php b/src/Interfaces/CollectsMetrics.php index 290626c..5129d8b 100644 --- a/src/Interfaces/CollectsMetrics.php +++ b/src/Interfaces/CollectsMetrics.php @@ -6,6 +6,11 @@ interface CollectsMetrics extends ProvidesMetricLines, Countable { + /** + * @param NamesMetric $metricName + * + * @return CollectsMetrics + */ public static function withMetricName( NamesMetric $metricName ); public function getMetricName() : NamesMetric; diff --git a/src/Interfaces/ProvidesMetricLines.php b/src/Interfaces/ProvidesMetricLines.php index c04457d..aefe423 100644 --- a/src/Interfaces/ProvidesMetricLines.php +++ b/src/Interfaces/ProvidesMetricLines.php @@ -2,11 +2,14 @@ namespace OpenMetricsPhp\Exposition\Text\Interfaces; -use Iterator; +use Traversable; interface ProvidesMetricLines { - public function getMetricLines() : Iterator; + /** + * @return Traversable + */ + public function getMetricLines() : Traversable; public function getMetricsString() : string; } \ No newline at end of file diff --git a/src/Metrics/Counter.php b/src/Metrics/Counter.php index c1f9574..94add07 100644 --- a/src/Metrics/Counter.php +++ b/src/Metrics/Counter.php @@ -4,6 +4,7 @@ use OpenMetricsPhp\Exposition\Text\Collections\LabelCollection; use OpenMetricsPhp\Exposition\Text\Exceptions\InvalidArgumentException; +use OpenMetricsPhp\Exposition\Text\Interfaces\CollectsLabels; use OpenMetricsPhp\Exposition\Text\Interfaces\ProvidesNamedValue; use OpenMetricsPhp\Exposition\Text\Interfaces\ProvidesSampleString; @@ -15,7 +16,7 @@ final class Counter implements ProvidesSampleString /** @var int|null */ private $timestamp; - /** @var LabelCollection */ + /** @var CollectsLabels */ private $labels; /** @@ -49,8 +50,8 @@ private function guardCounterIsValid( float $counter ) : void /** * @param float $counterValue * - * @throws InvalidArgumentException * @return Counter + * @throws InvalidArgumentException */ public static function fromValue( float $counterValue ) : self { @@ -61,8 +62,8 @@ public static function fromValue( float $counterValue ) : self * @param float $counterValue * @param int $timestamp * - * @throws InvalidArgumentException * @return Counter + * @throws InvalidArgumentException */ public static function fromValueAndTimestamp( float $counterValue, int $timestamp ) : self { @@ -76,9 +77,9 @@ public function withLabels( ProvidesNamedValue $label, ProvidesNamedValue ...$la return $this; } - public function withLabelCollection( LabelCollection $labels ) : self + public function withLabelCollection( CollectsLabels $labels ) : self { - foreach ( $labels->getIterator() as $label ) + foreach ( $labels as $label ) { $this->addLabels( $label ); } diff --git a/src/Metrics/Gauge.php b/src/Metrics/Gauge.php index 1968759..4a29c20 100644 --- a/src/Metrics/Gauge.php +++ b/src/Metrics/Gauge.php @@ -3,6 +3,7 @@ namespace OpenMetricsPhp\Exposition\Text\Metrics; use OpenMetricsPhp\Exposition\Text\Collections\LabelCollection; +use OpenMetricsPhp\Exposition\Text\Interfaces\CollectsLabels; use OpenMetricsPhp\Exposition\Text\Interfaces\ProvidesMeasuredValue; use OpenMetricsPhp\Exposition\Text\Interfaces\ProvidesNamedValue; use OpenMetricsPhp\Exposition\Text\Interfaces\ProvidesSampleString; @@ -16,7 +17,7 @@ final class Gauge implements ProvidesSampleString, ProvidesMeasuredValue /** @var int|null */ private $timestamp; - /** @var LabelCollection */ + /** @var CollectsLabels */ private $labels; /** @@ -50,7 +51,7 @@ public function withLabels( ProvidesNamedValue $label, ProvidesNamedValue ...$la return $this; } - public function withLabelCollection( LabelCollection $labels ) : self + public function withLabelCollection( CollectsLabels $labels ) : self { foreach ( $labels->getIterator() as $label ) { diff --git a/src/Metrics/Histogram.php b/src/Metrics/Histogram.php index 0e5ff7e..9b5461a 100644 --- a/src/Metrics/Histogram.php +++ b/src/Metrics/Histogram.php @@ -12,6 +12,7 @@ use OpenMetricsPhp\Exposition\Text\Metrics\Aggregations\Sum; use OpenMetricsPhp\Exposition\Text\Metrics\Histogram\Bucket; use OpenMetricsPhp\Exposition\Text\Metrics\Histogram\InfiniteBucket; +use Traversable; use function iterator_to_array; use function sort; use const SORT_ASC; @@ -41,11 +42,11 @@ private function __construct( NamesMetric $metricName ) /** * @param GaugeCollection $collection - * @param array $bounds + * @param array $bounds * @param string $suffix * - * @throws InvalidArgumentException * @return Histogram + * @throws InvalidArgumentException */ public static function fromGaugeCollectionWithBounds( GaugeCollection $collection, @@ -55,10 +56,7 @@ public static function fromGaugeCollectionWithBounds( { $histogram = new self( $collection->getMetricName()->withSuffix( $suffix ) ); - foreach ( $histogram->getBucketsForBounds( $collection, $bounds ) as $bucket ) - { - $histogram->buckets[] = $bucket; - } + $histogram->buckets = iterator_to_array( $histogram->getBucketsForBounds( $collection, $bounds ), true ); $countMeasurements = $collection->count(); $histogram->buckets[] = InfiniteBucket::new( $countMeasurements ); @@ -70,12 +68,12 @@ public static function fromGaugeCollectionWithBounds( /** * @param GaugeCollection $collection - * @param array $bounds + * @param array $bounds * + * @return Iterator * @throws InvalidArgumentException - * @return iterable */ - private function getBucketsForBounds( GaugeCollection $collection, array $bounds ) : iterable + private function getBucketsForBounds( GaugeCollection $collection, array $bounds ) : Iterator { sort( $bounds, SORT_NUMERIC | SORT_ASC ); @@ -112,7 +110,7 @@ private function getHelpString() : string return sprintf( '# HELP %s %s', $this->metricName->toString(), $this->help ); } - public function getMetricLines() : Iterator + public function getMetricLines() : Traversable { yield $this->getTypeString(); diff --git a/src/Metrics/Summary.php b/src/Metrics/Summary.php index be554ad..d4059b7 100644 --- a/src/Metrics/Summary.php +++ b/src/Metrics/Summary.php @@ -10,6 +10,8 @@ use OpenMetricsPhp\Exposition\Text\Metrics\Aggregations\Count; use OpenMetricsPhp\Exposition\Text\Metrics\Aggregations\Sum; use OpenMetricsPhp\Exposition\Text\Metrics\Summary\Quantile; +use Traversable; +use function iterator_to_array; use const SORT_ASC; use const SORT_NUMERIC; @@ -24,7 +26,7 @@ final class Summary implements ProvidesMetricLines /** @var string */ private $metricType; - /** @var array */ + /** @var array */ private $quantiles; private function __construct( NamesMetric $metricName ) @@ -37,11 +39,11 @@ private function __construct( NamesMetric $metricName ) /** * @param GaugeCollection $collection - * @param array $quantiles + * @param array $quantiles * @param string $suffix * - * @throws InvalidArgumentException * @return Summary + * @throws InvalidArgumentException */ public static function fromGaugeCollectionWithQuantiles( GaugeCollection $collection, @@ -49,12 +51,8 @@ public static function fromGaugeCollectionWithQuantiles( string $suffix = '' ) : self { - $summary = new self( $collection->getMetricName()->withSuffix( $suffix ) ); - - foreach ( $summary->getQuantiles( $collection, $quantiles ) as $quantile ) - { - $summary->quantiles[] = $quantile; - } + $summary = new self( $collection->getMetricName()->withSuffix( $suffix ) ); + $summary->quantiles = iterator_to_array( $summary->getQuantiles( $collection, $quantiles ), true ); $summary->quantiles[] = Sum::new( $collection->sumMeasuredValues() ); $summary->quantiles[] = Count::new( $collection->count() ); @@ -64,12 +62,12 @@ public static function fromGaugeCollectionWithQuantiles( /** * @param GaugeCollection $collection - * @param array $quantiles + * @param array $quantiles * + * @return Iterator * @throws InvalidArgumentException - * @return iterable */ - private function getQuantiles( GaugeCollection $collection, array $quantiles ) : iterable + private function getQuantiles( GaugeCollection $collection, array $quantiles ) : Iterator { sort( $quantiles, SORT_NUMERIC | SORT_ASC ); @@ -106,7 +104,7 @@ private function getHelpString() : string return sprintf( '# HELP %s %s', $this->metricName->toString(), $this->help ); } - public function getMetricLines() : Iterator + public function getMetricLines() : Traversable { yield $this->getTypeString(); diff --git a/tests/Integration/OpenMetrics/PythonParserTest.php b/tests/Integration/OpenMetrics/PythonParserTest.php index aeb5fb2..abff2af 100644 --- a/tests/Integration/OpenMetrics/PythonParserTest.php +++ b/tests/Integration/OpenMetrics/PythonParserTest.php @@ -59,7 +59,7 @@ public function testCanParseCounterMetricsWithPythonParser() : void private function assertParsedMetricOutput( string $expectedParserOutput, ProvidesMetricLines $metrics ) : void { $filename = tempnam( sys_get_temp_dir(), 'PythonParserTest_' ); - $command = sprintf( 'python %s/parseFile.py %s', __DIR__, $filename ); + $command = sprintf( 'cat "%s" | python %s/parseFile.py', $filename, __DIR__ ); file_put_contents( $filename, $metrics->getMetricsString() . "\n# EOF" ); diff --git a/tests/Integration/OpenMetrics/parseFile.py b/tests/Integration/OpenMetrics/parseFile.py index 3294d89..2ffbeb2 100644 --- a/tests/Integration/OpenMetrics/parseFile.py +++ b/tests/Integration/OpenMetrics/parseFile.py @@ -1,9 +1,8 @@ from prometheus_client.openmetrics.parser import text_string_to_metric_families -from path import path import sys -metrics = path(sys.argv[1]).bytes() +input = sys.stdin.read() -for family in text_string_to_metric_families(metrics): +for family in text_string_to_metric_families(input): for sample in family.samples: print("Name: {0} Labels: {1} Value: {2} Timestamp: {3}".format(*sample)) \ No newline at end of file diff --git a/tests/Traits/EmptyStringProviding.php b/tests/Traits/EmptyStringProviding.php index 9214666..a188504 100644 --- a/tests/Traits/EmptyStringProviding.php +++ b/tests/Traits/EmptyStringProviding.php @@ -4,7 +4,7 @@ trait EmptyStringProviding { - public function emptyStringProvider() : array + public static function emptyStringProvider() : array { return [ [ diff --git a/tests/Unit/Collections/CounterCollectionTest.php b/tests/Unit/Collections/CounterCollectionTest.php index 9a0023d..55d5c33 100644 --- a/tests/Unit/Collections/CounterCollectionTest.php +++ b/tests/Unit/Collections/CounterCollectionTest.php @@ -1,6 +1,6 @@ assertSame( 0, $collection->count() ); /** @var ProvidesNamedValue $labelStub */ - $labelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMockForAbstractClass(); + $labelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMock(); $collection->add( $labelStub ); @@ -70,7 +70,7 @@ public function testCanGetLabelsAsCombinedLabelString() : void $this->assertSame( '', $collection->getCombinedLabelString() ); - $firstLabelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMockForAbstractClass(); + $firstLabelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMock(); $firstLabelStub->method( 'getName' )->willReturn( 'name1' ); $firstLabelStub->method( 'getLabelString' )->willReturn( 'name1="value1"' ); @@ -79,7 +79,7 @@ public function testCanGetLabelsAsCombinedLabelString() : void $this->assertSame( '{name1="value1"}', $collection->getCombinedLabelString() ); - $secondLabelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMockForAbstractClass(); + $secondLabelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMock(); $secondLabelStub->method( 'getName' )->willReturn( 'name2' ); $secondLabelStub->method( 'getLabelString' )->willReturn( 'name2="value2"' ); @@ -97,7 +97,7 @@ public function testLabelWithSameNameOverwritesPreviousLabel() : void { $collection = LabelCollection::new(); - $labelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMockForAbstractClass(); + $labelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMock(); $labelStub->method( 'getName' )->willReturn( 'name1' ); $labelStub->method( 'getLabelString' )->willReturn( 'name1="value1"' ); @@ -106,7 +106,7 @@ public function testLabelWithSameNameOverwritesPreviousLabel() : void $this->assertSame( '{name1="value1"}', $collection->getCombinedLabelString() ); - $overwriteLabelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMockForAbstractClass(); + $overwriteLabelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMock(); $overwriteLabelStub->method( 'getName' )->willReturn( 'name1' ); $overwriteLabelStub->method( 'getLabelString' )->willReturn( 'name1="value2"' ); @@ -126,11 +126,11 @@ public function testCanAddMultipleLabels() : void $this->assertSame( '', $collection->getCombinedLabelString() ); - $firstLabelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMockForAbstractClass(); + $firstLabelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMock(); $firstLabelStub->method( 'getName' )->willReturn( 'name1' ); $firstLabelStub->method( 'getLabelString' )->willReturn( 'name1="value1"' ); - $secondLabelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMockForAbstractClass(); + $secondLabelStub = $this->getMockBuilder( ProvidesNamedValue::class )->getMock(); $secondLabelStub->method( 'getName' )->willReturn( 'name2' ); $secondLabelStub->method( 'getLabelString' )->willReturn( 'name2="value2"' ); diff --git a/tests/Unit/HttpResponse/OutputStreamTest.php b/tests/Unit/HttpResponse/OutputStreamTest.php deleted file mode 100644 index b2833ec..0000000 --- a/tests/Unit/HttpResponse/OutputStreamTest.php +++ /dev/null @@ -1,452 +0,0 @@ -write( 'Unit-Test' ); - $stream->rewind(); - - $this->assertSame( 'Unit-Test', $stream->read( 1024 ) ); - - $stream->close(); - } - - /** - * @throws InvalidArgumentException - * @throws RuntimeException - */ - public function testReadThrowsExceptionWhenNoResourceIsAvailable() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->close(); - - $this->expectException( RuntimeException::class ); - $this->expectExceptionMessage( 'No resource available; cannot read' ); - - /** @noinspection UnusedFunctionResultInspection */ - $stream->read( 1 ); - } - - /** - * @throws InvalidArgumentException - * @throws RuntimeException - */ - public function testReadThrowsExceptionForNonReadableStreams() : void - { - $nonReadableStream = new OutputStream( 'php://output', 'wb' ); - - $this->expectException( RuntimeException::class ); - $this->expectExceptionMessage( 'Stream is not readable' ); - - /** @noinspection UnusedFunctionResultInspection */ - $nonReadableStream->read( 1 ); - } - - /** - * @throws RuntimeException - * @throws InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testSeek() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->write( 'Unit-Test' ); - $stream->seek( 5 ); - - $this->assertSame( 'Test', $stream->read( 1024 ) ); - } - - /** - * @throws InvalidArgumentException - * @throws RuntimeException - */ - public function testSeekThrowsExceptionIfNoResourceIsAvailable() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->close(); - - $this->expectException( RuntimeException::class ); - $this->expectExceptionMessage( 'No resource available; cannot seek position' ); - - $stream->seek( 0 ); - } - - /** - * @throws InvalidArgumentException - * @throws RuntimeException - */ - public function testSeekThrowsExceptionOnErrorSeekingWithinStream() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - - $this->expectException( RuntimeException::class ); - $this->expectExceptionMessage( 'Error seeking within stream' ); - - $stream->seek( 1 ); - } - - /** - * @throws InvalidArgumentException - * @throws RuntimeException - */ - public function testSeekThrowsExceptionForNonSeekableStreams() : void - { - $stream = new OutputStream( 'php://output', 'wb' ); - - $this->expectException( RuntimeException::class ); - $this->expectExceptionMessage( 'Stream is not seekable' ); - - $stream->seek( 1 ); - } - - /** - * @throws InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws RuntimeException - */ - public function testGetSize() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->write( 'Unit-Test' ); - - $this->assertSame( 9, $stream->getSize() ); - - $stream->close(); - - $this->assertNull( $stream->getSize() ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testGetMetadata() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - - $expectedMetaData = [ - 'eof' => false, - 'unread_bytes' => 0, - 'seekable' => true, - 'uri' => 'php://memory', - 'timed_out' => false, - 'blocked' => true, - 'wrapper_type' => 'PHP', - 'stream_type' => 'MEMORY', - 'mode' => 'w+b', - ]; - - $this->assertEquals( $expectedMetaData, $stream->getMetadata() ); - $this->assertSame( 'PHP', $stream->getMetadata( 'wrapper_type' ) ); - $this->assertNull( $stream->getMetadata( 'no-meta-data-key' ) ); - - $stream->close(); - - $this->assertNull( $stream->getMetadata() ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws RuntimeException - */ - public function test__toString() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->write( 'Unit-Test' ); - - $this->assertSame( 'Unit-Test', (string)$stream ); - $this->assertSame( 'Unit-Test', $stream->__toString() ); - - $nonReadableStream = new OutputStream( 'php://stdout', 'wb' ); - $nonReadableStream->write( 'Unit-Test' ); - - $this->assertSame( '', (string)$nonReadableStream ); - $this->assertSame( '', $nonReadableStream->__toString() ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws RuntimeException - */ - public function testEof() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->write( 'Unit-Test' ); - $stream->rewind(); - - $this->assertFalse( $stream->eof() ); - - while ( !$stream->eof() ) - { - $stream->read( 11 ); - } - - $this->assertTrue( $stream->eof() ); - - $stream->close(); - - $this->assertTrue( $stream->eof() ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testIsWritable() : void - { - $tempFile = tempnam( sys_get_temp_dir(), 'Unit-Test-OutputStream-' ); - $writableStream = new OutputStream( $tempFile, 'w+b' ); - - $this->assertTrue( $writableStream->isWritable() ); - - $writableStream->close(); - - $this->assertFalse( $writableStream->isWritable() ); - - $nonWritableStream = new OutputStream( 'php://input', 'rb' ); - - $this->assertFalse( $nonWritableStream->isWritable() ); - - @unlink( $tempFile ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws RuntimeException - */ - public function testRewind() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->write( 'Unit-Test' ); - - $this->assertSame( 9, $stream->tell() ); - - $stream->rewind(); - - $this->assertSame( 0, $stream->tell() ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testClose() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->close(); - - $this->assertNull( $stream->detach() ); - - $stream->close(); - - $this->assertNull( $stream->detach() ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws RuntimeException - */ - public function testTell() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->write( 'Unit-Test' ); - - $this->assertSame( 9, $stream->tell() ); - - $stream->seek( 3 ); - - $this->assertSame( 3, $stream->tell() ); - } - - /** - * @throws InvalidArgumentException - * @throws RuntimeException - */ - public function testTellThrowsExceptionIfNoResourceIsAvailable() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->close(); - - $this->expectException( RuntimeException::class ); - $this->expectExceptionMessage( 'No resource available; cannot tell position' ); - - $stream->tell(); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws RuntimeException - */ - public function testDetach() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->write( 'Unit-Test' ); - - $resource = $stream->detach(); - - $this->assertIsResource( $resource ); - - $this->assertNull( $stream->detach() ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws RuntimeException - */ - public function testGetContents() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->write( 'Unit-Test' ); - - $stream->seek( 5 ); - - $this->assertSame( 'Test', $stream->getContents() ); - - $stream->rewind(); - - $this->assertSame( 'Unit-Test', $stream->getContents() ); - - $stream->close(); - - $this->assertSame( '', $stream->getContents() ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testIsReadable() : void - { - $readableStream = new OutputStream( 'php://memory', 'w+b' ); - - $this->assertTrue( $readableStream->isReadable() ); - - $readableStream->close(); - - $this->assertFalse( $readableStream->isReadable() ); - - $nonReadableStream = new OutputStream( 'php://output', 'wb' ); - - $this->assertFalse( $nonReadableStream->isReadable() ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - * @throws RuntimeException - */ - public function testWrite() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - - $this->assertSame( 9, $stream->write( 'Unit-Test' ) ); - } - - /** - * @throws InvalidArgumentException - * @throws RuntimeException - */ - public function testWriteThrowsExceptionIfNoResourceIsAvailable() : void - { - $stream = new OutputStream( 'php://memory', 'w+b' ); - $stream->close(); - - $this->expectException( RuntimeException::class ); - $this->expectExceptionMessage( 'No resource available; cannot write' ); - - $stream->write( 'Unit-Test' ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testIsSeekable() : void - { - $seekableStream = new OutputStream( 'php://input', 'rb' ); - - $this->assertTrue( $seekableStream->isSeekable() ); - - $seekableStream->close(); - - $this->assertFalse( $seekableStream->isSeekable() ); - } - - /** - * @throws \InvalidArgumentException - * @throws ExpectationFailedException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testCanConstructStreamFromResource() : void - { - $resource = fopen( 'php://memory', 'w+b' ); - $stream = new OutputStream( $resource ); - - $this->assertSame( $resource, $stream->detach() ); - } - - /** - * @throws InvalidArgumentException - */ - public function testThrowsExceptionForInvalidStream() : void - { - $this->expectException( InvalidArgumentException::class ); - $this->expectExceptionMessage( - 'Invalid file provided for stream; must be a valid path with valid permissions' - ); - - new OutputStream( 'file:///does/not/exist' ); - } - - /** - * @throws InvalidArgumentException - */ - public function testThrowsExceptionForInvalidStreamType() : void - { - $this->expectException( InvalidArgumentException::class ); - $this->expectExceptionMessage( - 'Invalid stream provided; must be a string stream identifier or stream resource' - ); - - /** @noinspection PhpParamsInspection */ - new OutputStream( ['stream'] ); - } -} diff --git a/tests/Unit/HttpResponseTest.php b/tests/Unit/HttpResponseTest.php deleted file mode 100644 index 022ebb2..0000000 --- a/tests/Unit/HttpResponseTest.php +++ /dev/null @@ -1,201 +0,0 @@ -getExampleGaugeCollection(); - $counterCollection = $this->getExampleCounterCollection(); - - $expectedOutput = "# TYPE unit_test_gauges gauge\n"; - $expectedOutput .= "# HELP unit_test_gauges Test gauges\n"; - $expectedOutput .= "unit_test_gauges 12.300000\n"; - $expectedOutput .= "unit_test_gauges 45.600000\n"; - $expectedOutput .= "unit_test_gauges 78.900000\n"; - $expectedOutput .= "# TYPE unit_test_counters counter\n"; - $expectedOutput .= "# HELP unit_test_counters Test counters\n"; - $expectedOutput .= "unit_test_counters_total 12.300000\n"; - $expectedOutput .= "unit_test_counters_total 45.600000\n"; - $expectedOutput .= "unit_test_counters_total 78.900000\n"; - - $this->expectOutputString( $expectedOutput ); - - HttpResponse::fromMetricCollections( $gaugeCollection, $counterCollection )->respond(); - - /** @noinspection ForgottenDebugOutputInspection */ - $this->assertContains( 'Content-Type: application/openmetrics-text; charset=utf-8', xdebug_get_headers() ); - } - - /** - * @throws InvalidArgumentException - * @return GaugeCollection - */ - private function getExampleGaugeCollection() : GaugeCollection - { - return GaugeCollection::fromGauges( - MetricName::fromString( 'unit_test_gauges' ), - Gauge::fromValue( 12.3 ), - Gauge::fromValue( 45.6 ), - Gauge::fromValue( 78.9 ) - )->withHelp( - 'Test gauges' - ); - } - - /** - * @throws InvalidArgumentException - * @return CounterCollection - */ - private function getExampleCounterCollection() : CounterCollection - { - return CounterCollection::fromCounters( - MetricName::fromString( 'unit_test_counters' ), - Counter::fromValue( 12.3 ), - Counter::fromValue( 45.6 ), - Counter::fromValue( 78.9 ) - )->withHelp( - 'Test counters' - ); - } - - /** - * @throws InvalidArgumentException - * @throws \InvalidArgumentException - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \RuntimeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testCanGetInstanceWithAddedHeader() : void - { - $response = HttpResponse::fromMetricCollections( $this->getExampleCounterCollection() ); - $withAddedHeader = $response->withAddedHeader( 'X-Test', 'Unit' ); - - $this->assertNotSame( $withAddedHeader, $response ); - $this->assertCount( 2, $withAddedHeader->getHeaders() ); - $this->assertCount( 1, $response->getHeaders() ); - } - - /** - * @throws InvalidArgumentException - * @throws \InvalidArgumentException - * @throws \OpenMetricsPhp\Exposition\Text\Exceptions\RuntimeException - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \RuntimeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testCanGetInstanceWithBody() : void - { - $stream = new HttpResponse\OutputStream( 'php://memory', 'w+b' ); - $stream->write( 'Unit-Test' ); - - $response = HttpResponse::fromMetricCollections( $this->getExampleCounterCollection() ); - $withBody = $response->withBody( $stream ); - - $this->assertNotSame( $withBody, $response ); - $this->assertNotSame( $withBody->getBody(), $response->getBody() ); - $this->assertSame( 9, $withBody->getBody()->getSize() ); - $this->assertSame( 179, $response->getBody()->getSize() ); - } - - /** - * @throws InvalidArgumentException - * @throws \InvalidArgumentException - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \RuntimeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testCanGetInstanceWithProtocolVersion() : void - { - $response = HttpResponse::fromMetricCollections( $this->getExampleCounterCollection() ); - $withProtocolVersion = $response->withProtocolVersion( '1.0' ); - - $this->assertNotSame( $response, $withProtocolVersion ); - $this->assertSame( '1.0', $withProtocolVersion->getProtocolVersion() ); - $this->assertSame( '1.1', $response->getProtocolVersion() ); - } - - /** - * @throws InvalidArgumentException - * @throws \InvalidArgumentException - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \RuntimeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testCanGetInstanceWithoutHeader() : void - { - $response = HttpResponse::fromMetricCollections( $this->getExampleCounterCollection() ); - $withoutHeader = $response->withoutHeader( 'Content-Type' ); - - $this->assertNotSame( $withoutHeader, $response ); - $this->assertCount( 0, $withoutHeader->getHeaders() ); - $this->assertCount( 1, $response->getHeaders() ); - } - - /** - * @throws InvalidArgumentException - * @throws \InvalidArgumentException - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \RuntimeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testCanGetInstanceWithStatus() : void - { - $response = HttpResponse::fromMetricCollections( $this->getExampleCounterCollection() ); - $withStatus = $response->withStatus( 500, 'Internal Server Error' ); - - $this->assertNotSame( $withStatus, $response ); - $this->assertSame( 500, $withStatus->getStatusCode() ); - $this->assertSame( 'Internal Server Error', $withStatus->getReasonPhrase() ); - $this->assertSame( 200, $response->getStatusCode() ); - $this->assertSame( 'OK', $response->getReasonPhrase() ); - } - - /** - * @throws InvalidArgumentException - * @throws \InvalidArgumentException - * @throws \PHPUnit\Framework\ExpectationFailedException - * @throws \RuntimeException - * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException - */ - public function testCanGetDefaultValues() : void - { - $response = HttpResponse::fromMetricCollections( - $this->getExampleCounterCollection(), - $this->getExampleGaugeCollection() - ); - - $this->assertSame( - ['Content-Type' => ['application/openmetrics-text; charset=utf-8']], - $response->getHeaders() - ); - - $this->assertSame( '1.1', $response->getProtocolVersion() ); - $this->assertSame( 'OK', $response->getReasonPhrase() ); - $this->assertSame( 200, $response->getStatusCode() ); - $this->assertSame( - 'application/openmetrics-text; charset=utf-8', - $response->getHeaderLine( 'Content-Type' ) - ); - $this->assertSame( '', $response->getHeaderLine( 'Not-existing-key' ) ); - $this->assertTrue( $response->hasHeader( 'Content-Type' ) ); - } -} diff --git a/tests/Unit/Types/LabelTest.php b/tests/Unit/Types/LabelTest.php index ba9652c..001f8eb 100644 --- a/tests/Unit/Types/LabelTest.php +++ b/tests/Unit/Types/LabelTest.php @@ -6,26 +6,28 @@ use OpenMetricsPhp\Exposition\Text\Tests\Traits\EmptyStringProviding; use OpenMetricsPhp\Exposition\Text\Types\Label; use PHPUnit\Framework\TestCase; +use Traversable; final class LabelTest extends TestCase { use EmptyStringProviding; /** - * @param string $value + * @param string $string * * @throws InvalidArgumentException * @throws \PHPUnit\Framework\AssertionFailedError * * @dataProvider emptyStringProvider */ - public function testThrowsExceptionForEmptyValue( string $value ) : void + #[DataProvider('emptyStringProvider')] + public function testThrowsExceptionForEmptyValue( string $string ) : void { $this->expectException( InvalidArgumentException::class ); $this->expectExceptionMessage( 'Label value cannot be empty.' ); /** @noinspection UnusedFunctionResultInspection */ - Label::fromNameAndValue( 'name', $value ); + Label::fromNameAndValue( 'name', $string ); $this->fail( 'Expected an InvalidArgumentException to be thrown for an empty label value.' ); } @@ -49,9 +51,15 @@ public function testThrowsExceptionForInvalidLabelName( string $name ) : void $this->fail( 'Expected an InvalidArgumentException to be thrown for invalid label name.' ); } - public function invalidLabelNameProvider() : iterable + public static function invalidLabelNameProvider() : Traversable { - yield from $this->emptyStringProvider(); + yield from array_map( + static function ( array $record ) : array + { + return ['name' => $record['string']]; + }, + self::emptyStringProvider() + ); yield from [ [ @@ -95,7 +103,7 @@ public function testCanGetLabelString( string $name, string $value, string $expe $this->assertSame( $expectedLabelString, $label->getLabelString() ); } - public function labelStringsProvider() : array + public static function labelStringsProvider() : array { return [ [ @@ -164,7 +172,7 @@ public function testCanGetNameAndValueFromLabelString( $this->assertSame( $expectedValue, $label->getValue() ); } - public function labelStringToNameValueProvider() : array + public static function labelStringToNameValueProvider() : array { return [ [ @@ -214,7 +222,7 @@ public function testThrowsExceptionForInvalidLabelStrings( string $labelString ) $this->fail( 'Expected exception for invalid label string.' ); } - public function invalidLabelStringProvider() : array + public static function invalidLabelStringProvider() : array { return [ [ diff --git a/tests/Unit/Types/MetricNameTest.php b/tests/Unit/Types/MetricNameTest.php index 6140a7f..3190ed2 100644 --- a/tests/Unit/Types/MetricNameTest.php +++ b/tests/Unit/Types/MetricNameTest.php @@ -6,6 +6,8 @@ use OpenMetricsPhp\Exposition\Text\Tests\Traits\EmptyStringProviding; use OpenMetricsPhp\Exposition\Text\Types\MetricName; use PHPUnit\Framework\TestCase; +use Traversable; +use function array_map; final class MetricNameTest extends TestCase { @@ -30,9 +32,15 @@ public function testThrowsExceptionForInvalidMetricName( string $metricName ) : $this->fail( 'Expected an InvalidArgumentException to be thrown for invalid metric name.' ); } - public function invalidMetricNameProvider() : iterable + public static function invalidMetricNameProvider() : Traversable { - yield from $this->emptyStringProvider(); + yield from array_map( + static function ( array $record ) : array + { + return ['metricName' => $record['string']]; + }, + self::emptyStringProvider() + ); yield from [ [ @@ -67,7 +75,7 @@ public function testCanGetMetricNameAsString( string $metricName, string $expect $this->assertSame( $expectedString, $metricNameObject->toString() ); } - public function validMetricNameProvider() : array + public static function validMetricNameProvider() : array { return [ [ @@ -105,7 +113,7 @@ public function testCanCheckIfMetricNamesAreEqual( string $name, string $otherNa $this->assertSame( $expectedResult, $other->equals( $metricName ) ); } - public function equalityMetricNameProvider() : array + public static function equalityMetricNameProvider() : array { return [ [ diff --git a/tests/phpunit-10.xml b/tests/phpunit-10.xml new file mode 100644 index 0000000..03829d4 --- /dev/null +++ b/tests/phpunit-10.xml @@ -0,0 +1,16 @@ + + + + + ../tests/Unit + + + ../tests/Integration + + + + + ../src + + + diff --git a/tests/phpunit-8.xml b/tests/phpunit-8.xml new file mode 100644 index 0000000..ad9380d --- /dev/null +++ b/tests/phpunit-8.xml @@ -0,0 +1,22 @@ + + + + ../tests/Unit + + + ../tests/Integration + + + + + ../src + + + diff --git a/tests/phpunit-9.xml b/tests/phpunit-9.xml new file mode 100644 index 0000000..18b6f21 --- /dev/null +++ b/tests/phpunit-9.xml @@ -0,0 +1,16 @@ + + + + + ../src + + + + + ../tests/Unit + + + ../tests/Integration + + +