Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Code Quality

on:
push:
branches: [master]
pull_request:
workflow_dispatch:

jobs:
phpstan:
name: PHPStan (PHP ${{ matrix.php-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-version: ['8.1', '8.5']
continue-on-error: ${{ matrix.php-version == '8.5' }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: none

- name: Install dependencies
run: composer install --no-interaction --prefer-dist

- name: Run PHPStan
run: vendor/bin/phpstan analyse --configuration=phpstan.neon --no-progress --error-format=github

phpcs:
name: PHP_CodeSniffer (PHP ${{ matrix.php-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-version: ['8.1', '8.5']
continue-on-error: ${{ matrix.php-version == '8.5' }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
coverage: none

- name: Install dependencies
run: composer install --no-interaction --prefer-dist

- name: Run PHPCS
run: vendor/bin/phpcs --standard=phpcs.xml src/
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ try {
$response = $client->post($secret_endpoint, $my_secret_stuff);
} catch (\TheNextInvoice\NoREST\Exceptions\RequestFailedException $e) {
echo 'oops, request failed: ' . $e->getMessage() . PHP_EOL;
echo 'the response headers are' . $e->getResponseHeaders() . PHP_EOL;
echo 'the response body was' . PHP_EOL;
echo $e->getBody();
}
Expand All @@ -32,7 +33,7 @@ Sometimes you need to interface with API's that aren't really playing by the rul
a couple of helpers.

### Treat header names case-sensitive
While RFC 2616 section 4.2 says that header field names should be treated in a case-insensitive manner, there are
While RFC 7230 section 3.2.2 says that header field names should be treated in a case-insensitive manner, there are
servers that do treat headers case-sensitive. If you want to make sure NoREST does not call `strtolower` on your header
key, do the following:
```php
Expand Down Expand Up @@ -62,3 +63,37 @@ $client = new \TheNextInvoice\NoREST\Client('https//api.example.com', [], [JSON_
```
All requests made with a client constructed this way will have `JSON_UNESCAPED_SLASHES` and `JSON_HEX_TAG` JSON flags
applied when encoding AND decoding the request and response body respectively.

### Timeouts
Some servers are slower than others, and NoREST's default timeouts of 10s for TLS handshakes and 30s for overall request
time might be too strict. To change those -for instance to 120s total and 10s handshake, simply call:
```php
// Construct client with higher timeouts
$client = new \TheNextInvoice\NoREST\Client('https//api.example.com', [], [], 120, 10);
// Or as a setter, returning a Client
$client = $client->setTimeout(120, 10);
```

### Response headers
Sometimes the body alone isn't enough: maybe you need a `Location` header, a `Retry-After`, or you're just trying to
figure out why a response got decoded the way it did. NoREST keeps the headers of the most recently received response
around for you, keyed by lowercased header name:
```php
$client->get('/endpoint');
$headers = $client->getLastResponseHeaders();
// e.g. ['content-type' => ['application/json'], 'x-ratelimit-remaining' => ['42']]
```
Each header maps to a list of values rather than a single string, since some servers do send the same header more
than once (looking at you, `Set-Cookie`). This returns `null` until a request has actually been made on that
instance.

If the request fails, you don't need a separate call to see what the failing response looked like: the headers are
already on the exception.
```php
try {
$client->get('/endpoint');
} catch (\TheNextInvoice\NoREST\Exceptions\RequestFailedException $e) {
$headers = $e->getResponseHeaders();
}
```
This is empty when the request failed before a response was even received, such as a due to a connection error.
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"license": "LGPL-2.1-only",
"description": "NOva REST for the wicked",
"type": "library",
"version": "1.2.0",
"version": "2.0.0",
"authors": [
{
"name": "Jur van den Berg",
Expand All @@ -24,10 +24,12 @@
}
},
"require": {
"php": ">= 7.3",
"php": ">= 8.1",
"ext-curl": "*",
"ext-json": "*"
},
"require-dev": {
"phpstan/phpstan": "^2.2",
"squizlabs/php_codesniffer": "^4.0"
}
}
161 changes: 155 additions & 6 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<ruleset name="PSR12">
<description>TNI Style: opinionated PSR12</description>
<arg name="tab-width" value="4"/>
<!-- Include the whole PSR-12 standard -->
<rule ref="PSR12"/>

<!-- Use spaces instead of tabs to indent files -->
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>

<!-- Require strict_types declaration in all files -->
<rule ref="Generic.PHP.RequireStrictTypes">
<type>warning</type>
</rule>

<!-- Opinionated: use short array syntax [] instead of array() -->
<rule ref="Generic.Arrays.DisallowLongArraySyntax.Found">
<type>warning</type>
</rule>
</ruleset>
4 changes: 4 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
parameters:
level: 9
paths:
- src/
Loading
Loading