diff --git a/.gitattributes b/.gitattributes index 4c37270a7..220bf9a38 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14,4 +14,4 @@ /psalm.xml export-ignore /CHANGELOG.md export-ignore /README.md export-ignore -/rector.yml export-ignore +/rector.php export-ignore diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 81c932f42..898298954 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -21,7 +21,7 @@ First, install the dependencies: $ make install ``` -Then run the test suite and static analyzer: +Then run the test suite and static analyzers: ```bash $ make test diff --git a/.styleci.yml b/.styleci.yml index a923f78ee..0eeb39abb 100644 --- a/.styleci.yml +++ b/.styleci.yml @@ -7,6 +7,7 @@ enabled: - alpha_ordered_imports - array_indentation - const_visibility_required + - declare_strict_types - native_constant_invocation - native_function_invocation - phpdoc_order diff --git a/CHANGELOG.md b/CHANGELOG.md index 7334f8ee3..80ab1c17f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,19 @@ CHANGE LOG ========== +## 10.0.2 (26/10/2020) + +* Fixed phpdoc typo +* Fixed broken query builder + + +## 10.0.1 (24/10/2020) + +* Fixed using the name of a group as an ID +* Fixed various phpdoc issues +* Reverted query builder changes + + ## 10.0.0 (15/08/2020) * Added void return types to void methods diff --git a/composer.json b/composer.json index bcf57c25b..81208bce6 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "ext-xml": "*", "php-http/cache-plugin": "^1.7", "php-http/client-common": "^2.3", - "php-http/discovery": "^1.9", + "php-http/discovery": "^1.12", "php-http/httplug": "^2.1", "php-http/multipart-stream-builder": "^1.1", "psr/cache": "^1.0", diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index c5438dd29..29c373163 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,7 +1,7 @@ parameters: ignoreErrors: - - message: "#^Parameter \\#1 \\$error_handler of function set_error_handler expects \\(callable\\(int, string, string, int, array\\)\\: bool\\)\\|null, Closure\\(\\)\\: void given\\.$#" + message: "#^Parameter \\#1 \\$callback of function set_error_handler expects \\(callable\\(int, string, string, int, array\\)\\: bool\\)\\|null, Closure\\(\\)\\: void given\\.$#" count: 1 path: src/Api/AbstractApi.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index ff0d9a9ed..d60257f37 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + diff --git a/rector.php b/rector.php new file mode 100644 index 000000000..0857e6b09 --- /dev/null +++ b/rector.php @@ -0,0 +1,170 @@ +parameters(); + $parameters->set('autoload_paths', [__DIR__.'/vendor/autoload.php', __DIR__.'/vendor-bin/phpunit/vendor/autoload.php']); + $parameters->set('auto_import_names', true); + $parameters->set('import_short_classes', false); + $parameters->set('import_doc_blocks', false); + $parameters->set('php_version_features', '7.1'); + $parameters->set('paths', [__DIR__.'/src', __DIR__.'/tests']); + $parameters->set('sets', ['php52', 'php53', 'php54', 'php55', 'php56', 'php70', 'php71', 'phpunit40', 'phpunit50', 'phpunit60', 'phpunit70', 'phpunit75']); + + $services = $containerConfigurator->services(); + $services->set(ArrayKeyExistsTernaryThenValueToCoalescingRector::class); + $services->set(ArrayKeysAndInArrayToArrayKeyExistsRector::class); + $services->set(ArrayMergeOfNonArraysToSimpleArrayRector::class); + $services->set(BooleanNotIdenticalToNotIdenticalRector::class); + $services->set(ChangeArrayPushToArrayAssignRector::class); + $services->set(CombineIfRector::class); + $services->set(CombinedAssignRector::class); + $services->set(CompactToVariablesRector::class); + $services->set(CompleteDynamicPropertiesRector::class); + $services->set(ConsecutiveNullCompareReturnsToNullCoalesceQueueRector::class); + $services->set(ExplicitBoolCompareRector::class); + $services->set(ForeachToInArrayRector::class); + $services->set(InArrayAndArrayKeysToArrayKeyExistsRector::class); + $services->set(InlineIfToExplicitIfRector::class); + $services->set(IntvalToTypeCastRector::class); + $services->set(IsAWithStringWithThirdArgumentRector::class); + $services->set(RemoveAlwaysTrueConditionSetInConstructorRector::class); + $services->set(RemoveSoleValueSprintfRector::class); + $services->set(ShortenElseIfRector::class); + $services->set(SimplifyArraySearchRector::class); + $services->set(SimplifyBoolIdenticalTrueRector::class); + $services->set(SimplifyConditionsRector::class); + $services->set(SimplifyDuplicatedTernaryRector::class); + $services->set(SimplifyForeachToCoalescingRector::class); + $services->set(SimplifyIfElseToTernaryRector::class); + $services->set(SimplifyIfIssetToNullCoalescingRector::class); + $services->set(SimplifyIfNotNullReturnRector::class); + $services->set(SimplifyIfReturnBoolRector::class); + $services->set(SimplifyInArrayValuesRector::class); + $services->set(SimplifyStrposLowerRector::class); + $services->set(SimplifyTautologyTernaryRector::class); + $services->set(SimplifyUselessVariableRector::class); + $services->set(SingleInArrayToCompareRector::class); + $services->set(SplitListAssignToSeparateLineRector::class); + $services->set(StrlenZeroToIdenticalEmptyStringRector::class); + $services->set(UnnecessaryTernaryExpressionRector::class); + $services->set(UseIdenticalOverEqualWithSameTypeRector::class); + $services->set(ConsistentImplodeRector::class); + $services->set(EncapsedStringsToSprintfRector::class); + $services->set(FunctionCallToConstantRector::class); + $services->set(MakeInheritedMethodVisibilitySameAsParentRector::class); + $services->set(NullableCompareToNullRector::class); + $services->set(SimpleArrayCallableToStringRector::class); + $services->set(SplitGroupedConstantsAndPropertiesRector::class); + $services->set(VarConstantCommentRector::class); + $services->set(VersionCompareFuncCallToConstantRector::class); + $services->set(RemoveAlwaysTrueIfConditionRector::class); + $services->set(RemoveAndTrueRector::class); + $services->set(RemoveAssignOfVoidReturnFunctionRector::class); + $services->set(RemoveCodeAfterReturnRector::class); + $services->set(RemoveDeadIfForeachForRector::class); + $services->set(RemoveDeadReturnRector::class); + $services->set(RemoveDeadStmtRector::class); + $services->set(RemoveDeadTryCatchRector::class); + $services->set(RemoveDeadZeroAndOneOperationRector::class); + $services->set(RemoveDoubleAssignRector::class); + $services->set(RemoveDuplicatedArrayKeyRector::class); + $services->set(RemoveDuplicatedCaseInSwitchRector::class); + $services->set(RemoveDuplicatedIfReturnRector::class); + $services->set(RemoveDuplicatedInstanceOfRector::class); + $services->set(RemoveUnreachableStatementRector::class); + $services->set(RemoveUnusedClassConstantRector::class); + $services->set(RemoveUnusedForeachKeyRector::class); + $services->set(RemoveUnusedNonEmptyArrayBeforeForeachRector::class); + $services->set(RemoveUnusedPrivateConstantRector::class); + $services->set(RemoveUnusedPrivateMethodRector::class); + $services->set(RemoveUnusedPrivatePropertyRector::class); + $services->set(RemoveUnusedVariableAssignRector::class); + $services->set(SimplifyIfElseWithSameContentRector::class); + $services->set(SimplifyMirrorAssignRector::class); + $services->set(TernaryToBooleanOrFalseToBooleanAndRector::class); + $services->set(PreslashSimpleFunctionRector::class); + $services->set(ChangeNestedForeachIfsToEarlyContinueRector::class); + $services->set(ChangeIfElseValueAssignToEarlyReturnRector::class); + $services->set(ChangeNestedIfsToEarlyReturnRector::class); + $services->set(RemoveAlwaysElseRector::class); +}; diff --git a/rector.yml b/rector.yml deleted file mode 100644 index 9e1dcadaa..000000000 --- a/rector.yml +++ /dev/null @@ -1,102 +0,0 @@ -parameters: - autoload_paths: - - 'vendor/autoload.php' - - 'vendor-bin/phpunit/vendor/autoload.php' - auto_import_names: true - import_short_classes: false - import_doc_blocks: false - php_version_features: '7.1' - paths: - - 'src' - - 'tests' - sets: - - 'php52' - - 'php53' - - 'php54' - - 'php55' - - 'php56' - - 'php70' - - 'php71' - - 'phpunit40' - - 'phpunit50' - - 'phpunit60' - - 'phpunit70' - - 'phpunit75' - -services: - Rector\CodeQuality\Rector\Ternary\ArrayKeyExistsTernaryThenValueToCoalescingRector: ~ - Rector\CodeQuality\Rector\FuncCall\ArrayKeysAndInArrayToArrayKeyExistsRector: ~ - Rector\CodeQuality\Rector\FuncCall\ArrayMergeOfNonArraysToSimpleArrayRector: ~ - Rector\CodeQuality\Rector\Identical\BooleanNotIdenticalToNotIdenticalRector: ~ - Rector\CodeQuality\Rector\FuncCall\ChangeArrayPushToArrayAssignRector: ~ - Rector\CodeQuality\Rector\If_\CombineIfRector: ~ - Rector\CodeQuality\Rector\Assign\CombinedAssignRector: ~ - Rector\CodeQuality\Rector\FuncCall\CompactToVariablesRector: ~ - Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector: ~ - Rector\CodeQuality\Rector\If_\ConsecutiveNullCompareReturnsToNullCoalesceQueueRector: ~ - Rector\CodeQuality\Rector\If_\ExplicitBoolCompareRector: ~ - Rector\CodeQuality\Rector\Foreach_\ForeachToInArrayRector: ~ - Rector\CodeQuality\Rector\FuncCall\InArrayAndArrayKeysToArrayKeyExistsRector: ~ - Rector\CodeQuality\Rector\BinaryOp\InlineIfToExplicitIfRector: ~ - Rector\CodeQuality\Rector\FuncCall\IntvalToTypeCastRector: ~ - Rector\CodeQuality\Rector\FuncCall\IsAWithStringWithThirdArgumentRector: ~ - Rector\CodeQuality\Rector\If_\RemoveAlwaysTrueConditionSetInConstructorRector: ~ - Rector\CodeQuality\Rector\FuncCall\RemoveSoleValueSprintfRector: ~ - Rector\CodeQuality\Rector\If_\ShortenElseIfRector: ~ - Rector\CodeQuality\Rector\Identical\SimplifyArraySearchRector: ~ - Rector\CodeQuality\Rector\Identical\SimplifyBoolIdenticalTrueRector: ~ - Rector\CodeQuality\Rector\Identical\SimplifyConditionsRector: ~ - Rector\CodeQuality\Rector\Ternary\SimplifyDuplicatedTernaryRector: ~ - Rector\CodeQuality\Rector\Foreach_\SimplifyForeachToCoalescingRector: ~ - Rector\CodeQuality\Rector\If_\SimplifyIfElseToTernaryRector: ~ - Rector\CodeQuality\Rector\If_\SimplifyIfIssetToNullCoalescingRector: ~ - Rector\CodeQuality\Rector\If_\SimplifyIfNotNullReturnRector: ~ - Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector: ~ - Rector\CodeQuality\Rector\FuncCall\SimplifyInArrayValuesRector: ~ - Rector\CodeQuality\Rector\FuncCall\SimplifyStrposLowerRector: ~ - Rector\CodeQuality\Rector\Ternary\SimplifyTautologyTernaryRector: ~ - Rector\CodeQuality\Rector\Return_\SimplifyUselessVariableRector: ~ - Rector\CodeQuality\Rector\FuncCall\SingleInArrayToCompareRector: ~ - Rector\CodeQuality\Rector\Assign\SplitListAssignToSeparateLineRector: ~ - Rector\CodeQuality\Rector\FuncCall\StrlenZeroToIdenticalEmptyStringRector: ~ - Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector: ~ - Rector\CodeQuality\Rector\Equal\UseIdenticalOverEqualWithSameTypeRector: ~ - Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector: ~ - Rector\CodingStyle\Rector\Encapsed\EncapsedStringsToSprintfRector: ~ - Rector\CodingStyle\Rector\FuncCall\FunctionCallToConstantRector: ~ - Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector: ~ - Rector\CodingStyle\Rector\If_\NullableCompareToNullRector: ~ - Rector\CodingStyle\Rector\FuncCall\SimpleArrayCallableToStringRector: ~ - Rector\CodingStyle\Rector\ClassConst\SplitGroupedConstantsAndPropertiesRector: ~ - Rector\CodingStyle\Rector\ClassConst\VarConstantCommentRector: ~ - Rector\CodingStyle\Rector\FuncCall\VersionCompareFuncCallToConstantRector: ~ - Rector\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector: ~ - Rector\DeadCode\Rector\BooleanAnd\RemoveAndTrueRector: ~ - Rector\DeadCode\Rector\Assign\RemoveAssignOfVoidReturnFunctionRector: ~ - Rector\DeadCode\Rector\FunctionLike\RemoveCodeAfterReturnRector: ~ - Rector\DeadCode\Rector\For_\RemoveDeadIfForeachForRector: ~ - Rector\DeadCode\Rector\FunctionLike\RemoveDeadReturnRector: ~ - Rector\DeadCode\Rector\Stmt\RemoveDeadStmtRector: ~ - Rector\DeadCode\Rector\TryCatch\RemoveDeadTryCatchRector: ~ - Rector\DeadCode\Rector\Plus\RemoveDeadZeroAndOneOperationRector: ~ - Rector\DeadCode\Rector\Assign\RemoveDoubleAssignRector: ~ - Rector\DeadCode\Rector\Array_\RemoveDuplicatedArrayKeyRector: ~ - Rector\DeadCode\Rector\Switch_\RemoveDuplicatedCaseInSwitchRector: ~ - Rector\DeadCode\Rector\FunctionLike\RemoveDuplicatedIfReturnRector: ~ - Rector\DeadCode\Rector\Instanceof_\RemoveDuplicatedInstanceOfRector: ~ - Rector\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector: ~ - Rector\DeadCode\Rector\ClassConst\RemoveUnusedClassConstantRector: ~ - Rector\DeadCode\Rector\Foreach_\RemoveUnusedForeachKeyRector: ~ - Rector\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector: ~ - Rector\DeadCode\Rector\ClassConst\RemoveUnusedPrivateConstantRector: ~ - Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPrivateMethodRector: ~ - Rector\DeadCode\Rector\Property\RemoveUnusedPrivatePropertyRector: ~ - Rector\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector: ~ - Rector\DeadCode\Rector\If_\SimplifyIfElseWithSameContentRector: ~ - Rector\DeadCode\Rector\Expression\SimplifyMirrorAssignRector: ~ - Rector\DeadCode\Rector\Ternary\TernaryToBooleanOrFalseToBooleanAndRector: ~ - Rector\Performance\Rector\FuncCall\PreslashSimpleFunctionRector: ~ - Rector\SOLID\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector: ~ - Rector\SOLID\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector: ~ - Rector\SOLID\Rector\If_\ChangeNestedIfsToEarlyReturnRector: ~ - Rector\SOLID\Rector\If_\RemoveAlwaysElseRector: ~ diff --git a/src/Api/Groups.php b/src/Api/Groups.php index 46bc628f3..3d2bf98df 100644 --- a/src/Api/Groups.php +++ b/src/Api/Groups.php @@ -31,11 +31,11 @@ public function all(array $parameters = []) } /** - * @param int $id + * @param int|string $id * * @return mixed */ - public function show(int $id) + public function show($id) { return $this->get('groups/'.self::encodePath($id)); } @@ -71,44 +71,44 @@ public function create(string $name, string $path, string $description = null, s } /** - * @param int $id - * @param array $params + * @param int|string $id + * @param array $params * * @return mixed */ - public function update(int $id, array $params) + public function update($id, array $params) { return $this->put('groups/'.self::encodePath($id), $params); } /** - * @param int $group_id + * @param int|string $group_id * * @return mixed */ - public function remove(int $group_id) + public function remove($group_id) { return $this->delete('groups/'.self::encodePath($group_id)); } /** - * @param int $group_id + * @param int|string $group_id * @param int|string $project_id * * @return mixed */ - public function transfer(int $group_id, $project_id) + public function transfer($group_id, $project_id) { return $this->post('groups/'.self::encodePath($group_id).'/projects/'.self::encodePath($project_id)); } /** - * @param int $group_id - * @param array $parameters + * @param int|string $group_id + * @param array $parameters * * @return mixed */ - public function allMembers(int $group_id, array $parameters = []) + public function allMembers($group_id, array $parameters = []) { $resolver = $this->createOptionsResolver(); $resolver->setDefined('query'); @@ -117,15 +117,15 @@ public function allMembers(int $group_id, array $parameters = []) } /** - * @param int $group_id - * @param array $parameters { + * @param int|string $group_id + * @param array $parameters { * * @var string $query A query string to search for members. * } * * @return mixed */ - public function members(int $group_id, array $parameters = []) + public function members($group_id, array $parameters = []) { $resolver = $this->createOptionsResolver(); $resolver->setDefined('query'); @@ -134,24 +134,24 @@ public function members(int $group_id, array $parameters = []) } /** - * @param int $group_id - * @param int $user_id + * @param int|string $group_id + * @param int $user_id * * @return mixed */ - public function member(int $group_id, int $user_id) + public function member($group_id, int $user_id) { return $this->get('groups/'.self::encodePath($group_id).'/members/'.self::encodePath($user_id)); } /** - * @param int $group_id - * @param int $user_id - * @param int $access_level + * @param int|string $group_id + * @param int $user_id + * @param int $access_level * * @return mixed */ - public function addMember(int $group_id, int $user_id, int $access_level) + public function addMember($group_id, int $user_id, int $access_level) { return $this->post('groups/'.self::encodePath($group_id).'/members', [ 'user_id' => $user_id, @@ -160,13 +160,13 @@ public function addMember(int $group_id, int $user_id, int $access_level) } /** - * @param int $group_id - * @param int $user_id - * @param int $access_level + * @param int|string $group_id + * @param int $user_id + * @param int $access_level * * @return mixed */ - public function saveMember(int $group_id, int $user_id, int $access_level) + public function saveMember($group_id, int $user_id, int $access_level) { return $this->put('groups/'.self::encodePath($group_id).'/members/'.self::encodePath($user_id), [ 'access_level' => $access_level, @@ -174,19 +174,19 @@ public function saveMember(int $group_id, int $user_id, int $access_level) } /** - * @param int $group_id - * @param int $user_id + * @param int|string $group_id + * @param int $user_id * * @return mixed */ - public function removeMember(int $group_id, int $user_id) + public function removeMember($group_id, int $user_id) { return $this->delete('groups/'.self::encodePath($group_id).'/members/'.self::encodePath($user_id)); } /** - * @param int $id - * @param array $parameters { + * @param int|string $id + * @param array $parameters { * * @var bool $archived limit by archived status * @var string $visibility limit by visibility public, internal, or private @@ -206,7 +206,7 @@ public function removeMember(int $group_id, int $user_id) * * @return mixed */ - public function projects(int $id, array $parameters = []) + public function projects($id, array $parameters = []) { $resolver = $this->createOptionsResolver(); $booleanNormalizer = function (Options $resolver, $value) { @@ -264,8 +264,8 @@ public function projects(int $id, array $parameters = []) } /** - * @param int $group_id - * @param array $parameters { + * @param int|string $group_id + * @param array $parameters { * * @var int[] $skip_groups skip the group IDs passes * @var bool $all_available show all the groups you have access to @@ -278,7 +278,7 @@ public function projects(int $id, array $parameters = []) * * @return mixed */ - public function subgroups(int $group_id, array $parameters = []) + public function subgroups($group_id, array $parameters = []) { $resolver = $this->getGroupSearchResolver(); @@ -286,12 +286,12 @@ public function subgroups(int $group_id, array $parameters = []) } /** - * @param int $group_id - * @param array $parameters + * @param int|string $group_id + * @param array $parameters * * @return mixed */ - public function labels(int $group_id, array $parameters = []) + public function labels($group_id, array $parameters = []) { $resolver = $this->createOptionsResolver(); @@ -299,34 +299,34 @@ public function labels(int $group_id, array $parameters = []) } /** - * @param int $group_id - * @param array $params + * @param int|string $group_id + * @param array $params * * @return mixed */ - public function addLabel(int $group_id, array $params) + public function addLabel($group_id, array $params) { return $this->post('groups/'.self::encodePath($group_id).'/labels', $params); } /** - * @param int $group_id - * @param array $params + * @param int|string $group_id + * @param array $params * * @return mixed */ - public function updateLabel(int $group_id, array $params) + public function updateLabel($group_id, array $params) { return $this->put('groups/'.self::encodePath($group_id).'/labels', $params); } /** - * @param int $group_id - * @param string $name + * @param int|string $group_id + * @param string $name * * @return mixed */ - public function removeLabel(int $group_id, string $name) + public function removeLabel($group_id, string $name) { return $this->delete('groups/'.self::encodePath($group_id).'/labels', [ 'name' => $name, @@ -334,12 +334,12 @@ public function removeLabel(int $group_id, string $name) } /** - * @param int $group_id - * @param array $parameters + * @param int|string $group_id + * @param array $parameters * * @return mixed */ - public function variables(int $group_id, array $parameters = []) + public function variables($group_id, array $parameters = []) { $resolver = $this->createOptionsResolver(); @@ -347,25 +347,25 @@ public function variables(int $group_id, array $parameters = []) } /** - * @param int $group_id - * @param string $key + * @param int|string $group_id + * @param string $key * * @return mixed */ - public function variable(int $group_id, string $key) + public function variable($group_id, string $key) { return $this->get('groups/'.self::encodePath($group_id).'/variables/'.self::encodePath($key)); } /** - * @param int $group_id - * @param string $key - * @param string $value - * @param bool|null $protected + * @param int|string $group_id + * @param string $key + * @param string $value + * @param bool|null $protected * * @return mixed */ - public function addVariable(int $group_id, string $key, string $value, ?bool $protected = null) + public function addVariable($group_id, string $key, string $value, ?bool $protected = null) { $payload = [ 'key' => $key, @@ -380,14 +380,14 @@ public function addVariable(int $group_id, string $key, string $value, ?bool $pr } /** - * @param int $group_id - * @param string $key - * @param string $value - * @param bool|null $protected + * @param int|string $group_id + * @param string $key + * @param string $value + * @param bool|null $protected * * @return mixed */ - public function updateVariable(int $group_id, string $key, string $value, ?bool $protected = null) + public function updateVariable($group_id, string $key, string $value, ?bool $protected = null) { $payload = [ 'value' => $value, @@ -401,12 +401,12 @@ public function updateVariable(int $group_id, string $key, string $value, ?bool } /** - * @param int $group_id - * @param string $key + * @param int|string $group_id + * @param string $key * * @return mixed */ - public function removeVariable(int $group_id, string $key) + public function removeVariable($group_id, string $key) { return $this->delete('groups/'.self::encodePath($group_id).'/variables/'.self::encodePath($key)); } diff --git a/src/Api/GroupsBoards.php b/src/Api/GroupsBoards.php index 0541b4715..04e6594af 100644 --- a/src/Api/GroupsBoards.php +++ b/src/Api/GroupsBoards.php @@ -7,12 +7,12 @@ class GroupsBoards extends AbstractApi { /** - * @param int|null $group_id - * @param array $parameters + * @param int|string|null $group_id + * @param array $parameters * * @return mixed */ - public function all(?int $group_id = null, array $parameters = []) + public function all($group_id = null, array $parameters = []) { $resolver = $this->createOptionsResolver(); @@ -22,81 +22,81 @@ public function all(?int $group_id = null, array $parameters = []) } /** - * @param int $group_id - * @param int $board_id + * @param int|string $group_id + * @param int $board_id * * @return mixed */ - public function show(int $group_id, int $board_id) + public function show($group_id, int $board_id) { return $this->get('groups/'.self::encodePath($group_id).'/boards/'.self::encodePath($board_id)); } /** - * @param int $group_id - * @param array $params + * @param int|string $group_id + * @param array $params * * @return mixed */ - public function create(int $group_id, array $params) + public function create($group_id, array $params) { return $this->post('groups/'.self::encodePath($group_id).'/boards', $params); } /** - * @param int $group_id - * @param int $board_id - * @param array $params + * @param int|string $group_id + * @param int $board_id + * @param array $params * * @return mixed */ - public function update(int $group_id, int $board_id, array $params) + public function update($group_id, int $board_id, array $params) { return $this->put('groups/'.self::encodePath($group_id).'/boards/'.self::encodePath($board_id), $params); } /** - * @param int $group_id - * @param int $board_id + * @param int|string $group_id + * @param int $board_id * * @return mixed */ - public function remove(int $group_id, int $board_id) + public function remove($group_id, int $board_id) { return $this->delete('groups/'.self::encodePath($group_id).'/boards/'.self::encodePath($board_id)); } /** - * @param int $group_id - * @param int $board_id + * @param int|string $group_id + * @param int $board_id * * @return mixed */ - public function allLists(int $group_id, int $board_id) + public function allLists($group_id, int $board_id) { return $this->get('groups/'.self::encodePath($group_id).'/boards/'.self::encodePath($board_id).'/lists'); } /** - * @param int $group_id - * @param int $board_id - * @param int $list_id + * @param int|string $group_id + * @param int $board_id + * @param int $list_id * * @return mixed */ - public function showList(int $group_id, int $board_id, int $list_id) + public function showList($group_id, int $board_id, int $list_id) { return $this->get('groups/'.self::encodePath($group_id).'/boards/'.self::encodePath($board_id).'/lists/'.self::encodePath($list_id)); } /** - * @param int $group_id - * @param int $board_id - * @param int $label_id + * @param int|string $group_id + * @param int $board_id + * @param int $label_id * * @return mixed */ - public function createList(int $group_id, int $board_id, int $label_id) + public function createList($group_id, int $board_id, int $label_id) { $params = [ 'label_id' => $label_id, @@ -106,14 +106,14 @@ public function createList(int $group_id, int $board_id, int $label_id) } /** - * @param int $group_id - * @param int $board_id - * @param int $list_id - * @param int $position + * @param int|string $group_id + * @param int $board_id + * @param int $list_id + * @param int $position * * @return mixed */ - public function updateList(int $group_id, int $board_id, int $list_id, int $position) + public function updateList($group_id, int $board_id, int $list_id, int $position) { $params = [ 'position' => $position, @@ -123,13 +123,13 @@ public function updateList(int $group_id, int $board_id, int $list_id, int $posi } /** - * @param int $group_id - * @param int $board_id - * @param int $list_id + * @param int|string $group_id + * @param int $board_id + * @param int $list_id * * @return mixed */ - public function deleteList(int $group_id, int $board_id, int $list_id) + public function deleteList($group_id, int $board_id, int $list_id) { return $this->delete('groups/'.self::encodePath($group_id).'/boards/'.self::encodePath($board_id).'/lists/'.self::encodePath($list_id)); } diff --git a/src/Api/GroupsMilestones.php b/src/Api/GroupsMilestones.php index 6e121d911..a6e0e19e4 100644 --- a/src/Api/GroupsMilestones.php +++ b/src/Api/GroupsMilestones.php @@ -17,8 +17,8 @@ class GroupsMilestones extends AbstractApi public const STATE_CLOSED = 'closed'; /** - * @param int $group_id - * @param array $parameters { + * @param int|string $group_id + * @param array $parameters { * * @var int[] $iids return only the milestones having the given iids * @var string $state return only active or closed milestones @@ -27,7 +27,7 @@ class GroupsMilestones extends AbstractApi * * @return mixed */ - public function all(int $group_id, array $parameters = []) + public function all($group_id, array $parameters = []) { $resolver = $this->createOptionsResolver(); $resolver->setDefined('iids') @@ -45,68 +45,68 @@ public function all(int $group_id, array $parameters = []) } /** - * @param int $group_id - * @param int $milestone_id + * @param int|string $group_id + * @param int $milestone_id * * @return mixed */ - public function show(int $group_id, int $milestone_id) + public function show($group_id, int $milestone_id) { return $this->get('groups/'.self::encodePath($group_id).'/milestones/'.self::encodePath($milestone_id)); } /** - * @param int $group_id - * @param array $params + * @param int|string $group_id + * @param array $params * * @return mixed */ - public function create(int $group_id, array $params) + public function create($group_id, array $params) { return $this->post('groups/'.self::encodePath($group_id).'/milestones', $params); } /** - * @param int $group_id - * @param int $milestone_id - * @param array $params + * @param int|string $group_id + * @param int $milestone_id + * @param array $params * * @return mixed */ - public function update(int $group_id, int $milestone_id, array $params) + public function update($group_id, int $milestone_id, array $params) { return $this->put('groups/'.self::encodePath($group_id).'/milestones/'.self::encodePath($milestone_id), $params); } /** - * @param int $group_id - * @param int $milestone_id + * @param int|string $group_id + * @param int $milestone_id * * @return mixed */ - public function remove(int $group_id, int $milestone_id) + public function remove($group_id, int $milestone_id) { return $this->delete('groups/'.self::encodePath($group_id).'/milestones/'.self::encodePath($milestone_id)); } /** - * @param int $group_id - * @param int $milestone_id + * @param int|string $group_id + * @param int $milestone_id * * @return mixed */ - public function issues(int $group_id, int $milestone_id) + public function issues($group_id, int $milestone_id) { return $this->get('groups/'.self::encodePath($group_id).'/milestones/'.self::encodePath($milestone_id).'/issues'); } /** - * @param int $group_id - * @param int $milestone_id + * @param int|string $group_id + * @param int $milestone_id * * @return mixed */ - public function mergeRequests(int $group_id, int $milestone_id) + public function mergeRequests($group_id, int $milestone_id) { return $this->get('groups/'.self::encodePath($group_id).'/milestones/'.self::encodePath($milestone_id).'/merge_requests'); } diff --git a/src/Api/Issues.php b/src/Api/Issues.php index 78ba1aeb5..c7e0ffadb 100644 --- a/src/Api/Issues.php +++ b/src/Api/Issues.php @@ -43,12 +43,12 @@ public function all($project_id = null, array $parameters = []) } /** - * @param int $group_id - * @param array $parameters + * @param int|string $group_id + * @param array $parameters * * @return mixed */ - public function group(int $group_id, array $parameters = []) + public function group($group_id, array $parameters = []) { return $this->get( 'groups/'.self::encodePath($group_id).'/issues', @@ -399,7 +399,7 @@ public function closedByMergeRequests($project_id, int $issue_iid) */ public function relatedMergeRequests($project_id, int $issue_iid) { - return $this->get($this->getProjectPath($project_id, 'issues/'.$this::encodePath($issue_iid).'/related_merge_requests')); + return $this->get($this->getProjectPath($project_id, 'issues/'.self::encodePath($issue_iid).'/related_merge_requests')); } /** diff --git a/src/Api/IssuesStatistics.php b/src/Api/IssuesStatistics.php index b733b07fb..34c455db7 100644 --- a/src/Api/IssuesStatistics.php +++ b/src/Api/IssuesStatistics.php @@ -31,12 +31,12 @@ public function project($project_id, array $parameters) } /** - * @param int $group_id - * @param array $parameters + * @param int|string $group_id + * @param array $parameters * * @return mixed */ - public function group(int $group_id, array $parameters) + public function group($group_id, array $parameters) { return $this->get('groups/'.self::encodePath($group_id).'/issues_statistics', $this->createOptionsResolver()->resolve($parameters)); } diff --git a/src/Api/Projects.php b/src/Api/Projects.php index 6237a33cc..62db861c0 100644 --- a/src/Api/Projects.php +++ b/src/Api/Projects.php @@ -530,7 +530,7 @@ public function boards($project_id) */ public function getRepositoryCommitDiscussions($project_id, string $commit_id) { - return $this->get($this->getProjectPath($project_id, 'repository/commits/'.$this::encodePath($commit_id)).'/discussions'); + return $this->get($this->getProjectPath($project_id, 'repository/commits/'.self::encodePath($commit_id)).'/discussions'); } /** @@ -977,11 +977,11 @@ public function addShare($project_id, array $parameters = []) /** * @param int|string $project_id - * @param int $group_id + * @param int|string $group_id * * @return mixed */ - public function removeShare($project_id, int $group_id) + public function removeShare($project_id, $group_id) { return $this->delete($this->getProjectPath($project_id, 'share/'.$group_id)); } diff --git a/src/HttpClient/Builder.php b/src/HttpClient/Builder.php index 0c6045ca0..ff703c5a8 100644 --- a/src/HttpClient/Builder.php +++ b/src/HttpClient/Builder.php @@ -98,7 +98,7 @@ public function __construct( $this->httpClient = $httpClient ?? Psr18ClientDiscovery::find(); $this->requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory(); $this->streamFactory = $streamFactory ?? Psr17FactoryDiscovery::findStreamFactory(); - $this->uriFactory = $uriFactory ?? Psr17FactoryDiscovery::findUrlFactory(); + $this->uriFactory = $uriFactory ?? Psr17FactoryDiscovery::findUriFactory(); } /** diff --git a/src/HttpClient/Util/QueryStringBuilder.php b/src/HttpClient/Util/QueryStringBuilder.php index 4bb89ae6f..efda1e655 100644 --- a/src/HttpClient/Util/QueryStringBuilder.php +++ b/src/HttpClient/Util/QueryStringBuilder.php @@ -12,16 +12,72 @@ final class QueryStringBuilder /** * Encode a query as a query string according to RFC 3986. * + * Indexed arrays are encoded using empty squared brackets ([]) unlike + * `http_build_query`. + * * @param array $query * * @return string */ public static function build(array $query) { - if (0 === \count($query)) { - return ''; + return \sprintf('?%s', \implode('&', \array_map(function ($value, $key) { + return self::encode($value, $key); + }, $query, \array_keys($query)))); + } + + /** + * Encode a value. + * + * @param mixed $query + * @param string $prefix + * + * @return string + */ + private static function encode($query, $prefix) + { + if (!\is_array($query)) { + return self::rawurlencode($prefix).'='.self::rawurlencode($query); + } + + $isList = self::isList($query); + + return \implode('&', \array_map(function ($value, $key) use ($prefix, $isList) { + $prefix = $isList ? $prefix.'[]' : $prefix.'['.$key.']'; + + return self::encode($value, $prefix); + }, $query, \array_keys($query))); + } + + /** + * Tell if the given array is a list. + * + * @param array $query + * + * @return bool + */ + private static function isList(array $query) + { + if (0 === \count($query) || !isset($query[0])) { + return false; + } + + return \array_keys($query) === \range(0, \count($query) - 1); + } + + /** + * Encode a value like rawurlencode, but return "0" when false is given. + * + * @param mixed $value + * + * @return string + */ + private static function rawurlencode($value) + { + if (false === $value) { + return '0'; } - return \sprintf('?%s', \http_build_query($query, '', '&', \PHP_QUERY_RFC3986)); + return \rawurlencode((string) $value); } } diff --git a/src/Model/Commit.php b/src/Model/Commit.php index 717c0f7fe..add89c578 100644 --- a/src/Model/Commit.php +++ b/src/Model/Commit.php @@ -79,12 +79,12 @@ public static function fromArray(Client $client, Project $project, array $data) /** * @param Project $project - * @param int|null $id + * @param string|null $id * @param Client|null $client * * @return void */ - public function __construct(Project $project, ?int $id = null, Client $client = null) + public function __construct(Project $project, ?string $id = null, Client $client = null) { $this->setClient($client); $this->setData('project', $project); diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 89b0bdf56..77464d56f 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -1,5 +1,7 @@ assertSame(\sprintf('?%s', $expected), QueryStringBuilder::build($query)); + } + + public function queryStringProvider() + { + //Indexed array. + yield [ + ['iids' => [88, 86]], + //iids[]=88&iids[]=86 + 'iids%5B%5D=88&iids%5B%5D=86', + ]; + + //Non indexed array with only numeric keys. + yield [ + ['iids' => [0 => 88, 2 => 86]], + //iids[0]=88&iids[2]=86 + 'iids%5B0%5D=88&iids%5B2%5D=86', + ]; + + yield [ + [ + 'source_branch' => 'test_source', + 'target_branch' => 'test_master', + 'title' => 'test', + ], + 'source_branch=test_source&target_branch=test_master&title=test', + ]; + + //Boolean encoding + yield [ + ['push_events' => false, 'merge_requests_events' => 1], + 'push_events=0&merge_requests_events=1', + ]; + + //A deeply nested array. + yield [ + [ + 'search' => 'a project', + 'owned' => 'true', + 'iids' => [88, 86], + 'assoc' => [ + 'a' => 'b', + 'c' => [ + 'd' => 'e', + 'f' => 'g', + ], + ], + 'nested' => [ + 'a' => [ + [ + 'b' => 'c', + ], + [ + 'd' => 'e', + 'f' => [ + 'g' => 'h', + 'i' => 'j', + 'k' => [87, 89], + ], + ], + ], + ], + ], + //search=a project + //&owned=true + //&iids[]=88&iids[]=86 + //&assoc[a]=b&assoc[c][d]=e&assoc[c][f]=g + //&nested[a][][b]=c&nested[a][][d]=e + //&nested[a][][f][g]=h&nested[a][][f][i]=j + //&nested[a][][f][k][]=87&nested[a][][f][k][]=89 + 'search=a%20project&owned=true&iids%5B%5D=88&iids%5B%5D=86'. + '&assoc%5Ba%5D=b&assoc%5Bc%5D%5Bd%5D=e&assoc%5Bc%5D%5Bf%5D=g'. + '&nested%5Ba%5D%5B%5D%5Bb%5D=c&nested%5Ba%5D%5B%5D%5Bd%5D=e'. + '&nested%5Ba%5D%5B%5D%5Bf%5D%5Bg%5D=h&nested%5Ba%5D%5B%5D%5Bf%5D%5Bi%5D=j'. + '&nested%5Ba%5D%5B%5D%5Bf%5D%5Bk%5D%5B%5D=87&nested%5Ba%5D%5B%5D%5Bf%5D%5Bk%5D%5B%5D=89', + ]; + } +} diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index bede693f8..29ce1e69c 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -1,5 +1,7 @@