Skip to content

Commit 8665629

Browse files
authored
Merge pull request #326 from Art4/add-phpstan
Add phpstan
2 parents 3e2dcb2 + 0c91f76 commit 8665629

18 files changed

+229
-149
lines changed

.github/workflows/tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ jobs:
3636
- name: "Install Composer dependencies"
3737
uses: "ramsey/composer-install@v2"
3838

39+
- name: Run static code analysis
40+
if: ${{ matrix.php-versions == '8.2' }}
41+
run: composer run phpstan -- --error-format=github
42+
3943
- name: Run tests
4044
run: vendor/bin/phpunit --coverage-clover .phpunit.cache/clover.xml
4145

.phpstan.neon

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
parameters:
2+
level: 4
3+
4+
paths:
5+
- src/
6+
- tests/
7+
8+
scanDirectories:
9+
- vendor

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"friendsofphp/php-cs-fixer": "^3",
3333
"phpunit/phpunit": "^9 || 10.2.*",
3434
"guzzlehttp/psr7": "^2",
35-
"php-mock/php-mock-phpunit": "^2.6"
35+
"php-mock/php-mock-phpunit": "^2.6",
36+
"phpstan/phpstan": "^1.10"
3637
},
3738
"autoload": {
3839
"psr-4": {
@@ -46,6 +47,7 @@
4647
},
4748
"scripts": {
4849
"coverage": "phpunit --coverage-html=\".phpunit.cache/code-coverage\"",
50+
"phpstan": "phpstan analyze --memory-limit 512M --configuration .phpstan.neon",
4951
"test": "phpunit"
5052
}
5153
}

