Skip to content
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- New method `Redmine\Api\Attachment::fromHttpClient()` for creating the class.
- New method `Redmine\Api\CustomField::fromHttpClient()` for creating the class.
- New method `Redmine\Api\Group::fromHttpClient()` for creating the class.
- New method `Redmine\Api\Issue::fromHttpClient()` for creating the class.
- New method `Redmine\Api\IssueCategory::fromHttpClient()` for creating the class.
- Add support for PHP 8.5
- Add support for Redmine 6.1.

### Changed

- Behaviour-driven tests are run against Redmine 6.1.0, 6.0.7, 5.1.10.

### Deprecated

- `Redmine\Api\Attachment::__construct()` is deprecated and will be set to private in future, use `\Redmine\Api\Attachment::fromHttpClient()` instead.
- Extending `Redmine\Api\Attachment` is deprecated and will be set to final in future, create a wrapper class instead.
- `Redmine\Api\CustomField::__construct()` is deprecated and will be set to private in future, use `\Redmine\Api\CustomField::fromHttpClient()` instead.
- Extending `Redmine\Api\CustomField` is deprecated and will be set to final in future, create a wrapper class instead.
- `Redmine\Api\Group::__construct()` is deprecated and will be set to private in future, use `\Redmine\Api\Group::fromHttpClient()` instead.
- Extending `Redmine\Api\Group` is deprecated and will be set to final in future, create a wrapper class instead.
- `Redmine\Api\Issue::__construct()` is deprecated and will be set to private in future, use `\Redmine\Api\Issue::fromHttpClient()` instead.
- Extending `Redmine\Api\Issue` is deprecated and will be set to final in future, create a wrapper class instead.
- `Redmine\Api\IssueCategory::__construct()` is deprecated and will be set to private in future, use `\Redmine\Api\IssueCategory::fromHttpClient()` instead.
- Extending `Redmine\Api\IssueCategory` is deprecated and will be set to final in future, create a wrapper class instead.

### Removed

- Drop support for Redmine 5.0.x.
Expand Down
33 changes: 33 additions & 0 deletions src/Redmine/Api/Attachment.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace Redmine\Api;

