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);
+ }
+}