diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6537ca4 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1f35192 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# Auto detect text files and perform LF normalization +* text=auto + +.gitattributes export-ignore +.github export-ignore +.gitignore export-ignore +.github/ export-ignore +tests/ export-ignore +phpunit.xml export-ignore +phpstan.neon export-ignore +psalm.xml export-ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..0cc5fb2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: ci + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + laravel-tests: + runs-on: ubuntu-latest + timeout-minutes: 5 + strategy: + matrix: + php-version: [7.4,8.0] + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php-version }}" + coverage: "pcov" + + - name: Install PHP dependencies + run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + + - name: Run PHPUnit + run: vendor/bin/phpunit --coverage-clover=coverage.xml + + - name: Upload coverage + uses: codecov/codecov-action@v2 + with: + files: ./coverage.xml diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 0000000..5f950f4 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,18 @@ +name: release-please + +on: + push: + branches: + - main + +jobs: + update_release_draft: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - uses: google-github-actions/release-please-action@v2 + with: + release-type: php + - uses: actions/checkout@v2 + if: ${{ steps.release.outputs.release_created }} diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000..1f35a16 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,66 @@ +name: static analysis + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + phpstan: + name: PHPStan + runs-on: ubuntu-latest + timeout-minutes: 5 + strategy: + matrix: + php-version: [7.4,8.0] + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php-version }}" + coverage: none + extensions: mbstring + tools: composer + + - name: Download dependencies + run: composer update --no-interaction --no-progress + + - name: Download PHPStan + run: composer bin phpstan require phpstan/phpstan + + - name: Execute PHPStan + run: vendor/bin/phpstan analyze --no-progress + + psalm: + name: Psalm + runs-on: ubuntu-latest + timeout-minutes: 5 + strategy: + matrix: + php-version: [7.4,8.0] + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php-version }}" + coverage: none + extensions: mbstring + tools: composer + + - name: Download dependencies + run: composer update --no-interaction --no-progress + + - name: Download Psalm + run: composer bin psalm require vimeo/psalm + + - name: Execute Psalm + run: vendor/bin/psalm --no-progress --shepherd --output-format=github diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ca314ba --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +/vendor/ +/vendor-bin/ +node_modules/ +npm-debug.log +yarn-error.log + +# Laravel 4 specific +bootstrap/compiled.php +app/storage/ + +# Laravel 5 & Lumen specific +public/storage +public/hot + +# Laravel 5 & Lumen specific with changed public path +public_html/storage +public_html/hot + +storage/*.key +.env +Homestead.yaml +Homestead.json +/.vagrant +.phpunit.result.cache +composer.lock diff --git a/README.md b/README.md index 8f089a8..096afce 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,40 @@ # resolve-uri + +[![Latest Stable Version](https://img.shields.io/github/v/release/brokeyourbike/resolve-uri)](https://github.com/brokeyourbike/resolve-uri/releases) +[![Total Downloads](https://poser.pugx.org/brokeyourbike/resolve-uri/downloads)](https://packagist.org/packages/brokeyourbike/resolve-uri) +[![License: MPL-2.0](https://img.shields.io/badge/license-MPL--2.0-purple.svg)](https://github.com/brokeyourbike/resolve-uri/blob/main/LICENSE) + +[![ci](https://github.com/brokeyourbike/resolve-uri/actions/workflows/ci.yml/badge.svg)](https://github.com/brokeyourbike/resolve-uri/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/brokeyourbike/resolve-uri/branch/main/graph/badge.svg?token=ImcgnxzGfc)](https://codecov.io/gh/brokeyourbike/resolve-uri) +[![Type Coverage](https://shepherd.dev/github/brokeyourbike/resolve-uri/coverage.svg)](https://shepherd.dev/github/brokeyourbike/resolve-uri) + Trait for resolving URI + +## Installation + +```bash +composer require brokeyourbike/resolve-uri +``` + +## Usage + +```php +use BrokeYourBike\ResolveUri\ResolveUriTrait; + +class APIClient +{ + use ResolveUriTrait; + + public function fetchProduct(string $productId) + { + $uri = $this->fetchUriFor('https://example.com', "products/{$productId}"); + } +} +``` + +## Why + +Resolve URI when `base_url` is not defined in HTTP client. + +## License +[Mozilla Public License v2.0](https://github.com/brokeyourbike/resolve-uri/blob/main/LICENSE) diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..fca4ff7 --- /dev/null +++ b/composer.json @@ -0,0 +1,33 @@ +{ + "name": "brokeyourbike/resolve-uri", + "description": "Trait for resolving URI", + "type": "library", + "license": "MPL-2.0", + "autoload": { + "psr-4": { + "BrokeYourBike\\ResolveUri\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "BrokeYourBike\\Tests\\ResolveUri\\": "tests/" + } + }, + "authors": [ + { + "name": "Ivan Stasiuk", + "email": "brokeyourbike@gmail.com", + "homepage": "https://github.com/brokeyourbike" + } + ], + "minimum-stability": "stable", + "require": { + "php": "^7.4|^8.0", + "psr/http-message": "^1.0", + "guzzlehttp/psr7": "^2" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4", + "phpunit/phpunit": "^9.5" + } +} diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..27bc5cc --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,5 @@ +parameters: + checkMissingIterableValueType: false + level: max + paths: + - src diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..78754c5 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,19 @@ + + + + + ./tests + + + + + + src + + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..8134333 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/src/ResolveUriTrait.php b/src/ResolveUriTrait.php new file mode 100644 index 0000000..fbfd102 --- /dev/null +++ b/src/ResolveUriTrait.php @@ -0,0 +1,29 @@ +. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +namespace BrokeYourBike\ResolveUri; + +use GuzzleHttp\Psr7\Utils; +use GuzzleHttp\Psr7\UriResolver; + +/** + * @author Ivan Stasiuk + */ +trait ResolveUriTrait +{ + /** + * @return \Psr\Http\Message\UriInterface + */ + public function resolveUriFor(string $baseUri, string $uri): \Psr\Http\Message\UriInterface + { + $baseUri = Utils::uriFor($baseUri); + $uri = Utils::uriFor($uri); + + return UriResolver::resolve($baseUri, $uri); + } +} diff --git a/tests/ApiClientFixture.php b/tests/ApiClientFixture.php new file mode 100644 index 0000000..abbea7b --- /dev/null +++ b/tests/ApiClientFixture.php @@ -0,0 +1,19 @@ +. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +namespace BrokeYourBike\Tests\ResolveUri; + +use BrokeYourBike\ResolveUri\ResolveUriTrait; + +/** + * @author Ivan Stasiuk + */ +class ApiClientFixture +{ + use ResolveUriTrait; +} diff --git a/tests/ResolveUriTest.php b/tests/ResolveUriTest.php new file mode 100644 index 0000000..fb9f044 --- /dev/null +++ b/tests/ResolveUriTest.php @@ -0,0 +1,37 @@ +. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +namespace BrokeYourBike\Tests\ResolveUri; + +use PHPUnit\Framework\TestCase; +use BrokeYourBike\ResolveUri\ResolveUriTrait; + +/** + * @author Ivan Stasiuk + */ +class ResolveUriTest extends TestCase +{ + /** @test */ + public function it_uses_json_request_result_trait(): void + { + $usedTraits = class_uses(ApiClientFixture::class); + + $this->assertArrayHasKey(ResolveUriTrait::class, $usedTraits); + } + + /** @test */ + public function it_can_resolve_uri() + { + $apiClient = new ApiClientFixture(); + + $uri = $apiClient->resolveUriFor('https://example.com', 'hello-world'); + + $this->assertInstanceOf(\Psr\Http\Message\UriInterface::class, $uri); + $this->assertSame('https://example.com/hello-world', (string) $uri); + } +}