Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
.gitignore export-ignore
AGENTS.md export-ignore
composer.* export-ignore
Makefile export-ignore
phpcs.xml export-ignore
phpstan.neon export-ignore
README.md export-ignore
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

- package-ecosystem: "composer"
directory: "/"
schedule:
interval: "weekly"
4 changes: 1 addition & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
run: "composer validate --strict"

php:
name: "PHP ${{ matrix.php-version }} (${{ matrix.dependencies }}) - lint, PHPStan, PHPCS"
name: "PHP ${{ matrix.php-version }} (${{ matrix.dependencies }})"
needs: "composer"
runs-on: "ubuntu-latest"
timeout-minutes: 15
Expand Down Expand Up @@ -62,8 +62,6 @@ jobs:
- name: "audit dependencies for known security vulnerabilities"
if: "matrix.php-version == '8.1'"
run: "composer audit"
- name: "check PHP version"
run: "php -v"
- name: "lint PHP files"
run: "find src -type f -name '*.php' -print0 | xargs -0 -n1 php -l"
- name: "run PHPStan"
Expand Down
81 changes: 41 additions & 40 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,50 +49,48 @@ about a specific extension's behavior belong in the connector repo consuming it

## 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). 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 commands from the repository root. PHPUnit is installed (vendor/bin/phpunit), but
# running it here without a target only shows usage: this package ships reusable abstract
# tests, not a local suite (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, uses phpcs.xml, PSR-12 for src/; 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 --no-progress --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

# Syntax-check every .php file in src/, 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, PSR-12 for src/)
make sniff PHP_VERSION=8.1

# Check dependencies for known vulnerabilities
make audit

# Run composer validate --strict
make validate
```

There is no `test` Makefile target: PHPUnit is installed (`vendor/bin/phpunit`), but
running it here without a target only shows usage, since this package ships reusable
abstract tests, not a local suite. Consumer repositories run the tests via their own
`make test`.

**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.
PHPCS on a single file), 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 runs at **level 9** (`phpstan.neon`, scans `src/`).

## Conventions
Expand All @@ -110,3 +108,6 @@ PHPStan runs at **level 9** (`phpstan.neon`, scans `src/`).
- Before changing a mock name or module template expectation, trace all related abstract methods and the `SapRfcModuleMocks::validateId()` path.
- Before changing JSON fixtures, inspect the matching assertions in `AbstractSapRfcTestCase.php` so types still align with expected PHP values (`DateTime`, `DateInterval`, arrays, decoded hex values).
- 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 file), base the invocation on the relevant
Makefile target and only append the extra arguments.
91 changes: 91 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
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 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/ and composer.lock (reset to a fresh checkout)
rm -rf vendor composer.lock

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 \
'

lint: check-php-version ## Syntax-check every .php file in src/ for PHP_VERSION
docker run --rm --init $(DOCKER_USER) $(DOCKER_MOUNT) \
"php:$(PHP_VERSION)-cli" sh -c "find src -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 -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 -t $(DOCKER_USER) $(DOCKER_MOUNT) \
$(COMPOSER_IMAGE) composer validate --strict
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,15 @@

This repository contains tests common to all [PHP/SAP][phpsap] implementations.

## Development

All development commands (install, 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
8 changes: 4 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
"php": "^8.1",
"ext-json": "*",
"phpunit/phpunit": "^9.6.33",
"php-sap/interfaces": "^5.1.4",
"php-sap/common": "^6.1.3",
"php-sap/datetime": "^1.5.1"
"php-sap/interfaces": "^5.1.6",
"php-sap/common": "^6.2.0",
"php-sap/datetime": "^1.5.2"
},
"require-dev": {
"phpstan/phpstan": "^2.2",
"phpstan/phpstan": "^2.2.3",
"squizlabs/php_codesniffer": "^4.0"
},
"autoload": {
Expand Down