Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[2.x] Adds strict types using Phpstan #44

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 23 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
5 changes: 1 addition & 4 deletions .github/workflows/cs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Install CS fixer
run: composer global require friendsofphp/php-cs-fixer

- name: Run CS fixer
run: ~/.composer/vendor/bin/php-cs-fixer fix
run: composer lint

- name: Commit changes
uses: EndBug/add-and-commit@v7
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php: ['7.4', '8.0']
php: ['8.0']

name: PHP ${{ matrix.php }}

Expand All @@ -32,5 +32,5 @@ jobs:
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-suggest

- name: Run Pest
run: php vendor/bin/pest
- name: Run tests
run: composer test:ci
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
vendor/
composer.lock
.php-cs-fixer.cache
.phpunit.result.cache
.phpunit.result.cache
35 changes: 26 additions & 9 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
<?php

declare(strict_types=1);

$finder = PhpCsFixer\Finder::create()
->in(__DIR__ . DIRECTORY_SEPARATOR . 'tests')
->in(__DIR__ . DIRECTORY_SEPARATOR . 'src')
->in('src')
->in('tests')
->append(['.php-cs-fixer.dist.php']);

$rules = [
'@Symfony' => true,
'declare_strict_types' => true,
'phpdoc_no_empty_return' => false,
'array_syntax' => ['syntax' => 'short'],
'yoda_style' => false,
'concat_space' => ['spacing' => 'one'],
'not_operator_with_space' => false,
'php_unit_method_casing' => ['case' => 'snake_case'],
'increment_style' => ['style' => 'post'],
'phpdoc_no_alias_tag' => false,
'phpdoc_align' => [
'align' => 'vertical',
'tags' => [
'param',
'property',
'property-read',
'property-write',
'return',
'throws',
'type',
'var',
'method',
],
],
'final_class' => true,
'global_namespace_import' => [
'import_classes' => true,
],
];

$rules['increment_style'] = ['style' => 'post'];

return (new PhpCsFixer\Config())
->setUsingCache(true)
->setRules($rules)
->setFinder($finder);
return (new PhpCsFixer\Config())->setFinder($finder)->setRules($rules);
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ specific, so you can pass the `fake` method an array of endpoints as keys
and response objects as values:

```php
Soap::fake(['http://endpoint.com' => Response(['foo' => 'bar'])]);
Soap::fake(['http://endpoint.com' => Soap::response(['foo' => 'bar'])]);
```

In the above example, any SOAP request made to `http://endpoint.com` will
Expand All @@ -339,13 +339,13 @@ be returned instead.
What if you want to specify the SOAP action too? Easy! Just add `:{ActionName}` after your endpoint, like so:

```php
Soap::fake(['http://endpoint.com:Details' => Response(['foo' => 'bar'])]);
Soap::fake(['http://endpoint.com:Details' => Soap::response(['foo' => 'bar'])]);
```
Now, only SOAP requests to the `Details` actions will be mocked.

You can also specify multiple actions with the `|` operator:
```php
Soap::fake(['http://endpoint.com:Details|Information|Overview' => Response(['foo' => 'bar'])]);
Soap::fake(['http://endpoint.com:Details|Information|Overview' => Soap::response(['foo' => 'bar'])]);
```
Now, only SOAP requests to the `Details`, `Information` and `Overview` actions will be mocked.