src/Redmine/Api/Group.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public function listing($forceUpdate = false)
6363
*
6464
* @throws MissingParameterException Missing mandatory parameters
6565
*
66-
* @return \SimpleXMLElement
66+
* @return string|false
6767
*/
6868
public function create(array $params = [])
6969
{

src/Redmine/Api/Issue.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public function show($id, array $params = [])
7676
*
7777
* @param array $params the new issue data
7878
*
79-
* @return \SimpleXMLElement
79+
* @return string|false
8080
*/
8181
public function create(array $params = [])
8282
{
@@ -174,6 +174,7 @@ public function removeWatcher($id, $watcherUserId)
174174
*/
175175
public function setIssueStatus($id, $status)
176176
{
177+
/** @var IssueStatus */
177178
$api = $this->client->getApi('issue_status');
178179

179180
return $this->update($id, [
@@ -204,31 +205,37 @@ public function addNoteToIssue($id, $note, $privateNote = false)
204205
private function cleanParams(array $params = [])
205206
{
206207
if (isset($params['project'])) {
208+
/** @var Project */
207209
$apiProject = $this->client->getApi('project');
208210
$params['project_id'] = $apiProject->getIdByName($params['project']);
209211
unset($params['project']);
210212
if (isset($params['category'])) {
213+
/** @var IssueCategory */
211214
$apiIssueCategory = $this->client->getApi('issue_category');
212215
$params['category_id'] = $apiIssueCategory->getIdByName($params['project_id'], $params['category']);
213216
unset($params['category']);
214217
}
215218
}
216219
if (isset($params['status'])) {
220+
/** @var IssueStatus */
217221
$apiIssueStatus = $this->client->getApi('issue_status');
218222
$params['status_id'] = $apiIssueStatus->getIdByName($params['status']);
219223
unset($params['status']);
220224
}
221225
if (isset($params['tracker'])) {
226+
/** @var Tracker */
222227
$apiTracker = $this->client->getApi('tracker');
223228
$params['tracker_id'] = $apiTracker->getIdByName($params['tracker']);
224229
unset($params['tracker']);
225230
}
226231
if (isset($params['assigned_to'])) {
232+
/** @var User */
227233
$apiUser = $this->client->getApi('user');
228234
$params['assigned_to_id'] = $apiUser->getIdByUsername($params['assigned_to']);
229235
unset($params['assigned_to']);
230236
}
231237
if (isset($params['author'])) {
238+
/** @var User */
232239
$apiUser = $this->client->getApi('user');
233240
$params['author_id'] = $apiUser->getIdByUsername($params['author']);
234241
unset($params['author']);

src/Redmine/Api/Membership.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public function removeMember($projectId, $userId, array $params = [])
121121
{
122122
$memberships = $this->all($projectId, $params);
123123
if (!isset($memberships['memberships']) || !is_array($memberships['memberships'])) {
124-
return;
124+
return false;
125125
}
126126
$removed = false;
127127
foreach ($memberships['memberships'] as $membership) {

src/Redmine/Api/Project.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public function show($id, array $params = [])
106106
*
107107
* @throws MissingParameterException
108108
*
109-
* @return \SimpleXMLElement
109+
* @return string|false
110110
*/
111111
public function create(array $params = [])
112112
{

src/Redmine/Client/NativeCurlClient.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Redmine\Client;
66

7-
use Redmine\Api;
87
use Redmine\Exception\ClientException;
98

109
/**
@@ -246,7 +245,9 @@ private function request(string $method, string $path, string $body = ''): bool
246245
/**
247246
* Prepare the request by setting the cURL options.
248247
*
249-
* @return resource a cURL handle on success, <b>FALSE</b> on errors
248+
* BC for PHP 7.4: Do not add the return type because CurlHandle was introduced in PHP 8.0
249+
*
250+
* @return \CurlHandle a cURL handle on success, <b>FALSE</b> on errors
250251
*/
251252
private function createCurl(string $method, string $path, string $body = '')
252253
{
@@ -281,13 +282,13 @@ private function createCurl(string $method, string $path, string $body = '')
281282
$curlOptions[CURLOPT_POSTFIELDS] = $filedata;
282283
$curlOptions[CURLOPT_INFILE] = $file;
283284
$curlOptions[CURLOPT_INFILESIZE] = $size;
284-
} elseif (isset($body)) {
285+
} elseif ($body !== '') {
285286
$curlOptions[CURLOPT_POSTFIELDS] = $body;
286287
}
287288
break;
288289
case 'put':
289290
$curlOptions[CURLOPT_CUSTOMREQUEST] = 'PUT';
290-
if (isset($body)) {
291+
if ($body !== '') {
291292
$curlOptions[CURLOPT_POSTFIELDS] = $body;
292293
}
293294
break;

tests/Fixtures/MockClient.php

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@
1212
* The runRequest method of this client class just returns the value of
1313
* the path, method and data or the $runRequestReturnValue value if set.
1414
*/
15-
class MockClient implements Client
15+
final class MockClient implements Client
1616
{
1717
use ClientApiTrait;
1818

19+
public static function create()
20+
{
21+
return new self();
22+
}
23+
1924
/**
2025
* Return value the mocked runRequest method should return.
2126
*
@@ -34,22 +39,7 @@ class MockClient implements Client
3439
public $responseCodeMock;
3540
public $responseContentTypeMock;
3641

37-
private string $url;
38-
private string $apikeyOrUsername;
39-
private ?string $password;
40-
41-
/**
42-
* $apikeyOrUsername should be your ApiKey, but it could also be your username.
43-
* $password needs to be set if a username is given (not recommended).
44-
*/
45-
public function __construct(
46-
string $url,
47-
string $apikeyOrUsername,
48-
string $password = null
49-
) {
50-
$this->url = $url;
51-
$this->apikeyOrUsername = $apikeyOrUsername;
52-
$this->password = $password;
42+
private function __construct() {
5343
}
5444

5545
/**

tests/Integration/GroupXmlTest.php

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,10 @@
99

1010
class GroupXmlTest extends TestCase
1111
{
12-
/**
13-
* @var MockClient
14-
*/
15-
private $client;
16-
17-
public function setup(): void
18-
{
19-
$this->client = new MockClient('http://test.local', 'asdf');
20-
}
21-
2212
public function testCreateBlank()
2313
{
24-
$api = $this->client->getApi('group');
14+
/** @var \Redmine\Api\Group */
15+
$api = MockClient::create()->getApi('group');
2516
$this->assertInstanceOf('Redmine\Api\Group', $api);
2617

2718
$this->expectException(MissingParameterException::class);
@@ -32,7 +23,9 @@ public function testCreateBlank()
3223

3324
public function testCreateComplex()
3425
{
35-
$res = $this->client->getApi('group')->create([
26+
/** @var \Redmine\Api\Group */
27+
$api = MockClient::create()->getApi('group');
28+
$res = $api->create([
3629
'name' => 'Developers',
3730
'user_ids' => [3, 5],
3831
]);
@@ -57,7 +50,8 @@ public function testCreateComplex()
5750

5851
public function testUpdateNotImplemented()
5952
{
60-
$api = $this->client->getApi('group');
53+
/** @var \Redmine\Api\Group */
54+
$api = MockClient::create()->getApi('group');
6155
$this->assertInstanceOf('Redmine\Api\Group', $api);
6256

6357
$this->expectException(Exception::class);

tests/Integration/IssueCategoryXmlTest.php

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,10 @@
88

99
class IssueCategoryXmlTest extends TestCase
1010
{
11-
/**
12-
* @var MockClient
13-
*/
14-
private $client;
15-
16-
public function setup(): void
17-
{
18-
$this->client = new MockClient('http://test.local', 'asdf');
19-
}
20-
2111
public function testCreateBlank()
2212
{
23-
$api = $this->client->getApi('issue_category');
13+
/** @var \Redmine\Api\IssueCategory */
14+
$api = MockClient::create()->getApi('issue_category');
2415
$this->assertInstanceOf('Redmine\Api\IssueCategory', $api);
2516

2617
$this->expectException(MissingParameterException::class);
@@ -31,7 +22,8 @@ public function testCreateBlank()
3122

3223
public function testCreateComplex()
3324
{
34-
$api = $this->client->getApi('issue_category');
25+
/** @var \Redmine\Api\IssueCategory */
26+
$api = MockClient::create()->getApi('issue_category');
3527
$res = $api->create('otherProject', [
3628
'name' => 'test category',
3729
]);
@@ -52,7 +44,8 @@ public function testCreateComplex()
5244

5345
public function testUpdate()
5446
{
55-
$api = $this->client->getApi('issue_category');
47+
/** @var \Redmine\Api\IssueCategory */
48+
$api = MockClient::create()->getApi('issue_category');
5649
$res = $api->update(1, [
5750
'name' => 'new category name',
5851
]);

tests/Integration/IssueXmlTest.php

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,10 @@
77

88
class IssueXmlTest extends TestCase
99
{
10-
/**
11-
* @var MockClient
12-
*/
13-
private $client;
14-
15-
public function setup(): void
16-
{
17-
$this->client = new MockClient('http://test.local', 'asdf');
18-
}
19-
2010
public function testCreateBlank()
2111
{
22-
$api = $this->client->getApi('issue');
12+
/** @var \Redmine\Api\Issue */
13+
$api = MockClient::create()->getApi('issue');
2314
$this->assertInstanceOf('Redmine\Api\Issue', $api);
2415

2516
$res = $api->create();
@@ -38,7 +29,8 @@ public function testCreateBlank()
3829

3930
public function testCreateComplexWithUpload()
4031
{
41-
$api = $this->client->getApi('issue');
32+
/** @var \Redmine\Api\Issue */
33+
$api = MockClient::create()->getApi('issue');
4234
$res = $api->create([
4335
'project_id' => 'myproject',
4436
'subject' => 'A test issue',
@@ -79,7 +71,8 @@ public function testCreateComplexWithUpload()
7971

8072
public function testCreateComplex()
8173
{
82-
$api = $this->client->getApi('issue');
74+
/** @var \Redmine\Api\Issue */
75+
$api = MockClient::create()->getApi('issue');
8376
$res = $api->create([
8477
'project_id' => 'test',
8578
'subject' => 'test api (xml) 3',
@@ -129,7 +122,8 @@ public function testCreateComplex()
129122

130123
public function testCreateComplexWithLineBreakInDescription()
131124
{
132-
$api = $this->client->getApi('issue');
125+
/** @var \Redmine\Api\Issue */
126+
$api = MockClient::create()->getApi('issue');
133127
$res = $api->create([
134128
'project_id' => 'test',
135129
'subject' => 'test api (xml) 3',
@@ -180,7 +174,8 @@ public function testCreateComplexWithLineBreakInDescription()
180174

181175
public function testUpdateIssue()
182176
{
183-
$api = $this->client->getApi('issue');
177+
/** @var \Redmine\Api\Issue */
178+
$api = MockClient::create()->getApi('issue');
184179
$res = $api->update(1, [
185180
'subject' => 'test note (xml) 1',
186181
'notes' => 'test note api',
@@ -215,7 +210,8 @@ public function testUpdateIssue()
215210

216211
public function testAddNoteToIssue()
217212
{
218-
$api = $this->client->getApi('issue');
213+
/** @var \Redmine\Api\Issue */
214+
$api = MockClient::create()->getApi('issue');
219215
$res = $api->addNoteToIssue(1, 'some comment');
220216
$response = json_decode($res, true);
221217

tests/Integration/MembershipXmlTest.php

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,10 @@
88

99
class MembershipXmlTest extends TestCase
1010
{
11-
/**
12-
* @var MockClient
13-
*/
14-
private $client;
15-
16-
public function setup(): void
17-
{
18-
$this->client = new MockClient('http://test.local', 'asdf');
19-
}
20-
2111
public function testCreateBlank()
2212
{
23-
$api = $this->client->getApi('membership');
13+
/** @var \Redmine\Api\Membership */
14+
$api = MockClient::create()->getApi('membership');
2415
$this->assertInstanceOf('Redmine\Api\Membership', $api);
2516

2617
$this->expectException(MissingParameterException::class);
@@ -31,7 +22,8 @@ public function testCreateBlank()
3122

3223
public function testCreateComplex()
3324
{
34-
$api = $this->client->getApi('membership');
25+
/** @var \Redmine\Api\Membership */
26+
$api = MockClient::create()->getApi('membership');
3527
$res = $api->create('otherProject', [
3628
'user_id' => 1,
3729
'role_ids' => [1, 2],
@@ -57,7 +49,8 @@ public function testCreateComplex()
5749

5850
public function testUpdate()
5951
{
60-
$api = $this->client->getApi('membership');
52+
/** @var \Redmine\Api\Membership */
53+
$api = MockClient::create()->getApi('membership');
6154
$res = $api->update(1, [
6255
'role_ids' => [1, 2],
6356
]);

0 commit comments

Comments
 (0)