Skip to content
Open
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
61 changes: 0 additions & 61 deletions .github/workflows/ci.yml

This file was deleted.

69 changes: 69 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Tests

on:
push:
pull_request:
workflow_dispatch:

jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
extensions: xdebug
- run: composer install --no-interaction
- run: composer dump-autoload
- run: |
if [ -d src ]; then vendor/bin/phpcs --standard=PSR12 src/; else echo "→ Skip (src/ not yet present)"; fi
- run: |
if [ -d src ]; then vendor/bin/phpstan analyse src/ --level=max; else echo "→ Skip (src/ not yet present)"; fi
- run: |
if [ -d test/Unit ]; then vendor/bin/phpunit --testsuite=unit --display-skipped; else echo "→ Skip (test/Unit/ not yet present)"; fi

integration:
runs-on: ubuntu-latest
continue-on-error: true
container:
image: zammad/zammad-ci:latest
services:
postgresql:
image: postgres:17
env:
POSTGRES_USER: zammad
POSTGRES_PASSWORD: zammad
redis:
image: redis:7
steps:
- uses: actions/checkout@v5
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
- name: Install dependencies
run: composer install --no-interaction
- name: Dump autoload
run: composer dump-autoload
- name: Set up Zammad
shell: bash
run: |
git clone --depth 1 https://github.com/zammad/zammad.git
cd zammad
source /etc/profile.d/rvm.sh
bundle config set --local frozen 'true'
bundle config set --local path 'vendor'
bundle install -j $(nproc)
bundle exec ruby .gitlab/configure_environment.rb
source .gitlab/environment.env
RAILS_ENV=test bundle exec rake db:create
RAILS_ENV=test bundle exec rake zammad:ci:test:start zammad:setup:auto_wizard
- name: Run integration tests
run: |
set -x
if [ -d test/Integration ]; then vendor/bin/phpunit --testsuite=integration --group=integration --display-skipped; else echo "→ Skip (test/Integration/ not yet present)"; fi
env:
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL: http://localhost:3000/api/v1
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME: admin@example.com
ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_PASSWORD: test
10 changes: 4 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/vendor/
/phpunit.xml
/.phpunit.cache/
/composer.lock
/examples/config.php
/examples/test.php
/test*.php
*~
.phpunit.result.cache
/build/
/.opencode/
/playground.php
69 changes: 46 additions & 23 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,56 @@
## [2.3.0] - 2026-06-10
# Changelog

## [3.0.0] — unreleased

### Added
- PSR-18 / PSR-17 compliant HTTP layer (`RequestHandler`, `RetryAfterMiddleware`)
- Typed DTOs for all 10 resources (`Ticket`, `User`, `Organization`, `Group`, `TicketArticle`, `TicketState`, `TicketPriority`, `Tag`, `TextModule`, `Link`)
- Repository pattern with generator-based pagination (`AbstractRepository`)
- `patch()` method for partial updates via `array` or `TicketUpdateDTO`
- Proper exception hierarchy: `AuthenticationException`, `NotFoundException`, `ValidationException`, `RateLimitException`, `ServerErrorException`, `NetworkException`
- `ZammadClient::withToken()` / `withOAuth2()` / `withBasicAuth()` factory methods
- PSR-3 Logger injection
- OpenAPI schema validation in integration tests
- Framework bridges for Laravel and Symfony
- `TicketArticleType` enum for article channel types
- `ClientFactoryInterface` interface + `GuzzleClientFactory` — `createHandler()` baut den `RequestHandler`
- `ImpersonationHandler` — stateless decorator fur API-Impersonation
- `ImpersonationHandler` — scoped via `new ZammadClient(new ImpersonationHandler($handler, $userId))`

### Changed
- **Breaking:** PHP >= 8.1 required
- **Breaking:** `ZammadClient::withToken()` / `withOAuth2()` / `withBasicAuth()` → `new ZammadClient(GuzzleClientFactory::with*())` (Namespace `ZammadAPIClient\Factory`).
Non-Guzzle via `new ZammadClient(new RequestHandler($psr18Client, $psr17Factory, $url))`.
- **Breaking:** `ZammadClient::withClient()` entfernt; `ZammadClient` constructor akzeptiert `RequestHandlerInterface|ClientFactoryInterface`
- **Breaking:** `ZammadClient::setOnBehalfOfUser()` / `unsetOnBehalfOfUser()` / `onBehalfOf()` / `performOnBehalfOf()` entfernt.
Impersonation via `new ZammadClient(new ImpersonationHandler($handler, $userId))`.
- **Breaking:** `RequestHandlerInterface::setOnBehalfOfUser()` / `getOnBehalfOfUser()` entfernt.
`RequestHandler` halt keinen Impersonation-State mehr.
- **Breaking:** `RequestHandlerInterface::getRaw()` Signatur um `$headers`-Parameter erweitert
- **Breaking:** `Client` class replaced by `ZammadClient` with repository accessors
- **Breaking:** Array return values replaced by typed DTOs
- **Breaking:** Guzzle used as default transport; `withClient()` supports any PSR-18 client
- `getListKey()` in Repository-Klassen nicht mehr abstrakt; Default ist `$this->resourcePath`

### Removed
- Magic resource accessor (`$client->ticket()`) — ersatzlos entfernt. Nutze `$client->repo(TicketRepository::class)`.
- `ZammadClient::aliasMap()`, `resolveAlias()`, `__call()` — deprecated in v3.0, jetzt entfernt
- Shared mutable Impersonation-State aus `RequestHandler` entfernt

