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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## [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
- Fix PHP deprecation warnings.

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ If you want Zammad to execute an API call on behalf of another user than the one
```php
$client->setOnBehalfOfUser('myuser');
```
Any API call after above code will use this setting. If you want to return to using the user you used for authentication, call:
This sets the `From` HTTP header. Any API call after above code will use this setting. If you want to return to using the user you used for authentication, call:
```php
$client->unsetOnBehalfOfUser();
```
Expand Down
4 changes: 2 additions & 2 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ private function request ( $method, $url, array $url_parameters = [], array $opt
$options['headers']['Accept'] = 'application/json';
$options['headers']['Content-Type'] = 'application/json; charset=utf-8';

// Set "on behalf of user" header
// Set "on behalf of user" header (X-On-Behalf-Of was deprecated in Zammad 6.5 in favor of From)
if ( !empty($this->on_behalf_of_user) ) {
$options['headers']['X-On-Behalf-Of'] = $this->on_behalf_of_user;
$options['headers']['From'] = $this->on_behalf_of_user;
}

$http_client_response = $this->http_client->request( $method, $url, $options );
Expand Down
81 changes: 81 additions & 0 deletions test/ZammadAPIClient/ClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@
use PHPUnit\Framework\TestCase;

use ZammadAPIClient\Client;
use ZammadAPIClient\EnvConfigTrait;
use ZammadAPIClient\HTTPClientInterface;
use GuzzleHttp\Exception\ConnectException;
use Psr\Http\Message\ResponseInterface;

class ClientTest extends TestCase
{
use EnvConfigTrait;

public function testNetworkError()
{
// When providing a wrong URL, there must be a proper exception thrown.
Expand All @@ -21,4 +26,80 @@ public function testNetworkError()
]);
$client->get('/nonexisting');
}

public function testSetsFromHeaderWhenOnBehalfOfUserIsSet()
{
$client = $this->createClientWithMockHttpClient();
$client->setOnBehalfOfUser('testuser');
$client->get('tickets');

$this->assertArrayHasKey('From', $this->capturedOptions['headers']);
$this->assertSame('testuser', $this->capturedOptions['headers']['From']);
}

public function testDoesNotSetFromHeaderByDefault()
{
$client = $this->createClientWithMockHttpClient();
$client->get('tickets');

$this->assertArrayNotHasKey('From', $this->capturedOptions['headers']);
}

public function testRemovesFromHeaderAfterUnsetOnBehalfOfUser()
{
$client = $this->createClientWithMockHttpClient();
$client->setOnBehalfOfUser('testuser');
$client->unsetOnBehalfOfUser();
$client->get('tickets');

$this->assertArrayNotHasKey('From', $this->capturedOptions['headers']);
}

public function testFromHeaderAgainstZammad()
{
$client = self::createZammadClient();
if (!$client) {
$this->markTestSkipped(
'Zammad environment variables not set. '
. 'Set ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL, '
. 'ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME, '
. 'ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_PASSWORD.'
);
}

$client->setOnBehalfOfUser(getenv('ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME'));
$response = $client->get('users');

$this->assertSame(200, $response->getStatusCode());
$this->assertFalse($response->hasError());

$client->unsetOnBehalfOfUser();
$response = $client->get('users');

$this->assertSame(200, $response->getStatusCode());
$this->assertFalse($response->hasError());
}

private $capturedOptions = [];

private function createClientWithMockHttpClient()
{
$this->capturedOptions = [];

$mockResponse = $this->createMock(ResponseInterface::class);

$mockHttpClient = $this->createMock(HTTPClientInterface::class);
$mockHttpClient
->method('request')
->will($this->returnCallback(function ($method, $uri, $options) use ($mockResponse) {
$this->capturedOptions = $options;
return $mockResponse;
}));

return new Client([
'url' => 'https://example.com/',
'username' => 'test',
'password' => 'test',
], $mockHttpClient);
}
}
39 changes: 39 additions & 0 deletions test/ZammadAPIClient/EnvConfigTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace ZammadAPIClient;

trait EnvConfigTrait
{
private static function getZammadConfig(array $defaults = []): array
{
$config = $defaults;

$env_keys = [
'url' => 'ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL',
'username' => 'ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME',
'password' => 'ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_PASSWORD',
];

foreach ($env_keys as $config_key => $env_key) {
$value = getenv($env_key);
if (empty($value)) {
throw new \RuntimeException("Missing environment variable $env_key");
}
$config[$config_key] = $value;
}

return $config;
}

private static function createZammadClient(array $extra_config = []): ?Client
{
try {
$config = self::getZammadConfig($extra_config);
}
catch (\RuntimeException $e) {
return null;
}

return new Client($config);
}
}
24 changes: 6 additions & 18 deletions test/ZammadAPIClient/Resource/AbstractBaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
use PHPUnit\Framework\TestCase;

use ZammadAPIClient\Client;
use ZammadAPIClient\EnvConfigTrait;
use ZammadAPIClient\Exception\AlreadyFetchedObjectException;

abstract class AbstractBaseTest extends TestCase
{
use EnvConfigTrait;

private static $client;
protected $resource_type;
protected static $created_objects = [];
Expand All @@ -17,25 +20,10 @@ abstract class AbstractBaseTest extends TestCase

public static function setUpBeforeClass(): void
{
$client_config = [
# Set a high timeout for tests to work with slow CI.
$client_config = self::getZammadConfig([
'timeout' => 30,
'debug' => getenv('ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_DEBUG'),
];

$env_keys = [
'url' => 'ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_URL',
'username' => 'ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_USERNAME',
'password' => 'ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_PASSWORD',
];
foreach ( $env_keys as $config_key => $env_key ) {
$value = getenv($env_key);
if ( empty($value) ) {
throw new \RuntimeException("Missing environment variable $env_key");
}

$client_config[$config_key] = $value;
}
'debug' => getenv('ZAMMAD_PHP_API_CLIENT_UNIT_TESTS_DEBUG'),
]);

self::$client = new Client($client_config);
}
Expand Down