From c665c1275e4bb463d07e3e63d55ecc01d7bf09c5 Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 31 Aug 2026 14:53:07 +0200 Subject: [PATCH 1/8] update phpunit to ^9.6.33 in development dependencies --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 130d5ab..a0731c2 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ } }, "require-dev": { - "phpunit/phpunit": "^9.0", + "phpunit/phpunit": "^9.6.33", "phpstan/phpstan": "^2.0", "squizlabs/php_codesniffer": "^4.0" } From 3fb3fa2be22f5806cbbf68acb04b757b95fab4cf Mon Sep 17 00:00:00 2001 From: Gregor Date: Mon, 31 Aug 2026 14:53:14 +0200 Subject: [PATCH 2/8] synchronize CI pipeline --- .github/workflows/main.yml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9226974..1a86c1e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,11 +2,7 @@ name: "CI" on: push: - branches: - - "main" pull_request: - branches: - - "main" workflow_dispatch: permissions: @@ -17,7 +13,6 @@ concurrency: cancel-in-progress: true jobs: - # composer validation composer: name: "composer config validation" runs-on: "ubuntu-latest" @@ -30,9 +25,8 @@ jobs: - name: "Validate composer.json" run: "composer validate --strict" - # PHP lint, PHPUnit, PHPStan and PHPCS for different PHP versions php: - name: "PHP ${{ matrix.php-version }} (${{ matrix.dependencies }}) - PHPUnit, PHPStan, PHPCS" + name: "PHP ${{ matrix.php-version }} (${{ matrix.dependencies }})" needs: "composer" runs-on: "ubuntu-latest" timeout-minutes: 15 @@ -60,7 +54,6 @@ jobs: extensions: "json" coverage: "xdebug" tools: "composer:v2" - cache: "composer" - name: "install composer dependencies (locked)" if: "matrix.dependencies == 'locked'" run: "composer install --prefer-dist --no-interaction --no-progress" @@ -68,12 +61,13 @@ jobs: if: "matrix.dependencies == 'lowest'" run: "composer update --prefer-lowest --prefer-dist --no-interaction --no-progress" - name: "audit dependencies for known security vulnerabilities" + if: "matrix.php-version == '8.1'" run: "composer audit" - name: "lint PHP files" run: "find exceptions/ src/ tests/ -type f -name '*.php' -print0 | xargs -0 -n1 php -l" - name: "run PHPUnit" - run: "php vendor/bin/phpunit --coverage-text" + run: "php vendor/bin/phpunit --coverage-text --colors=never" - name: "run PHPStan" - run: "vendor/bin/phpstan analyse --no-progress" + run: "php vendor/bin/phpstan analyse --no-progress" - name: "run PHPCS" - run: "vendor/bin/phpcs" + run: "php vendor/bin/phpcs" From 87af830a7960129a08a54e98478d6f41cae93efc Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 1 Sep 2026 11:34:18 +0200 Subject: [PATCH 3/8] synchronize dependencies --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index a0731c2..2d20470 100644 --- a/composer.json +++ b/composer.json @@ -19,8 +19,8 @@ "require": { "php": "^8.1", "ext-json": "*", - "php-sap/interfaces": "^5.1", - "php-sap/datetime": "^1.5" + "php-sap/interfaces": "^5.1.5", + "php-sap/datetime": "^1.5.1" }, "autoload": { "psr-4": { @@ -35,7 +35,7 @@ }, "require-dev": { "phpunit/phpunit": "^9.6.33", - "phpstan/phpstan": "^2.0", + "phpstan/phpstan": "^2.2.3", "squizlabs/php_codesniffer": "^4.0" } } From 0c8f03c1970e4b4aa5583bb1237dd2d103a175e8 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 1 Sep 2026 11:36:35 +0200 Subject: [PATCH 4/8] synchronize AGENTS.md --- AGENTS.md | 135 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 53 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 32237de..0e2c1c1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,56 +5,31 @@ `php-sap/common` is a **PHP library** providing SAP RFC (Remote Function Call) abstractions that are independent of the underlying PHP SAP extension (e.g. `saprfc` or `sapnwrfc`). It defines abstract base classes, API type descriptors, configuration models, and exceptions -consumed by concrete SAP connector packages. +consumed by concrete SAP connector packages. Part of [PHP/SAP](https://php-sap.github.io). Namespaces: - `phpsap\classes\*` → `src/` - `phpsap\exceptions\*` → `exceptions/` - `tests\phpsap\classes\*` → `tests/` -## Developer Commands +## Ecosystem -All commands run inside official PHP Docker images so the host machine does not need a -local PHP installation. Use PHP 8.1, 8.2, and 8.3 (matching the CI matrix in -`.github/workflows/main.yml`) for anything version-sensitive (PHPStan, PHP lint). -If you are behind a proxy, forward `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` into the -container whenever the command needs network access (e.g. `composer install`). +[PHP/SAP](https://php-sap.github.io) is split across five focused repositories that build on +each other instead of one monolithic package: -```bash -# Install/update dependencies (needs network access -> forward proxy settings) -docker run --rm --init --interactive --tty \ - --user "$(id -u)":"$(id -g)" \ - --env HTTP_PROXY --env HTTPS_PROXY --env NO_PROXY \ - --volume "$(pwd)":/app --workdir /app \ - composer:2 install +| Repository | Role | Depends on (`composer.json`) | +|------------------------------|-------------------------------------------------------------------------------------------------------|-------------------------------------------------------| +| `php-sap/interfaces` | Contract-only interfaces (`IApi`, `IConfiguration`, `IFunction`, exceptions). No concrete classes. | — | +| `php-sap/datetime` | SAP date/time format support on top of native `DateTime`/`DateInterval`. | — | +| `php-sap/common` | Generic abstract classes, API/config value objects, and exceptions implementing `interfaces`. | `interfaces`, `datetime` | +| `php-sap/integration-tests` | Shared abstract PHPUnit test infrastructure and SAP module mocks reused by concrete connector packages. | `interfaces`, `common`, `datetime` | +| `php-sap/saprfc-kralik` | Concrete adapter for Gregor Kralik's `ext-sapnwrfc` extension. | `interfaces`, `common` (+ `integration-tests` for tests only) | -# Run tests (no network access needed) -docker run --rm --init \ - --user "$(id -u)":"$(id -g)" \ - --volume "$(pwd)":/app --workdir /app \ - php:8.1-cli php vendor/bin/phpunit +**→ You are here: `php-sap/common`** — the generic implementation of `interfaces`. -# Fix code style (run first, no network access needed) -docker run --rm --init \ - --user "$(id -u)":"$(id -g)" \ - --volume "$(pwd)":/app --workdir /app \ - php:8.1-cli php vendor/bin/phpcbf - -# Check remaining style issues (no network access needed) -docker run --rm --init \ - --user "$(id -u)":"$(id -g)" \ - --volume "$(pwd)":/app --workdir /app \ - php:8.1-cli php vendor/bin/phpcs - -# Run static analysis for every supported PHP version (no network access needed; -# --memory-limit=-1 works around the image's low default memory_limit) -for PHP_VERSION in 8.1 8.2 8.3; do - docker run --rm --init \ - --user "$(id -u)":"$(id -g)" \ - --volume "$(pwd)":/app --workdir /app \ - "php:${PHP_VERSION}-cli" php vendor/bin/phpstan analyse --memory-limit=-1 -done -``` +This package implements the interfaces generically; extension-specific glue (marshaling +parameters to a native SAP module) belongs in a concrete connector like `saprfc-kralik`, +not here. ## Architecture @@ -109,24 +84,78 @@ Both implement `ISapException` from `php-sap/interfaces`. - Subclasses implement `connect()`, `execute()`, `extractApi()`. - Stores parameters via `JsonSerializable`'s `set()`/`get()` methods. -## Testing Conventions - -- Test helpers live in `tests/helper/` (e.g. `AbstractFunctionInstance.php` — provides - fake `extractApi()` and `invoke()` via static properties for controlled test scenarios). -- Tests mirror source layout: `tests/Api/`, `tests/Config/`, `tests/Util/`. -- PHPUnit 9, bootstrap: `vendor/autoload.php`, strict coverage enforced via `phpunit.xml`. -- PHPStan level 9 (`phpstan.neon`); intentional type mismatches in tests are suppressed - with `@phpstan-ignore-next-line`. - -## Key Interfaces +### Key Interfaces All public contracts are defined in `php-sap/interfaces` (vendor dependency): `IApi`, `IApiElement`, `IValue`, `IMember`, `IStruct`, `ITable`, `IFunction`, `IConfiguration`, `IConfigTypeA`, `IConfigTypeB`, `ISapException`. When adding new public methods, check this package for the matching interface first. -## CI Tooling Notes +## Developer Workflows + +All commands run inside official PHP Docker images so the host machine does not need a +local PHP installation. Use PHP 8.1, 8.2, and 8.3 (matching the CI matrix in +`.github/workflows/main.yml`) for anything version-sensitive (PHPStan, PHP lint). +If you are behind a proxy, forward `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` into the +container whenever the command needs network access (e.g. `composer install`). + +```bash +# Install/update dependencies (needs network access -> forward proxy settings) +docker run --rm --init --interactive --tty \ + --user "$(id -u)":"$(id -g)" \ + --env HTTP_PROXY --env HTTPS_PROXY --env NO_PROXY \ + --volume "$(pwd)":/app --workdir /app \ + composer:2 install + +# Run tests (no network access needed) +docker run --rm --init \ + --user "$(id -u)":"$(id -g)" \ + --volume "$(pwd)":/app --workdir /app \ + php:8.1-cli php vendor/bin/phpunit + +# Fix code style (run first, no network access needed) +docker run --rm --init \ + --user "$(id -u)":"$(id -g)" \ + --volume "$(pwd)":/app --workdir /app \ + php:8.1-cli php vendor/bin/phpcbf + +# Check remaining style issues (no network access needed) +docker run --rm --init \ + --user "$(id -u)":"$(id -g)" \ + --volume "$(pwd)":/app --workdir /app \ + php:8.1-cli php vendor/bin/phpcs + +# Run static analysis for every supported PHP version (no network access needed; +# --memory-limit=-1 works around the image's low default memory_limit) +for PHP_VERSION in 8.1 8.2 8.3; do + docker run --rm --init \ + --user "$(id -u)":"$(id -g)" \ + --volume "$(pwd)":/app --workdir /app \ + "php:${PHP_VERSION}-cli" php vendor/bin/phpstan analyse --memory-limit=-1 +done +``` + +`phpstan/phpstan` and `squizlabs/php_codesniffer` are managed as Composer `require-dev` +dependencies (no separate download step needed, unlike `saprfc-kralik`'s `phpcs.phar`). +PHPStan runs at **level 9** (`phpstan.neon`); intentional type mismatches in tests are +suppressed with `@phpstan-ignore-next-line`. + +## Conventions + +- Test helpers live in `tests/helper/` (e.g. `AbstractFunctionInstance.php` — provides + fake `extractApi()` and `invoke()` via static properties for controlled test scenarios). +- Tests mirror source layout: `tests/Api/`, `tests/Config/`, `tests/Util/`. +- PHPUnit 9, bootstrap: `vendor/autoload.php`, strict coverage enforced via `phpunit.xml`. -- `phpstan/phpstan` and `squizlabs/php_codesniffer` are managed as Composer `require-dev` dependencies. -- CI runs `vendor/bin/phpstan` and `vendor/bin/phpcs` after `composer install`. +## Safe Change Strategy for Agents + +- Before adding a public method, check `php-sap/interfaces` for the matching contract first + — this package must stay a faithful, generic implementation of those interfaces. +- Before adding a new SAP type, extend `TypeTrait`'s constants and the corresponding + `CastPrimitivesTrait` logic together; don't add one without the other. +- Extension-specific glue (e.g. marshaling parameters for a native SAP module) does not + belong here — that's the job of concrete connector packages like `saprfc-kralik`. +- Keep new code PHPStan level 9 clean; only suppress with `@phpstan-ignore-next-line` for + intentional test-only type mismatches, matching the existing pattern. +- Write documentation, comments, and new code in English to match the repository style. From eb816f775320ef4a5b7496237ecc8c0278bf3a8d Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 1 Sep 2026 12:59:14 +0200 Subject: [PATCH 5/8] add Makefile --- .gitattributes | 1 + Makefile | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 Makefile diff --git a/.gitattributes b/.gitattributes index 696f291..42d5d28 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,6 +5,7 @@ tests/ export-ignore .gitignore export-ignore AGENTS.md export-ignore composer.* export-ignore +Makefile export-ignore phpcs.xml export-ignore phpstan.neon export-ignore phpunit.xml export-ignore diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c842eda --- /dev/null +++ b/Makefile @@ -0,0 +1,95 @@ +SHELL := /bin/sh +.DEFAULT_GOAL := help + +COMPOSER_IMAGE := composer:2 +COMPOSER_CACHE_DIR := $(HOME)/.cache/composer +DOCKER_USER := --user "$$(id -u)":"$$(id -g)" +DOCKER_MOUNT := --volume "$$(pwd)":/app --workdir /app + +# --prefer-lowest only has an effect on "composer update", not "composer +# install" (which just reproduces composer.lock) - switch commands so the +# flag actually does something, matching the CI "lowest" matrix job. +ifdef DEPENDENCIES_LOWEST +COMPOSER_INSTALL_CMD := update --prefer-lowest +else +COMPOSER_INSTALL_CMD := install +endif + +# Optional: set CA_CERT_FILE to a PEM file (e.g. a corporate proxy root CA) +# to make it trusted for HTTPS network access inside the containers used by +# "install" and "audit" (e.g. 'make install PHP_VERSION=8.1 CA_CERT_FILE=/path/to/ca.pem'). +ifdef CA_CERT_FILE +CA_MOUNT := --volume "$(CA_CERT_FILE)":/tmp/extra-ca.crt:ro +CA_TRUST_CMD := cat /etc/ssl/certs/ca-certificates.crt /tmp/extra-ca.crt > /tmp/ca-bundle.pem && export CURL_CA_BUNDLE=/tmp/ca-bundle.pem SSL_CERT_FILE=/tmp/ca-bundle.pem && +else +CA_MOUNT := +CA_TRUST_CMD := +endif + +.PHONY: help clean check-php-version install test lint analyze beautify sniff audit validate + +help: ## Show this help + @grep -E '^[a-zA-Z_-]+:.*## ' $(MAKEFILE_LIST) | sort | \ + awk 'BEGIN {FS = ":.*## "}; {printf " \033[36m%-10s\033[0m %s\n", $$1, $$2}' + +clean: ## Remove vendor/ composer.lock and .phpunit.result.cache (reset to a fresh checkout) + rm -rf vendor composer.lock .phpunit.result.cache + +check-php-version: + @if [ -z "$(PHP_VERSION)" ]; then \ + echo "Error: PHP_VERSION must be set, e.g. 'make $(MAKECMDGOALS) PHP_VERSION=8.1'." >&2; \ + exit 1; \ + fi + +install: check-php-version ## Install composer dependencies for PHP_VERSION (set DEPENDENCIES_LOWEST for --prefer-lowest, CA_CERT_FILE for a corporate proxy CA) + @mkdir -p "$(COMPOSER_CACHE_DIR)" + docker run --rm -t --init $(DOCKER_USER) \ + --env HTTP_PROXY --env HTTPS_PROXY --env NO_PROXY \ + --env COMPOSER_CACHE_DIR=/tmp/composer-cache \ + --volume "$(COMPOSER_CACHE_DIR)":/tmp/composer-cache \ + $(CA_MOUNT) \ + $(DOCKER_MOUNT) \ + $(COMPOSER_IMAGE) sh -c '\ + $(CA_TRUST_CMD) \ + composer config platform.php "$(PHP_VERSION)" && \ + composer $(COMPOSER_INSTALL_CMD) --prefer-dist --no-interaction --no-progress; \ + status=$$?; \ + composer config --unset platform.php; \ + composer config --unset platform 2>/dev/null; \ + composer config --unset config 2>/dev/null; \ + if [ $$status -eq 0 ]; then \ + composer update --lock --no-interaction --no-progress; \ + status=$$?; \ + fi; \ + exit $$status \ + ' + +test: check-php-version ## Run PHPUnit for PHP_VERSION + docker run --rm -t --init $(DOCKER_USER) $(DOCKER_MOUNT) \ + "php:$(PHP_VERSION)-cli" php vendor/bin/phpunit + +lint: check-php-version ## Syntax-check every .php file in exceptions/, src/ and tests/ for PHP_VERSION + docker run --rm --init $(DOCKER_USER) $(DOCKER_MOUNT) \ + "php:$(PHP_VERSION)-cli" sh -c "find exceptions src tests -type f -name '*.php' -print0 | xargs -0 -n1 php -l" + +analyze: check-php-version ## Run PHPStan for PHP_VERSION + docker run --rm -t --init $(DOCKER_USER) $(DOCKER_MOUNT) \ + "php:$(PHP_VERSION)-cli" php vendor/bin/phpstan analyse --memory-limit=-1 + +beautify: check-php-version ## Run PHPCBF (auto-fix code style) for PHP_VERSION + docker run --rm --init $(DOCKER_USER) $(DOCKER_MOUNT) \ + "php:$(PHP_VERSION)-cli" php vendor/bin/phpcbf + +sniff: check-php-version ## Run PHPCS (code style check) for PHP_VERSION + docker run --rm --init $(DOCKER_USER) $(DOCKER_MOUNT) \ + "php:$(PHP_VERSION)-cli" php vendor/bin/phpcs + +audit: ## Run composer audit (checks dependencies for known vulnerabilities; CA_CERT_FILE for a corporate proxy CA) + docker run --rm --init $(DOCKER_USER) \ + --env HTTP_PROXY --env HTTPS_PROXY --env NO_PROXY \ + $(CA_MOUNT) $(DOCKER_MOUNT) \ + $(COMPOSER_IMAGE) sh -c '$(CA_TRUST_CMD) composer audit' + +validate: ## Run composer validate --strict + docker run --rm --init $(DOCKER_USER) $(DOCKER_MOUNT) \ + $(COMPOSER_IMAGE) composer validate --strict From 3faf367cddc5cf403286abed992f6839694da165 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 1 Sep 2026 13:01:17 +0200 Subject: [PATCH 6/8] update README.md and AGENTS.md to use Makefile --- AGENTS.md | 91 ++++++++++++++++++++++++++++--------------------------- README.md | 11 +++++++ 2 files changed, 57 insertions(+), 45 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0e2c1c1..a6e2d7d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,13 +17,13 @@ Namespaces: [PHP/SAP](https://php-sap.github.io) is split across five focused repositories that build on each other instead of one monolithic package: -| Repository | Role | Depends on (`composer.json`) | -|------------------------------|-------------------------------------------------------------------------------------------------------|-------------------------------------------------------| -| `php-sap/interfaces` | Contract-only interfaces (`IApi`, `IConfiguration`, `IFunction`, exceptions). No concrete classes. | — | -| `php-sap/datetime` | SAP date/time format support on top of native `DateTime`/`DateInterval`. | — | -| `php-sap/common` | Generic abstract classes, API/config value objects, and exceptions implementing `interfaces`. | `interfaces`, `datetime` | -| `php-sap/integration-tests` | Shared abstract PHPUnit test infrastructure and SAP module mocks reused by concrete connector packages. | `interfaces`, `common`, `datetime` | -| `php-sap/saprfc-kralik` | Concrete adapter for Gregor Kralik's `ext-sapnwrfc` extension. | `interfaces`, `common` (+ `integration-tests` for tests only) | +| Repository | Role | Depends on (`composer.json`) | +|-----------------------------|---------------------------------------------------------------------------------------------------------|---------------------------------------------------------------| +| `php-sap/interfaces` | Contract-only interfaces (`IApi`, `IConfiguration`, `IFunction`, exceptions). No concrete classes. | — | +| `php-sap/datetime` | SAP date/time format support on top of native `DateTime`/`DateInterval`. | — | +| `php-sap/common` | Generic abstract classes, API/config value objects, and exceptions implementing `interfaces`. | `interfaces`, `datetime` | +| `php-sap/integration-tests` | Shared abstract PHPUnit test infrastructure and SAP module mocks reused by concrete connector packages. | `interfaces`, `common`, `datetime` | +| `php-sap/saprfc-kralik` | Concrete adapter for Gregor Kralik's `ext-sapnwrfc` extension. | `interfaces`, `common` (+ `integration-tests` for tests only) | **→ You are here: `php-sap/common`** — the generic implementation of `interfaces`. @@ -93,48 +93,46 @@ When adding new public methods, check this package for the matching interface fi ## Developer Workflows -All commands run inside official PHP Docker images so the host machine does not need a -local PHP installation. Use PHP 8.1, 8.2, and 8.3 (matching the CI matrix in -`.github/workflows/main.yml`) for anything version-sensitive (PHPStan, PHP lint). -If you are behind a proxy, forward `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY` into the -container whenever the command needs network access (e.g. `composer install`). +All commands run through the `Makefile` via Docker, so the host machine does not need a +local PHP installation. Run `make help` for the full target list. Use PHP 8.1, 8.2, and +8.3 (matching the CI matrix in `.github/workflows/main.yml`) for anything +version-sensitive (PHPStan, PHP lint, tests). If you are behind a proxy, `install` and +`audit` already forward `HTTP_PROXY`/`HTTPS_PROXY`/`NO_PROXY`; pass +`CA_CERT_FILE=/path/to/ca.pem` to trust a corporate proxy root CA inside the container. ```bash -# Install/update dependencies (needs network access -> forward proxy settings) -docker run --rm --init --interactive --tty \ - --user "$(id -u)":"$(id -g)" \ - --env HTTP_PROXY --env HTTPS_PROXY --env NO_PROXY \ - --volume "$(pwd)":/app --workdir /app \ - composer:2 install - -# Run tests (no network access needed) -docker run --rm --init \ - --user "$(id -u)":"$(id -g)" \ - --volume "$(pwd)":/app --workdir /app \ - php:8.1-cli php vendor/bin/phpunit - -# Fix code style (run first, no network access needed) -docker run --rm --init \ - --user "$(id -u)":"$(id -g)" \ - --volume "$(pwd)":/app --workdir /app \ - php:8.1-cli php vendor/bin/phpcbf - -# Check remaining style issues (no network access needed) -docker run --rm --init \ - --user "$(id -u)":"$(id -g)" \ - --volume "$(pwd)":/app --workdir /app \ - php:8.1-cli php vendor/bin/phpcs - -# Run static analysis for every supported PHP version (no network access needed; -# --memory-limit=-1 works around the image's low default memory_limit) -for PHP_VERSION in 8.1 8.2 8.3; do - docker run --rm --init \ - --user "$(id -u)":"$(id -g)" \ - --volume "$(pwd)":/app --workdir /app \ - "php:${PHP_VERSION}-cli" php vendor/bin/phpstan analyse --memory-limit=-1 -done +# Install/update dependencies for a given PHP version (set DEPENDENCIES_LOWEST=1 for +# --prefer-lowest, matching the CI "lowest" matrix job) +make install PHP_VERSION=8.1 + +# Run PHPUnit +make test PHP_VERSION=8.1 + +# Syntax-check every .php file in exceptions/, src/ and tests/, matches CI +make lint PHP_VERSION=8.1 + +# Run PHPStan +make analyze PHP_VERSION=8.1 + +# Auto-fix code style (run this before "sniff") +make beautify PHP_VERSION=8.1 + +# Check code style (uses phpcs.xml) +make sniff PHP_VERSION=8.1 + +# Check dependencies for known vulnerabilities +make audit + +# Run composer validate --strict +make validate ``` +**Always use these Makefile targets instead of inventing ad-hoc `docker run`/`composer`/ +`php` commands.** If a task needs something the Makefile doesn't expose directly (e.g. +PHPUnit for a single test file/method), take the exact `docker run` invocation from the +matching Makefile target (image, `DOCKER_USER`, `DOCKER_MOUNT`, env forwarding) and only +append the extra arguments — don't build the command from scratch. + `phpstan/phpstan` and `squizlabs/php_codesniffer` are managed as Composer `require-dev` dependencies (no separate download step needed, unlike `saprfc-kralik`'s `phpcs.phar`). PHPStan runs at **level 9** (`phpstan.neon`); intentional type mismatches in tests are @@ -158,4 +156,7 @@ suppressed with `@phpstan-ignore-next-line`. - Keep new code PHPStan level 9 clean; only suppress with `@phpstan-ignore-next-line` for intentional test-only type mismatches, matching the existing pattern. - Write documentation, comments, and new code in English to match the repository style. +- Always run QA/build commands through the `Makefile` targets, not self-invented `docker run` + commands. For one-off variants (a single test, a single file), base the invocation on the + relevant Makefile target and only append the extra arguments. diff --git a/README.md b/README.md index 13d5f9c..8d4b747 100644 --- a/README.md +++ b/README.md @@ -5,5 +5,16 @@ Exceptions and abstract classes containing logic for [PHP/SAP][phpsap] that is not specific to the underlying PHP module. +## Development + +All development commands (install, test, lint, analyze, beautify, sniff, audit, +validate) run via Docker through the `Makefile`, so no local PHP installation is +needed. Run `make help` to list all targets. Most targets require `PHP_VERSION`, +e.g.: + +```sh +make install PHP_VERSION=8.1 +``` + [phpsap]: https://php-sap.github.io [license-mit]: https://img.shields.io/badge/license-MIT-blue.svg From f5f4b6c17091a27b25664ea3353f6345b8f20cb7 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 1 Sep 2026 13:03:32 +0200 Subject: [PATCH 7/8] update dependencies in composer.json --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 2d20470..3f77e53 100644 --- a/composer.json +++ b/composer.json @@ -19,8 +19,8 @@ "require": { "php": "^8.1", "ext-json": "*", - "php-sap/interfaces": "^5.1.5", - "php-sap/datetime": "^1.5.1" + "php-sap/interfaces": "^5.1.6", + "php-sap/datetime": "^1.5.2" }, "autoload": { "psr-4": { From bd6079ec147bd2cf0db160074d36d97dfee32e79 Mon Sep 17 00:00:00 2001 From: Gregor Date: Tue, 1 Sep 2026 13:03:44 +0200 Subject: [PATCH 8/8] add -t flag to docker run calls --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index c842eda..8428fbc 100644 --- a/Makefile +++ b/Makefile @@ -85,11 +85,11 @@ sniff: check-php-version ## Run PHPCS (code style check) for PHP_VERSION "php:$(PHP_VERSION)-cli" php vendor/bin/phpcs audit: ## Run composer audit (checks dependencies for known vulnerabilities; CA_CERT_FILE for a corporate proxy CA) - docker run --rm --init $(DOCKER_USER) \ + docker run --rm -t --init $(DOCKER_USER) \ --env HTTP_PROXY --env HTTPS_PROXY --env NO_PROXY \ $(CA_MOUNT) $(DOCKER_MOUNT) \ $(COMPOSER_IMAGE) sh -c '$(CA_TRUST_CMD) composer audit' validate: ## Run composer validate --strict - docker run --rm --init $(DOCKER_USER) $(DOCKER_MOUNT) \ + docker run --rm -t --init $(DOCKER_USER) $(DOCKER_MOUNT) \ $(COMPOSER_IMAGE) composer validate --strict