---

## Previous v2 releases

### [2.3.0] - 2026-06-10
- Added [#78](https://github.com/zammad/zammad-api-client-php/pull/78) - Ticket linking support (Link resource).
- Added [#59](https://github.com/zammad/zammad-api-client-php/issues/59) - Admin-scoped tag support.
- Added [#126](https://github.com/zammad/zammad-api-client-php/pull/126) - Configurable test timeout via `ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_TIMEOUT` and `connection_options` support in HTTPClient.
- Fixed [#52](https://github.com/zammad/zammad-api-client-php/issues/52) - `getAttachmentContent()` to return string instead of Stream object.
- Added [#43](https://github.com/zammad/zammad-api-client-php/issues/43) - `sort_by` and `order_by` parameters for `search()`.
- Fixed [#77](https://github.com/zammad/zammad-api-client-php/issues/77) - `getID()` to handle null values and always return a string.

## [2.2.3] - 2026-06-08
### [2.2.3] - 2026-06-08
- Fixed [#64](https://github.com/zammad/zammad-api-client-php/issues/64) - Use `From` header instead of deprecated `X-On-Behalf-Of`.

## [2.2.2] - 2026-04-27
### [2.2.2] - 2026-04-27
- Fix PHP deprecation warnings.

## [2.2.1] - 2025-09-10
- Improve handling of lowercase HTTP response headers.

## [2.2.0] - 2023-05-11
- Switch to dual licensing under AGPL-3.0 or MIT licenses.

## [2.1.0] - 2022-12-01
- Added [#48](https://github.com/zammad/zammad-api-client-php/pull/48) - Allowing injection of custom client.

## [2.0.5] - 2022-09-02
- Fixed [#42](https://github.com/zammad/zammad-api-client-php/issues/42) - Call to undefined method GuzzleHttp\Exception\ConnectException::getResponse().

## [2.0.4] - 2022-07-29
- Fixed [#49](https://github.com/zammad/zammad-api-client-php/issues/49) - "on_behalf_of_user" never gets initialized, which leads to conflicts.
- Fixed [#47](https://github.com/zammad/zammad-api-client-php/issues/47) - Make Guzzles HTTP connect_timeout configurable or set a decent default.
- Fixed [#46](https://github.com/zammad/zammad-api-client-php/issues/46) - Wrong documentation for SSL verification switch.

## [2.0.3] - 2022-04-14
- Updated tests to be compatible with new Zammad versions.
53 changes: 53 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.PHONY: help test test-integration coverage cookbook clean

help: ## Show available make targets
@grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-18s\033[0m %s\n", $$1, $$2}'

test: ## Run phpcs, phpstan, phpunit (same as CI)
@echo "→ phpcs (PSR-12)"
@if [ -f vendor/bin/phpcs ] && [ -d src ]; then vendor/bin/phpcs --standard=PSR12 src/; else echo " Skip (src/ or vendor/ not present)"; fi
@echo "→ phpstan (level=max)"
@if [ -f vendor/bin/phpstan ] && [ -d src ]; then vendor/bin/phpstan analyse src/ --level=max; else echo " Skip (src/ or vendor/ not present)"; fi
@echo "→ phpunit (unit suite)"
@if [ -f vendor/bin/phpunit ] && [ -d test/Unit ]; then XDEBUG_MODE=off vendor/bin/phpunit --testsuite=unit --no-coverage; else echo " Skip (test/Unit/ or vendor/ not present)"; fi

test-integration: ## Run integration tests (needs Zammad instance)
@echo "→ Integration test setup"
@echo " ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL = $${ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL:-not set}"
@echo " ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_TOKEN = $${ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_TOKEN:-not set}"
@echo " ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME = $${ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME:-not set}"
@echo " ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_PASSWORD = $${ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_PASSWORD:-not set}"
@if [ -z "$${ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL:-}" ]; then \
echo " → Set ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL to enable"; \
echo " Example:"; \
echo " ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL=http://localhost:3000 \\"; \
echo " ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_TOKEN=xxx \\"; \
echo " make test-integration"; \
exit 1; \
fi
@if [ -z "$${ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_TOKEN:-}" ] && [ -z "$${ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME:-}" ]; then \
echo " → Set ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_TOKEN or ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME for authentication"; \
exit 1; \
fi
@echo "→ Running integration tests..."
@if [ -f vendor/bin/phpunit ] && [ -d test/Integration ]; then \
XDEBUG_MODE=off vendor/bin/phpunit --testsuite=integration --group=integration --display-skipped; \
else \
echo " Skip (test/Integration/ or vendor/ not present)"; \
fi

coverage: ## Run unit tests with coverage report (build/coverage/html/)
@if [ -f vendor/bin/phpunit ] && [ -d test/Unit ]; then \
php -d xdebug.mode=coverage vendor/bin/phpunit --testsuite=unit; \
else \
echo " Skip (test/Unit/ or vendor/ not present)"; \
fi

cookbook: ## Run all cookbook recipes (needs Zammad instance)
@for f in examples/cookbook/0[1-6]*.php; do \
echo "=== $$f ===" && php "$$f"; \
done

clean: ## Remove build artifacts
rm -rf build/
rm -f .phpunit.cache
Loading