use Redmine\Client\Client;
use Redmine\Exception\SerializerException;
use Redmine\Exception\UnexpectedResponseException;
use Redmine\Http\HttpClient;
use Redmine\Http\HttpFactory;
use Redmine\Serializer\JsonSerializer;
use Redmine\Serializer\PathSerializer;
Expand All @@ -17,6 +19,37 @@
*/
class Attachment extends AbstractApi
{
final public static function fromHttpClient(HttpClient $httpClient): self
{
return new self($httpClient, true);
}

/**
* @deprecated v2.9.0 Use fromHttpClient() instead.
* @see Attachment::fromHttpClient()
*
* @param Client|HttpClient $client
*/
public function __construct($client/*, bool $privatelyCalled = false*/)
{
$privatelyCalled = (func_num_args() > 1) ? func_get_arg(1) : false;

if ($privatelyCalled === true) {
parent::__construct($client);

return;
}

if (static::class !== self::class) {
$className = (new \ReflectionClass($this))->isAnonymous() ? '' : ' in `' . static::class . '`';
@trigger_error('Class `' . self::class . '` will declared as final in v3.0.0, stop extending it' . $className . '.', E_USER_DEPRECATED);
} else {
@trigger_error('Method `' . __METHOD__ . '()` is deprecated since v2.9.0 and will declared as private in v3.0.0, use `' . self::class . '::fromHttpClient()` instead.', E_USER_DEPRECATED);
}

parent::__construct($client);
}

/**
* Get extended information about an attachment.
*
Expand Down
33 changes: 33 additions & 0 deletions src/Redmine/Api/CustomField.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace Redmine\Api;

use Redmine\Client\Client;
use Redmine\Exception;
use Redmine\Exception\SerializerException;
use Redmine\Exception\UnexpectedResponseException;
use Redmine\Http\HttpClient;

/**
* Listing custom fields.
Expand All @@ -15,6 +17,11 @@
*/
class CustomField extends AbstractApi
{
final public static function fromHttpClient(HttpClient $httpClient): self
{
return new self($httpClient, true);
}

/**
* @var null|array<mixed>
*/
Expand All @@ -25,6 +32,32 @@ class CustomField extends AbstractApi
*/
private ?array $customFieldNames = null;

/**
* @deprecated v2.9.0 Use fromHttpClient() instead.
* @see CustomField::fromHttpClient()
*
* @param Client|HttpClient $client
*/
public function __construct($client/*, bool $privatelyCalled = false*/)
{
$privatelyCalled = (func_num_args() > 1) ? func_get_arg(1) : false;

if ($privatelyCalled === true) {
parent::__construct($client);

return;
}

if (static::class !== self::class) {
$className = (new \ReflectionClass($this))->isAnonymous() ? '' : ' in `' . static::class . '`';
@trigger_error('Class `' . self::class . '` will declared as final in v3.0.0, stop extending it' . $className . '.', E_USER_DEPRECATED);
} else {
@trigger_error('Method `' . __METHOD__ . '()` is deprecated since v2.9.0 and will declared as private in v3.0.0, use `' . self::class . '::fromHttpClient()` instead.', E_USER_DEPRECATED);
}

parent::__construct($client);
}

/**
* List custom fields.
*
Expand Down
33 changes: 33 additions & 0 deletions src/Redmine/Api/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

namespace Redmine\Api;

use Redmine\Client\Client;
use Redmine\Exception;
use Redmine\Exception\MissingParameterException;
use Redmine\Exception\SerializerException;
use Redmine\Exception\UnexpectedResponseException;
use Redmine\Http\HttpClient;
use Redmine\Http\HttpFactory;
use Redmine\Serializer\JsonSerializer;
use Redmine\Serializer\PathSerializer;
Expand All @@ -21,6 +23,11 @@
*/
class Group extends AbstractApi
{
final public static function fromHttpClient(HttpClient $httpClient): self
{
return new self($httpClient, true);
}

/**
* @var null|array<mixed>
*/
Expand All @@ -31,6 +38,32 @@ class Group extends AbstractApi
*/
private ?array $groupNames = null;

/**
* @deprecated v2.9.0 Use fromHttpClient() instead.
* @see Group::fromHttpClient()
*
* @param Client|HttpClient $client
*/
public function __construct($client/*, bool $privatelyCalled = false*/)
{
$privatelyCalled = (func_num_args() > 1) ? func_get_arg(1) : false;

if ($privatelyCalled === true) {
parent::__construct($client);

return;
}

if (static::class !== self::class) {
$className = (new \ReflectionClass($this))->isAnonymous() ? '' : ' in `' . static::class . '`';
@trigger_error('Class `' . self::class . '` will declared as final in v3.0.0, stop extending it' . $className . '.', E_USER_DEPRECATED);
} else {
@trigger_error('Method `' . __METHOD__ . '()` is deprecated since v2.9.0 and will declared as private in v3.0.0, use `' . self::class . '::fromHttpClient()` instead.', E_USER_DEPRECATED);
}

parent::__construct($client);
}

/**
* List groups.
*
Expand Down
35 changes: 34 additions & 1 deletion src/Redmine/Api/Issue.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

namespace Redmine\Api;

use Redmine\Client\Client;
use Redmine\Client\NativeCurlClient;
use Redmine\Client\Psr18Client;
use Redmine\Exception;
use Redmine\Exception\SerializerException;
use Redmine\Exception\UnexpectedResponseException;
use Redmine\Http\HttpClient;
use Redmine\Http\HttpFactory;
use Redmine\Serializer\JsonSerializer;
use Redmine\Serializer\PathSerializer;
Expand Down Expand Up @@ -47,6 +49,11 @@ class Issue extends AbstractApi
*/
public const PRIO_IMMEDIATE = 5;

final public static function fromHttpClient(HttpClient $httpClient): self
{
return new self($httpClient, true);
}

/**
* @var null|IssueCategory
*/
Expand All @@ -72,6 +79,32 @@ class Issue extends AbstractApi
*/
private $userApi = null;

/**
* @deprecated v2.9.0 Use fromHttpClient() instead.
* @see Issue::fromHttpClient()
*
* @param Client|HttpClient $client
*/
public function __construct($client/*, bool $privatelyCalled = false*/)
{
$privatelyCalled = (func_num_args() > 1) ? func_get_arg(1) : false;

if ($privatelyCalled === true) {
parent::__construct($client);

return;
}

if (static::class !== self::class) {
$className = (new \ReflectionClass($this))->isAnonymous() ? '' : ' in `' . static::class . '`';
@trigger_error('Class `' . self::class . '` will declared as final in v3.0.0, stop extending it' . $className . '.', E_USER_DEPRECATED);
} else {
@trigger_error('Method `' . __METHOD__ . '()` is deprecated since v2.9.0 and will declared as private in v3.0.0, use `' . self::class . '::fromHttpClient()` instead.', E_USER_DEPRECATED);
}

parent::__construct($client);
}

/**
* List issues.
*
Expand Down Expand Up @@ -489,7 +522,7 @@ private function getIssueCategoryApi()
/** @var IssueCategory */
$issueCategoryApi = $this->client->getApi('issue_category');
} else {
$issueCategoryApi = new IssueCategory($this->getHttpClient());
$issueCategoryApi = IssueCategory::fromHttpClient($this->getHttpClient());
}

$this->issueCategoryApi = $issueCategoryApi;
Expand Down
33 changes: 33 additions & 0 deletions src/Redmine/Api/IssueCategory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

namespace Redmine\Api;

use Redmine\Client\Client;
use Redmine\Exception;
use Redmine\Exception\InvalidParameterException;
use Redmine\Exception\MissingParameterException;
use Redmine\Exception\SerializerException;
use Redmine\Exception\UnexpectedResponseException;
use Redmine\Http\HttpClient;
use Redmine\Http\HttpFactory;
use Redmine\Serializer\JsonSerializer;
use Redmine\Serializer\PathSerializer;
Expand All @@ -22,6 +24,11 @@
*/
class IssueCategory extends AbstractApi
{
final public static function fromHttpClient(HttpClient $httpClient): self
{
return new self($httpClient, true);
}

/**
* @var null|array<mixed>
*/
Expand All @@ -32,6 +39,32 @@ class IssueCategory extends AbstractApi
*/
private array $issueCategoriesNames = [];

/**
* @deprecated v2.9.0 Use fromHttpClient() instead.
* @see IssueCategory::fromHttpClient()
*
* @param Client|HttpClient $client
*/
public function __construct($client/*, bool $privatelyCalled = false*/)
{
$privatelyCalled = (func_num_args() > 1) ? func_get_arg(1) : false;

if ($privatelyCalled === true) {
parent::__construct($client);

return;
}

if (static::class !== self::class) {
$className = (new \ReflectionClass($this))->isAnonymous() ? '' : ' in `' . static::class . '`';
@trigger_error('Class `' . self::class . '` will declared as final in v3.0.0, stop extending it' . $className . '.', E_USER_DEPRECATED);
} else {
@trigger_error('Method `' . __METHOD__ . '()` is deprecated since v2.9.0 and will declared as private in v3.0.0, use `' . self::class . '::fromHttpClient()` instead.', E_USER_DEPRECATED);
}

parent::__construct($client);
}

/**
* List issue categories for a given project.
*
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Api/Attachment/DownloadTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function testDownloadReturnsCorrectResponse($id, string $expectedPath, in
);

// Create the object under test
$api = new Attachment($client);
$api = Attachment::fromHttpClient($client);

// Perform the tests
$this->assertSame($expectedReturn, $api->download($id));
Expand Down
21 changes: 21 additions & 0 deletions tests/Unit/Api/Attachment/FromHttpClientTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Redmine\Tests\Unit\Api\Attachment;

use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;
use Redmine\Api\Attachment;
use Redmine\Http\HttpClient;

#[CoversClass(Attachment::class)]
class FromHttpClientTest extends TestCase
{
public function testReturnsCorrectObject(): void
{
$httpClient = $this->createStub(HttpClient::class);

$api = Attachment::fromHttpClient($httpClient);

$this->assertInstanceOf(Attachment::class, $api);
}
}
2 changes: 1 addition & 1 deletion tests/Unit/Api/Attachment/RemoveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function testRemoveReturnsString(): void
],
);

$api = new Attachment($client);
$api = Attachment::fromHttpClient($client);

$this->assertSame('', $api->remove(5));
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Api/Attachment/ShowTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function testShowReturnsCorrectResponse($id, string $expectedPath, string
);

// Create the object under test
$api = new Attachment($client);
$api = Attachment::fromHttpClient($client);

// Perform the tests
$this->assertSame($expectedReturn, $api->show($id));
Expand Down
4 changes: 2 additions & 2 deletions tests/Unit/Api/Attachment/UpdateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function testUpdateReturnsCorrectResponse(int $id, array $params, string
);

// Create the object under test
$api = new Attachment($client);
$api = Attachment::fromHttpClient($client);

// Perform the tests
$this->assertSame($expectedReturn, $api->update($id, $params));
Expand Down Expand Up @@ -69,7 +69,7 @@ public function testUpdateThrowsUnexpectedResponseException(): void
],
);

$api = new Attachment($client);
$api = Attachment::fromHttpClient($client);

$this->expectException(UnexpectedResponseException::class);
$this->expectExceptionMessage('The Redmine server replied with an unexpected response.');
Expand Down
2 changes: 1 addition & 1 deletion tests/Unit/Api/Attachment/UploadTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function testUploadReturnsCorrectResponse(string $attachment, array $para
);

// Create the object under test
$api = new Attachment($client);
$api = Attachment::fromHttpClient($client);

// Perform the tests
$this->assertSame($expectedReturn, $api->upload($attachment, $params));
Expand Down
Loading
Loading