Expand Down
26 changes: 23 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,17 @@
}
},
"require": {
"php": "^7.4|^8.0",
"php": "^8.0",
"illuminate/support": "^8.16"
},
"require-dev": {
"phpunit/phpunit": "^9.4",
"orchestra/testbench": "^6.5",
"spatie/ray": "^1.17",
"ext-soap": "*",
"pestphp/pest": "^1.0"
"pestphp/pest": "^1.0",
"phpstan/phpstan": "^0.12.99",
"friendsofphp/php-cs-fixer": "^3.2"
},
"prefer-stable": true,
"authors": [
Expand All @@ -35,10 +37,28 @@
"email": "[email protected]"
}
],
"scripts": {
"lint": "./vendor/bin/php-cs-fixer fix --allow-risky=yes",
"test:lint": "./vendor/bin/php-cs-fixer fix -v --dry-run --allow-risky=yes",
"test:unit": "./vendor/bin/pest --exclude=integration",
"test:integration": "./vendor/bin/pest --group=integration",
"test:types": "./vendor/bin/phpstan analyse",
"test:ci": [
"@test:lint",
"@test:unit",
"@test:integration",
"@test:types"
],
"test": [
"@lint",
"@test:ci"
]
},
"extra": {
"laravel": {
"providers": [
"RicorocksDigitalAgency\\Soap\\Providers\\SoapServiceProvider"
"RicorocksDigitalAgency\\Soap\\Providers\\SoapServiceProvider",
"RicorocksDigitalAgency\\Soap\\Providers\\SoapRayServiceProvider"
],
"aliases": [
"RicorocksDigitalAgency\\Soap\\Facades\\Soap"
Expand Down
5 changes: 5 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
parameters:
paths:
- src

level: max
15 changes: 15 additions & 0 deletions src/Contracts/Builder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace RicorocksDigitalAgency\Soap\Contracts;

interface Builder
{
/**
* @param array<array<mixed>|Soapable>|Soapable $parameters
*
* @return array<string, mixed>
*/
public function handle(array|Soapable $parameters): array;
}
44 changes: 44 additions & 0 deletions src/Contracts/PhpSoap/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace RicorocksDigitalAgency\Soap\Contracts\PhpSoap;

use SoapHeader;

interface Client
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure it is possible to create a client using phpro/soap-client that implements this interface.

{
public function setEndpoint(string $endpoint): static;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if the setters in this interface are a good idea though. See further down this code review - it allows for some strange structures.


/**
* @param array<string, mixed> $options
*/
public function setOptions(array $options): static;

/**
* Set the given headers on the request.
*
* @param array<int, SoapHeader> $headers
*/
public function setHeaders(array $headers): static;

/**
* Make a request and return its response.
*/
public function call(string $method, mixed $body): mixed;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe better to change mixed $body to array $arguments?
That way it is compatible with how ext-soap works internally and with php-soap/engine:

It allows for multi-params requests (see #45)


/**
* Get an array of supported SOAP functions.
*
* @return array<int, string>
*/
public function getFunctions(): array;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our php-soap engine returns more information about the functions than a regular string.
https://github.com/php-soap/engine/blob/main/src/Metadata/Metadata.php

We could parse them back to a regular int, string - but it means you will loose some information.
Alternatively we could add a generic for determing what kind of information this function will return?


public function __getLastRequest(): ?string;

public function __getLastResponse(): ?string;

public function __getLastRequestHeaders(): ?string;

public function __getLastResponseHeaders(): ?string;
}
79 changes: 79 additions & 0 deletions src/Contracts/Request.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace RicorocksDigitalAgency\Soap\Contracts;

use RicorocksDigitalAgency\Soap\Response\Response;
use RicorocksDigitalAgency\Soap\Support\Header;

interface Request
{
public function to(string $endpoint): self;

/**
* @param array<mixed> $parameters
*
* @return mixed
*/
public function __call(string $name, array $parameters);

/**
* @return array<int, string>
*/
public function functions(): array;

/**
* @param array<string, mixed>|Soapable $body
*/
public function call(string $method, array|Soapable $body = []): Response;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as for the Client contract : it is better to make it an array of parameters imo or a variadic


/**
* @param callable(Request): mixed ...$closures
*/
public function beforeRequesting(callable ...$closures): self;

/**
* @param callable(Request, Response): mixed ...$closures
*/
public function afterRequesting(callable ...$closures): self;

/**
* @param callable(Request): Response|Response|null $response
*/
public function fakeUsing(callable|Response|null $response): self;

public function getEndpoint(): string;

public function getMethod(): string;

/**
* @return array<string, mixed>|Soapable
*/
public function getBody(): array|Soapable;

/**
* @return array<string, mixed>
*/
public function getOptions(): array;

public function set(string $key, mixed $value): self;

public function trace(bool $shouldTrace = true): self;

/**
* @param array<string, mixed> $options
*/
public function withOptions(array $options): self;

public function withBasicAuth(string $login, string $password): self;

public function withDigestAuth(string $login, string $password): self;

public function withHeaders(Header ...$headers): self;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This request is a bit shaped around the existing soap client.
Did you already think about how more advanced options could be added to a request? For example http middleware etc, ... - which are not possible with the regular soap client?

/**
* @return array<int, Header>
*/
public function getHeaders(): array;
}
4 changes: 3 additions & 1 deletion src/Contracts/Soapable.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<?php

declare(strict_types=1);

namespace RicorocksDigitalAgency\Soap\Contracts;

interface Soapable
{
public function toSoap();
public function toSoap(): mixed;
}
19 changes: 12 additions & 7 deletions src/Facades/Soap.php
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
<?php

declare(strict_types=1);

namespace RicorocksDigitalAgency\Soap\Facades;

use Illuminate\Support\Facades\Facade;
use RicorocksDigitalAgency\Soap\Header;
use RicorocksDigitalAgency\Soap\HeaderSet;
use RicorocksDigitalAgency\Soap\Inclusion;
use RicorocksDigitalAgency\Soap\OptionSet;
use RicorocksDigitalAgency\Soap\Contracts\Request;
use RicorocksDigitalAgency\Soap\Parameters\Node;
use RicorocksDigitalAgency\Soap\Request\Request;
use RicorocksDigitalAgency\Soap\Response\Response;
use RicorocksDigitalAgency\Soap\Support\Header;
use RicorocksDigitalAgency\Soap\Support\Scopes\HeaderSet;
use RicorocksDigitalAgency\Soap\Support\Scopes\Inclusion;
use RicorocksDigitalAgency\Soap\Support\Scopes\OptionSet;
use SoapVar;

/**
* Class Soap.
*
* @method static Request to(string $endpoint)
* @method static Header header(?string $name = null, ?string $namespace = null, $data = null, bool $mustUnderstand = false, ?string $actor = null)
* @method static Header header(string $name = '', string $namespace = '', array|SoapVar $data = null, bool $mustUnderstand = false, string|int $actor = null)
* @method static HeaderSet headers(Header ...$headers)
* @method static Node node(array $attributes = [])
* @method static Inclusion include(array $parameters)
Expand All @@ -26,8 +30,9 @@
* @method static void assertNothingSent()
* @method static void assertSent(callable $callback)
* @method static void assertNotSent(callable $callback)
* @method static Response response(array|stdClass $response) Create a new SOAP response to be returned when faking requests.
*/
class Soap extends Facade
final class Soap extends Facade
{
protected static function getFacadeAccessor()
{
Expand Down
20 changes: 0 additions & 20 deletions src/HeaderSet.php

This file was deleted.

Loading