Full Code of CodeDredd/laravel-soap for AI

master bfdc0d99b6aa cached
60 files
143.1 KB
37.2k tokens
215 symbols
1 requests
Download .txt
Repository: CodeDredd/laravel-soap
Branch: master
Commit: bfdc0d99b6aa
Files: 60
Total size: 143.1 KB

Directory structure:
gitextract_95jpnp3a/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   ├── pr-labeler.yml
│   ├── release-drafter.yml
│   ├── stale.yml
│   └── workflows/
│       ├── dependabot-auto-merge.yml
│       ├── mkdocs.yml
│       ├── php-cs-fixer.yml
│       ├── phpstan.yml
│       ├── pr-labeler.yml
│       ├── releaseDrafter.yml
│       └── tests.yml
├── .gitignore
├── .mailmap
├── .php_cs.dist.php
├── .styleci.yml
├── LICENSE
├── README.md
├── composer.json
├── config/
│   └── soap.php
├── mkdocs.yml
├── src/
│   ├── Client/
│   │   ├── Events/
│   │   │   ├── ConnectionFailed.php
│   │   │   ├── RequestSending.php
│   │   │   └── ResponseReceived.php
│   │   ├── Request.php
│   │   ├── Response.php
│   │   └── ResponseSequence.php
│   ├── Driver/
│   │   └── ExtSoap/
│   │       └── ExtSoapEngineFactory.php
│   ├── Exceptions/
│   │   ├── NotFoundConfigurationException.php
│   │   ├── RequestException.php
│   │   └── SoapException.php
│   ├── Facades/
│   │   └── Soap.php
│   ├── Faker/
│   │   ├── EngineFaker.php
│   │   └── fake.wsdl
│   ├── Middleware/
│   │   ├── CisDhlMiddleware.php
│   │   └── WsseMiddleware.php
│   ├── Ray/
│   │   ├── LaravelRay.php
│   │   └── SoapClientWatcher.php
│   ├── SoapClient.php
│   ├── SoapFactory.php
│   ├── SoapServiceProvider.php
│   ├── SoapTesting.php
│   └── Xml/
│       └── XMLSerializer.php
└── tests/
    ├── Fixtures/
    │   ├── CustomSoapClient.php
    │   ├── Responses/
    │   │   └── SoapFault.xml
    │   └── Wsdl/
    │       └── weather.wsdl
    ├── TestCase.php
    └── Unit/
        ├── Client/
        │   └── ResponseTest.php
        ├── Commands/
        │   ├── MakeClientCommandTest.php
        │   └── MakeValidationCommandTest.php
        ├── Middleware/
        │   └── CisDhlMiddlewareTest.php
        ├── SoapClientTest.php
        └── Xml/
            └── XmlSerializerTest.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
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

================================================
FILE: .gitattributes
================================================
* text=auto

/.github export-ignore
/tests export-ignore
.editorconfig export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.styleci.yml export-ignore
CHANGELOG-* export-ignore
CODE_OF_CONDUCT.md export-ignore
CONTRIBUTING.md export-ignore
phpunit.xml export-ignore


================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Code of Conduct

As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.

We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.

Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.

This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
github: [codedredd]
#open_collective: codedredd
#custom:


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---

### Reproduction

If possible, provide a boiled down editable reproduction using a service like JSFiddle, Codepen, CodeSandbox, or a GitHub repository based on this template: https://github.com/piniajs/bug-report. A failing unit test is even better! Otherwise provide as much information as possible to reproduce the problem. You can find examples of different environments at https://github.com/piniajs?q=example&type=source and use them as a bug reproduction.
If no reproduction is provided and the information is not enough to reproduce the problem, we won't be able to give it a look **and the issue will be converted into a question and moved to discussions**.

### Steps to reproduce the behavior

1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

### Expected behavior

A clear and concise description of what you expected to happen.

### Actual behavior

A clear and concise description of what actually happens.

### Additional information

Add any other context about the problem here.


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Ask a question
    url: https://github.com/CodeDredd/laravel-soap/discussions/new?category=q-a
    about: Ask the community for help
  - name: Request a feature
    url: https://github.com/CodeDredd/laravel-soap/discussions/new?category=ideas
    about: Share ideas for new features
  - name: Report a security issue
    url: https://github.com/CodeDredd/laravel-soap/security/policy
    about: Learn how to notify us for sensitive bugs
  - name: Report a bug
    url: https://github.com/CodeDredd/laravel-soap/issues/new
    about: Report a reproducable bug


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature request
assignees: ''

---

### What problem is this solving

A clear and concise description of what the problem is. Ex. when using the function X we cannot do Y.

### Proposed solution

A clear and concise description of what you want to happen with an API proposal when applicable

### Describe alternatives you've considered

A clear and concise description of any alternative solutions or features you've considered.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
Issue:

## What I did

## How to test

- Does this need an update to the documentation?

If your answer is yes to any of these, please make sure to include it in your PR.

<!--

Maintainers: Please tag your pull request with at least one of the following:
`["cleanup", "BREAKING CHANGE", "feature", "bug", "documentation", "maintenance"]`

-->


================================================
FILE: .github/dependabot.yml
================================================
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
    labels:
      - "dependencies"


================================================
FILE: .github/pr-labeler.yml
================================================
feature: ['feature/*', 'feat/*']
fix: fix/*
chore: chore/*

================================================
FILE: .github/release-drafter.yml
================================================
name-template: 'v$NEXT_PATCH_VERSION'
tag-template: 'v$NEXT_PATCH_VERSION'
categories:
  - title: '🚀 Features'
    labels:
      - 'feature'
      - 'enhancement'
  - title: '🐛 Bug Fixes'
    labels:
      - 'fix'
      - 'bugfix'
      - 'bug'
  - title: '🧰 Maintenance'
    labels:
      - 'chore'
      - 'documentation'
      - 'dependencies'
  - title: ':boom: BREAKING CHANGE'
    label: 'BREAKING CHANGE'
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
template: |
  ## Changes

  $CHANGES


================================================
FILE: .github/stale.yml
================================================
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 21
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 60
# Issues with these labels will never be considered stale
exemptLabels:
  - todo
  - 'in progress'
# Label to use when marking an issue as stale
staleLabel: inactive
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
  Hi everyone! Seems like there hasn't been much going on in this issue lately.
  If there are still questions, comments, or bugs, please feel free to continue
  the discussion. Unfortunately, we don't have time to get to every issue. We
  are always open to contributions so please send us a pull request if you would
  like to help. Inactive issues will be closed after 30 days. Thanks!
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
  Hey there, it's me again! I am going close this issue to help our maintainers
  focus on the current development roadmap instead. If the issue mentioned is
  still a concern, please open a new ticket and mention this old one. Cheers
  and thanks for using Laravel Soap!


================================================
FILE: .github/workflows/dependabot-auto-merge.yml
================================================

name: dependabot-auto-merge
on: pull_request_target

permissions:
  pull-requests: write
  contents: write

jobs:
  dependabot:
    runs-on: ubuntu-latest
    if: ${{ github.actor == 'dependabot[bot]' }}
    steps:

      - name: Dependabot metadata
        id: metadata
        uses: dependabot/fetch-metadata@v1.6.0
        with:
          github-token: "${{ secrets.GITHUB_TOKEN }}"

      - name: Auto-merge Dependabot PRs for semver-minor updates
        if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}}
        run: gh pr merge --auto --merge "$PR_URL"
        env:
          PR_URL: ${{github.event.pull_request.html_url}}
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

      - name: Auto-merge Dependabot PRs for semver-patch updates
        if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
        run: gh pr merge --auto --merge "$PR_URL"
        env:
          PR_URL: ${{github.event.pull_request.html_url}}
          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}


================================================
FILE: .github/workflows/mkdocs.yml
================================================
name: documentation
on:
  push:
    branches:
      - master

jobs:
  build:
    name: Deploy docs
    runs-on: ubuntu-latest
    steps:
      - name: Checkout master
        uses: actions/checkout@v3

      - name: Deploy docs
        uses: mhausenblas/mkdocs-deploy-gh-pages@master
        env:
          PERSONAL_TOKEN: ${{ secrets.PERSONAL_TOKEN }}


================================================
FILE: .github/workflows/php-cs-fixer.yml
================================================
name: Check & fix styling

on: [push]

jobs:
  php-cs-fixer:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3
        with:
          ref: ${{ github.head_ref }}

      - name: Run PHP CS Fixer
        uses: docker://oskarstark/php-cs-fixer-ga
        with:
          args: --config=.php_cs.dist.php --allow-risky=yes

#      - name: Commit changes
#        uses: stefanzweifel/git-auto-commit-action@v4
#        with:
#          commit_message: Fix styling


================================================
FILE: .github/workflows/phpstan.yml
================================================
name: PHPStan

on:
  push:
    paths:
      - '**.php'
      - 'phpstan.neon'

jobs:
  phpstan:
    name: phpstan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          coverage: none

      - name: Install composer dependencies
        uses: ramsey/composer-install@v2

      - name: Run PHPStan
        run: ./vendor/bin/phpstan --error-format=github


================================================
FILE: .github/workflows/pr-labeler.yml
================================================
name: PR Labeler
on:
  pull_request:
    types: [opened]

jobs:
  pr-labeler:
    runs-on: ubuntu-latest
    steps:
      - uses: TimonVS/pr-labeler-action@v4
        with:
          configuration-path: .github/pr-labeler.yml # optional, .github/pr-labeler.yml is the default value
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

================================================
FILE: .github/workflows/releaseDrafter.yml
================================================
name: Release Drafter

on:
  push:
    # branches to consider in the event; optional, defaults to all
    branches:
      - master
      - '1.0'

jobs:
  update_release_draft:
    runs-on: ubuntu-latest
    steps:
      # Drafts your next Release notes as Pull Requests are merged into "master"
      - uses: release-drafter/release-drafter@v5.25.0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .github/workflows/tests.yml
================================================
name: Unit Tests

on: push

env:
  # see https://github.com/composer/composer/issues/9368#issuecomment-718112361
  COMPOSER_ROOT_VERSION: "dev-master"

jobs:
  provide_php_versions_json:
    runs-on: ubuntu-latest

    steps:
      # git clone + use PHP + composer install
      -   uses: actions/checkout@v3
      -   uses: shivammathur/setup-php@v2
          with:
            php-version: 8.2
            tools: composer:v2

      -   run: composer install --no-progress --ansi

      -
        # to see the output
        run: vendor/bin/easy-ci php-versions-json

      # here we create the json, we need the "id:" so we can use it in "outputs" bellow

      -
        id: output_data
        run: echo "::set-output name=matrix::$(vendor/bin/easy-ci php-versions-json)"

    # here, we save the result of this 1st phase to the "outputs"
    outputs:
      matrix: ${{ steps.output_data.outputs.matrix }}

  unit_tests:
    needs: provide_php_versions_json

    runs-on: ubuntu-latest

    strategy:
      fail-fast: false
      matrix:
        php: ${{ fromJson(needs.provide_php_versions_json.outputs.matrix) }}

    name: PHP ${{ matrix.php }} tests

    steps:
      -   uses: actions/checkout@v3
      # required for "git tag" presence for changelog-linker git tags resolver; default is 1
      # https://github.com/actions/checkout#fetch-all-tags
      -   run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*

      # see https://github.com/shivammathur/setup-php
      -   uses: shivammathur/setup-php@v2
          with:
            php-version: ${{ matrix.php }}
            extensions: mbstring, intl
            ini-values: post_max_size=256M, max_execution_time=180
            coverage: xdebug
            tools: php-cs-fixer, phpunit, composer:v2

      # composer install cache - https://github.com/ramsey/composer-install
      -
        if: "matrix.php == 7.3"
        run: composer update  --no-progress --ansi --prefer-lowest

      -
        if: "matrix.php == 7.4"
        uses: "ramsey/composer-install@v2"

      -
        if: "matrix.php >= 8"
        uses: "ramsey/composer-install@v2"
        with:
          composer-options: "--ignore-platform-req php"

      -
        run: vendor/bin/phpunit


================================================
FILE: .gitignore
================================================
.idea
.env
.php_cs
.php_cs.cache
.phpunit.result.cache
build
composer.lock
coverage
docs
phpunit.xml
phpstan.neon
testbench.yaml
vendor
.php-cs-fixer.cache
ray.php
/docs/v3/node_modules/
/.phpunit.cache


================================================
FILE: .mailmap
================================================
CodeDredd <gregor@codedredd.de> Gregor Becker <g.becker@careerpartner.eu>
CodeDredd <gregor@codedredd.de> Gregor Becker <gregor.becker@getinbyte.com>


================================================
FILE: .php_cs.dist.php
================================================
<?php

$finder = Symfony\Component\Finder\Finder::create()
    ->in([
        __DIR__ . '/src',
        __DIR__ . '/tests',
    ])
    ->name('*.php')
    ->notName('*.blade.php')
    ->ignoreDotFiles(true)
    ->ignoreVCS(true);

return (new PhpCsFixer\Config())
    ->setRules([
        '@PSR12' => true,
        'array_syntax' => ['syntax' => 'short'],
        'ordered_imports' => ['sort_algorithm' => 'alpha'],
        'no_unused_imports' => true,
        'not_operator_with_successor_space' => true,
        'trailing_comma_in_multiline' => true,
        'phpdoc_scalar' => true,
        'unary_operator_spaces' => true,
        'binary_operator_spaces' => true,
        'blank_line_before_statement' => [
            'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
        ],
        'phpdoc_single_line_var_spacing' => true,
        'phpdoc_var_without_name' => true,
        'class_attributes_separation' => [
            'elements' => [
                'method' => 'one',
            ],
        ],
        'method_argument_space' => [
            'on_multiline' => 'ensure_fully_multiline',
            'keep_multiple_spaces_after_comma' => true,
        ],
        'single_trait_insert_per_statement' => true,
    ])
    ->setFinder($finder);


================================================
FILE: .styleci.yml
================================================
preset: laravel


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2014 Michael van de Rijt

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<p align="center">
  <a href="https://codedredd.github.io/laravel-soap/" target="_blank" rel="noopener noreferrer">
    <img style="width: 100%; height: auto" width="100%" src="https://www.codedredd.de/images/laravel-soap/preview.png" alt="Laravel SOAP">
  </a>
</p>

# Laravel SOAP Client

[![Software License](https://img.shields.io/github/license/codedredd/laravel-soap?style=flat-square)](LICENSE.md)
[![Total Downloads](https://img.shields.io/packagist/dt/codedredd/laravel-soap?style=flat-square)](https://packagist.org/packages/codedredd/laravel-soap)
[![test](https://img.shields.io/github/workflow/status/codedredd/laravel-soap/test?label=test&logo=github&style=flat-square)](https://github.com/CodeDredd/laravel-soap/actions?query=workflow%3Atest)
[![styleci](https://github.styleci.io/repos/7548986/shield)](https://github.styleci.io/repos/257192373)
[![version](https://img.shields.io/github/v/release/codedredd/laravel-soap?style=flat-square)](https://github.com/CodeDredd/laravel-soap/releases)
[![documentation](https://img.shields.io/github/workflow/status/codedredd/laravel-soap/documentation?label=docs&logo=read-the-docs&style=flat-square)](https://codedredd.github.io/laravel-soap/)

## Versions
| Laravel SOAP Version | Laravel Support | PHP Version |
|----------------------|-----------------|-------------|
| 1.x                  | 5.6, 6.x, 7.x   | 7.3 - 8.0   |
| 2.x                  | 8.x             | 7.3 - 8.0   |
| 3.x                  | 9.x             | 8.0 - 8.1   |
| 4.x                  | 9.x, 10.x       | 8.1 - 8.2   |

<a name="installation"></a>
## Installation

Execute the following command to get the latest version of the package:

    composer require codedredd/laravel-soap
    
## Documentation
You can find here a detailed [documentation](https://codedredd.github.io/laravel-soap/)

## Help me keep working on this project 💚

- [Become a Sponsor on GitHub](https://github.com/sponsors/codedredd)
- [One-time donation via PayPal](https://paypal.me/dredd1984)

<!--sponsors start-->
<h3 align="center">Platinum Sponsors</h3>
<p align="center">
</p>

<h4 align="center">Gold Sponsors</h4>
<p align="center">
</p>

<h4 align="center">Silver Sponsors</h4>
<p align="center">
</p>

<h4 align="center">Bronze Sponsors</h4>
<p align="center">
</p>

<!--sponsors end-->

---

<a name="contributing"></a>
## Contributing
Please post issues and send PRs.

<a name="licence"></a>
## License
Laravel Soap is open-sourced software licensed under the MIT license.


================================================
FILE: composer.json
================================================
{
    "name": "codedredd/laravel-soap",
    "description": "A SoapClient wrapper integration for Laravel",
    "keywords": [
        "laravel",
        "soap",
        "client",
        "wrapper"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Gregor Becker",
            "email": "gregor@codedredd.de"
        }
    ],
    "require": {
        "php": "~8.1 || ~8.2 || ~8.3 || ~8.4",
        "ext-soap": "*",
        "ext-bcmath": "*",
        "ext-intl": "*",
        "ext-json": "*",
        "ext-dom": "*",
        "ext-simplexml": "*",
        "illuminate/http": "^9.0 || ^10.0 || ^11.0 || ^12.0",
        "illuminate/support": "^9.0 || ^10.0 || ^11.0 || ^12.0",
        "phpro/soap-client": "^2.3.0 || ^3.1.0",
        "php-http/guzzle7-adapter": "^1.0",
        "php-http/discovery": "^1.14",
        "php-http/message": "^1.13",
        "php-http/client-common": "^2.6",
        "robrichards/wse-php": "^2.0",
        "php-soap/psr18-transport": "^1.7",
        "php-soap/psr18-wsse-middleware": "^2.6",
        "veewee/xml": "^2.6 || ^3.0",
        "spatie/laravel-package-tools": "^1.92",
        "illuminate/contracts": "^9.0 || ^10.0 || ^11.0 || ^12.0"
    },
    "require-dev": {
        "symfony/options-resolver": "^6.2.5",
        "phpunit/phpunit": "^10.0 || ^11.0",
        "orchestra/testbench": "^8.0 || ^9.0 || ^10.0",
        "laminas/laminas-code": "^4.8.0",
        "nunomaduro/collision": "^8.1",
        "symplify/easy-ci": "^10.0",
        "spatie/laravel-ray": "^1.32",
        "larastan/larastan": "^3.1"
    },
    "autoload": {
        "psr-4": {
            "CodeDredd\\Soap\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "CodeDredd\\Soap\\Tests\\": "tests/"
        }
    },
    "extra": {
        "laravel": {
            "providers": [
                "CodeDredd\\Soap\\SoapServiceProvider"
            ],
            "aliases": {
                "SOAP": "SoapFacade"
            }
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "config": {
        "allow-plugins": {
            "php-http/discovery": true
        }
    }
}


================================================
FILE: config/soap.php
================================================
<?php

use RobRichards\XMLSecLibs\XMLSecurityKey;

return [
    /*
    |--------------------------------------------------------------------------
    | SOAP Code Generation directory
    |--------------------------------------------------------------------------
    |
    | Define the destination for the code generator under the app directory
    */

    'code' => [
        'path' => app_path('Soap'),
        'namespace' => 'App\\Soap',
    ],

    /*
    |--------------------------------------------------------------------------
    | SOAP Ray Configuration
    |--------------------------------------------------------------------------
    |
    | Define if all requests should go to ray
    */

    'ray' => [
        'send_soap_client_requests' => false,
    ],

    /*
    |--------------------------------------------------------------------------
    | SOAP Call behaviour
    |--------------------------------------------------------------------------
    |
    | Define if the arguments should be wrapped in an array
    */

    'call' => [
        'wrap_arguments_in_array' => true,
    ],

    /*
    |--------------------------------------------------------------------------
    | SOAP Client Configuration
    |--------------------------------------------------------------------------
    |
    | Her you can setup your soap client by configuration so that ou just need
    | a name.
    |
    | example: Soap::buildClient('laravel_soap')
    */

    'clients' => [
        'laravel_soap' => [
            'base_wsdl' => 'test.wsdl',
            'with_wsa' => true,
            'with_basic_auth' => [
                'username' => 'username',
                'password' => 'password',
            ],
            'with_wsse' => [
                'user_token_name' => 'username',
                'user_token_password' => 'password',
                'private_key_file' => 'path/to/privatekey.pem',
                'public_key_file' => 'path/to/publickey.pyb',
                'server_certificate_file' => 'path/to/client-cert.pem',
                'server_certificate_has_subject_key_identifier' => false,
                'user_token_digest' => false,
                'digital_sign_method' => XMLSecurityKey::RSA_SHA1,
                'timestamp' => 3600,
                'sign_all_headers' => false,
            ],
        ],
    ],

];


================================================
FILE: mkdocs.yml
================================================
site_name: Laravel SOAP Docs
site_description: 'Laravel SOAP Documentation'
site_author: 'Gregor Becker'
docs_dir: docs/v2/
repo_name: 'codedredd/laravel-soap'
repo_url: 'https://github.com/codedredd/laravel-soap'

# Copyright
copyright: Copyright &copy; 2020 - 2020 Gregor Becker

nav:
  - 'Get started': 'index.md'
  - Client:
      - Request: 'client/request.md'
      - Response: 'client/response.md'
      - authentication: 'client/authentication.md'
      - configuration: 'client/configuration.md'
  - Commands: 'commands.md'
  - Testing: 'testing.md'
theme:
  name: 'material'
  language: 'en'
  palette:
    primary: 'cyan'
    accent: 'deep orange'
markdown_extensions:
  - admonition
  - attr_list
  - codehilite:
      guess_lang: false
  - toc:
      permalink: true
  - pymdownx.betterem:
      smart_enable: all
  - pymdownx.superfences
  - pymdownx.inlinehilite
  - pymdownx.tabbed
  - pymdownx.emoji:
      emoji_index: !!python/name:materialx.emoji.twemoji
      emoji_generator: !!python/name:materialx.emoji.to_svg
  - pymdownx.highlight:
      extend_pygments_lang:
        - name: php-inline
          lang: php
          options:
            startinline: true


================================================
FILE: src/Client/Events/ConnectionFailed.php
================================================
<?php

namespace CodeDredd\Soap\Client\Events;

use CodeDredd\Soap\Client\Request;

class ConnectionFailed
{
    /**
     * The request instance.
     *
     * @var \CodeDredd\Soap\Client\Request
     */
    public $request;

    /**
     * Create a new event instance.
     *
     * @param  \CodeDredd\Soap\Client\Request  $request
     * @return void
     */
    public function __construct(Request $request)
    {
        $this->request = $request;
    }
}


================================================
FILE: src/Client/Events/RequestSending.php
================================================
<?php

namespace CodeDredd\Soap\Client\Events;

use CodeDredd\Soap\Client\Request;

class RequestSending
{
    /**
     * The request instance.
     */
    public Request $request;

    /**
     * Create a new event instance.
     *
     * @param  Request  $request
     * @return void
     */
    public function __construct(Request $request)
    {
        $this->request = $request;
    }
}


================================================
FILE: src/Client/Events/ResponseReceived.php
================================================
<?php

namespace CodeDredd\Soap\Client\Events;

use CodeDredd\Soap\Client\Request;
use CodeDredd\Soap\Client\Response;

class ResponseReceived
{
    /**
     * The request instance.
     *
     * @var \CodeDredd\Soap\Client\Request
     */
    public Request $request;

    /**
     * The response instance.
     *
     * @var \CodeDredd\Soap\Client\Response
     */
    public Response $response;

    /**
     * Create a new event instance.
     *
     * @param  \CodeDredd\Soap\Client\Request  $request
     * @param  \CodeDredd\Soap\Client\Response  $response
     * @return void
     */
    public function __construct(Request $request, Response $response)
    {
        $this->request = $request;
        $this->response = $response;
    }
}


================================================
FILE: src/Client/Request.php
================================================
<?php

namespace CodeDredd\Soap\Client;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use LogicException;
use Psr\Http\Message\RequestInterface;
use Soap\Psr18Transport\HttpBinding\SoapActionDetector;
use Soap\Xml\Locator\SoapBodyLocator;
use VeeWee\Xml\Dom\Document;
use VeeWee\Xml\Dom\Traverser\Visitor\RemoveNamespaces;
use VeeWee\Xml\Encoding\Exception\EncodingException;

use function VeeWee\Xml\Dom\Configurator\traverse;
use function VeeWee\Xml\Encoding\element_decode;

/**
 * Class Request.
 */
class Request
{
    use Macroable;

    /**
     * The underlying PSR request.
     */
    protected RequestInterface $request;

    /**
     * The decoded payload for the request.
     */
    protected array $data;

    /**
     * Create a new request instance.
     *
     * @param  RequestInterface  $request
     * @return void
     */
    public function __construct($request)
    {
        $this->request = $request;
    }

    /**
     * Get the soap action for soap 1.1 and 1.2.
     */
    public function action(): string
    {
        return Str::remove('"', SoapActionDetector::detectFromRequest($this->request));
    }

    public function getRequest(): RequestInterface
    {
        return $this->request;
    }

    /**
     * Get the URL of the request.
     */
    public function url(): string
    {
        return (string) $this->request->getUri();
    }

    /**
     * Determine if the request has a given header.
     *
     * @param  string  $key
     * @param  mixed  $value
     * @return bool
     */
    public function hasHeader($key, $value = null)
    {
        if (is_null($value)) {
            return ! empty($this->request->getHeaders()[$key]);
        }

        $headers = $this->headers();

        if (! Arr::has($headers, $key)) {
            return false;
        }

        $value = is_array($value) ? $value : [$value];

        return empty(array_diff($value, $headers[$key]));
    }

    /**
     * Determine if the request has the given headers.
     *
     * @param  array|string  $headers
     * @return bool
     */
    public function hasHeaders($headers)
    {
        if (is_string($headers)) {
            $headers = [$headers => null];
        }

        foreach ($headers as $key => $value) {
            if (! $this->hasHeader($key, $value)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Get the values for the header with the given name.
     *
     * @param  string  $key
     * @return array
     */
    public function header($key)
    {
        return Arr::get($this->headers(), $key, []);
    }

    /**
     * Get the request headers.
     *
     * @return array
     */
    public function headers()
    {
        return $this->request->getHeaders();
    }

    /**
     * Get the body of the request.
     *
     * @return string
     */
    public function body()
    {
        return (string) $this->request->getBody();
    }

    /**
     * Return complete xml request body.
     */
    public function xmlContent(): string
    {
        return $this->request->getBody()->getContents();
    }

    /**
     * Return request arguments.
     *
     * @throws EncodingException
     */
    public function arguments(): array
    {
        $doc = Document::fromXmlString($this->body());
        $wrappedArguments = config()->get('soap.call.wrap_arguments_in_array', true);
        $method = $doc->locate(new SoapBodyLocator());

        if ($wrappedArguments) {
            $method = $method?->firstElementChild;
        }

        return Arr::wrap(Arr::get(element_decode($method, traverse(new RemoveNamespaces())), $wrappedArguments ? 'node' : 'Body', []));
    }

    /**
     * Set the decoded data on the request.
     *
     * @param  array  $data
     * @return $this
     */
    public function withData(array $data)
    {
        $this->data = $data;

        return $this;
    }

    /**
     * Get the underlying PSR compliant request instance.
     *
     * @return \Psr\Http\Message\RequestInterface
     */
    public function toPsrRequest()
    {
        return $this->request;
    }

    /**
     * Determine if the given offset exists.
     *
     * @param  string  $offset
     * @return bool
     *
     * @throws EncodingException
     */
    public function offsetExists(string $offset): bool
    {
        return isset($this->arguments()[$offset]);
    }

    /**
     * Get the value for a given offset.
     *
     * @param  string  $offset
     * @return mixed
     *
     * @throws EncodingException
     */
    public function offsetGet($offset): mixed
    {
        return $this->arguments()[$offset];
    }

    /**
     * Set the value at the given offset.
     *
     * @param  string  $offset
     * @param  mixed  $value
     * @return void
     *
     * @throws LogicException
     */
    public function offsetSet($offset, $value): void
    {
        throw new LogicException('Request data may not be mutated using array access.');
    }

    /**
     * Unset the value at the given offset.
     *
     * @param  string  $offset
     * @return void
     *
     * @throws LogicException
     */
    public function offsetUnset($offset): void
    {
        throw new LogicException('Request data may not be mutated using array access.');
    }
}


================================================
FILE: src/Client/Response.php
================================================
<?php

namespace CodeDredd\Soap\Client;

use ArrayAccess;
use CodeDredd\Soap\Exceptions\RequestException;
use GuzzleHttp\Psr7\Response as Psr7Response;
use GuzzleHttp\TransferStats;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;
use LogicException;
use Phpro\SoapClient\Type\ResultInterface;
use Psr\Http\Message\ResponseInterface;
use VeeWee\Xml\Dom\Document;

use function Psl\Type\string;

/**
 * Class Response.
 */
class Response implements ResultInterface, ArrayAccess
{
    use Macroable {
        __call as macroCall;
    }
    protected ?TransferStats $transferStats = null;

    protected array $cookies = [];

    /**
     * The underlying PSR response.
     */
    protected ResponseInterface $response;

    /**
     * The decoded JSON response.
     *
     * @var array
     */
    protected $decoded;

    /**
     * Create a new response instance.
     */
    public function __construct(ResponseInterface $response)
    {
        $this->response = $response;
    }

    public function setCookies(array $cookies): void
    {
        $this->cookies = $cookies;
    }

    public function setTransferStats(?TransferStats $transferStats): void
    {
        $this->transferStats = $transferStats;
    }

    public static function fromSoapResponse(mixed $result, int $status = 200): Response
    {
        return new self(new Psr7Response($status, [], json_encode($result)));
    }

    public static function fromSoapFault(\SoapFault $soapFault): Response
    {
        return new self(new Psr7Response(400, [], $soapFault->getMessage()));
    }

    /**
     * Get the underlying PSR response for the response.
     */
    public function toPsrResponse(): ResponseInterface
    {
        return $this->response;
    }

    /**
     * Get the JSON decoded body of the response as an object.
     *
     * @return object
     */
    public function object(): object
    {
        return json_decode($this->body(), false);
    }

    /**
     * Get the JSON decoded body of the response as a collection.
     *
     * @param  string|null  $key
     * @return \Illuminate\Support\Collection
     */
    public function collect(string $key = null): Collection
    {
        return Collection::make($this->json($key));
    }

    /**
     * Get the body of the response.
     *
     * @param  bool  $transformXml
     * @param  bool  $sanitizeXmlFaultMessage
     * @return string
     */
    public function body(bool $transformXml = true, bool $sanitizeXmlFaultMessage = true): string
    {
        $body = (string) $this->response->getBody();
        if ($transformXml && Str::contains($body, '<?xml')) {
            $message = Document::fromXmlString($body)
                    ->xpath()
                    ->evaluate('string(.//faultstring)', string())
                ?? 'No Fault Message found';

            return trim($sanitizeXmlFaultMessage ? Str::after($message, 'Exception:') : $message);
        }

        return $body;
    }

    /**
     * Determine if the request was successful.
     *
     * @return bool
     */
    public function successful(): bool
    {
        return $this->status() >= 200 && $this->status() < 300;
    }

    /**
     * Get the status code of the response.
     *
     * @return int
     */
    public function status(): int
    {
        return (int) $this->response->getStatusCode();
    }

    /**
     * Determine if the response code was "OK".
     */
    public function ok(): bool
    {
        return $this->status() === 200;
    }

    /**
     * Determine if the response indicates a client or server error occurred.
     */
    public function failed(): bool
    {
        return $this->serverError() || $this->clientError();
    }

    /**
     * Determine if the response was a redirect.
     */
    public function redirect(): bool
    {
        return $this->status() >= 300 && $this->status() < 400;
    }

    /**
     * Throw an exception if a server or client error occurred.
     *
     * @return $this
     *
     * @throws \CodeDredd\Soap\Exceptions\RequestException
     */
    public function throw()
    {
        $callback = func_get_args()[0] ?? null;

        if ($this->failed()) {
            throw tap(new RequestException($this), function ($exception) use ($callback) {
                if ($callback && is_callable($callback)) {
                    $callback($this, $exception);
                }
            });
        }

        return $this;
    }

    /**
     * Throw an exception if a server or client error occurred and the given condition evaluates to true.
     *
     * @param  bool  $condition
     * @return $this
     *
     * @throws \CodeDredd\Soap\Exceptions\RequestException
     */
    public function throwIf(bool $condition): Response|static
    {
        return $condition ? $this->throw() : $this;
    }

    /**
     * Determine if the response indicates a server error occurred.
     */
    public function serverError(): bool
    {
        return $this->status() >= 500;
    }

    /**
     * Determine if the response indicates a client error occurred.
     */
    public function clientError(): bool
    {
        return $this->status() >= 400 && $this->status() < 500;
    }

    /**
     * Execute the given callback if there was a server or client error.
     *
     * @param  \Closure|callable  $callback
     * @return \CodeDredd\Soap\Client\Response
     */
    public function onError(callable $callback)
    {
        if ($this->failed()) {
            $callback($this);
        }

        return $this;
    }

    /**
     * Get the handler stats of the response.
     *
     * @return array
     */
    public function handlerStats()
    {
        return $this->transferStats?->getHandlerStats() ?? [];
    }

    /**
     * Get the JSON decoded body of the response as an array.
     */
    public function json($key = null, $default = null): ?array
    {
        if (! $this->decoded) {
            $this->decoded = json_decode($this->body(), true);
        }

        if (is_null($key)) {
            return $this->decoded;
        }

        return data_get($this->decoded, $key, $default);
    }

    /**
     * Get a header from the response.
     */
    public function header(string $header): string
    {
        return $this->response->getHeaderLine($header);
    }

    /**
     * Get the headers from the response.
     */
    public function headers(): array
    {
        return $this->response->getHeaders();
    }

    /**
     * Determine if the given offset exists.
     *
     * @param  string  $offset
     * @return bool
     */
    public function offsetExists($offset): bool
    {
        return isset($this->json()[$offset]);
    }

    /**
     * Get the value for a given offset.
     *
     * @param  string  $offset
     * @return mixed
     */
    public function offsetGet($offset): mixed
    {
        return $this->json()[$offset];
    }

    /**
     * Set the value at the given offset.
     *
     * @param  string  $offset
     * @param  mixed  $value
     * @return void
     *
     * @throws \LogicException
     */
    public function offsetSet($offset, $value): void
    {
        throw new LogicException('Response data may not be mutated using array access.');
    }

    /**
     * Unset the value at the given offset.
     *
     * @param  string  $offset
     * @return void
     *
     * @throws \LogicException
     */
    public function offsetUnset($offset): void
    {
        throw new LogicException('Response data may not be mutated using array access.');
    }

    /**
     * Get the body of the response.
     *
     * @return string
     */
    public function __toString()
    {
        return $this->body();
    }

    /**
     * Dynamically proxy other methods to the underlying response.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return static::hasMacro($method)
            ? $this->macroCall($method, $parameters)
            : $this->response->{$method}(...$parameters);
    }
}


================================================
FILE: src/Client/ResponseSequence.php
================================================
<?php

namespace CodeDredd\Soap\Client;

use CodeDredd\Soap\SoapFactory;
use OutOfBoundsException;

class ResponseSequence
{
    /**
     * The responses in the sequence.
     *
     * @var array
     */
    protected $responses;

    /**
     * Indicates that invoking this sequence when it is empty should throw an exception.
     *
     * @var bool
     */
    protected $failWhenEmpty = true;

    /**
     * The response that should be returned when the sequence is empty.
     *
     * @var ?\GuzzleHttp\Promise\PromiseInterface
     */
    protected ?\GuzzleHttp\Promise\PromiseInterface $emptyResponse = null;

    /**
     * Create a new response sequence.
     *
     * @param  array  $responses
     * @return void
     */
    public function __construct(array $responses)
    {
        $this->responses = $responses;
    }

    /**
     * Push a response to the sequence.
     *
     * @param  string|array  $body
     * @param  int  $status
     * @param  array  $headers
     * @return $this
     */
    public function push($body = '', int $status = 200, array $headers = [])
    {
        return $this->pushResponse(
            SoapFactory::response($body, $status, $headers)
        );
    }

    /**
     * Push a response to the sequence.
     *
     * @param  mixed  $response
     * @return $this
     */
    public function pushResponse($response)
    {
        $this->responses[] = $response;

        return $this;
    }

    /**
     * Push a response with the given status code to the sequence.
     *
     * @param  int  $status
     * @param  array  $headers
     * @return $this
     */
    public function pushStatus(int $status, array $headers = [])
    {
        return $this->pushResponse(
            SoapFactory::response('', $status, $headers)
        );
    }

    /**
     * Push response with the contents of a file as the body to the sequence.
     *
     * @param  string  $filePath
     * @param  int  $status
     * @param  array  $headers
     * @return $this
     */
    public function pushFile(string $filePath, int $status = 200, array $headers = [])
    {
        $string = file_get_contents($filePath);

        return $this->pushResponse(
            SoapFactory::response($string, $status, $headers)
        );
    }

    /**
     * Make the sequence return a default response when it is empty.
     *
     * @return $this
     */
    public function dontFailWhenEmpty()
    {
        return $this->whenEmpty(SoapFactory::response());
    }

    /**
     * Make the sequence return a default response when it is empty.
     *
     * @param  \GuzzleHttp\Promise\PromiseInterface|\Closure  $response
     * @return $this
     */
    public function whenEmpty($response)
    {
        $this->failWhenEmpty = false;
        $this->emptyResponse = $response;

        return $this;
    }

    /**
     * Indicate that this sequence has depleted all of its responses.
     *
     * @return bool
     */
    public function isEmpty()
    {
        return count($this->responses) === 0;
    }

    /**
     * Get the next response in the sequence.
     *
     * @return mixed
     */
    public function __invoke()
    {
        if ($this->failWhenEmpty && count($this->responses) === 0) {
            throw new OutOfBoundsException('A request was made, but the response sequence is empty.');
        }

        if (! $this->failWhenEmpty && count($this->responses) === 0) {
            return value($this->emptyResponse ?? SoapFactory::response());
        }

        return array_shift($this->responses);
    }
}


================================================
FILE: src/Driver/ExtSoap/ExtSoapEngineFactory.php
================================================
<?php

declare(strict_types=1);

namespace CodeDredd\Soap\Driver\ExtSoap;

use CodeDredd\Soap\Faker\EngineFaker;
use Soap\Engine\SimpleEngine;
use Soap\Engine\Transport;
use Soap\ExtSoapEngine\ExtSoapDriver;
use Soap\ExtSoapEngine\ExtSoapOptions;

class ExtSoapEngineFactory
{
    public static function fromOptionsWithHandler(
        ExtSoapOptions $options,
        Transport $transport,
        $withMocking = false
    ): EngineFaker|SimpleEngine {
        $driver = ExtSoapDriver::createFromOptions($options);
        if ($withMocking) {
        }

        return $withMocking ? new EngineFaker($driver, $transport, $options) : new SimpleEngine($driver, $transport);
    }
}


================================================
FILE: src/Exceptions/NotFoundConfigurationException.php
================================================
<?php

namespace CodeDredd\Soap\Exceptions;

use RuntimeException;

class NotFoundConfigurationException extends RuntimeException
{
    /**
     * @param  \Throwable  $throwable
     * @return NotFoundConfigurationException
     */
    public static function fromThrowable(\Throwable $throwable): self
    {
        return new self($throwable->getMessage(), (int) $throwable->getCode(), $throwable);
    }
}


================================================
FILE: src/Exceptions/RequestException.php
================================================
<?php

namespace CodeDredd\Soap\Exceptions;

use CodeDredd\Soap\Client\Response;
use Exception;

class RequestException extends Exception
{
    /**
     * The response instance.
     *
     * @var \CodeDredd\Soap\Client\Response
     */
    public $response;

    /**
     * Create a new exception instance.
     *
     * @param  \CodeDredd\Soap\Client\Response  $response
     * @return void
     */
    public function __construct(Response $response)
    {
        parent::__construct(
            "Soap request error with status code {$response->status()}:\n {$response->body()}",
            $response->status()
        );

        $this->response = $response;
    }
}


================================================
FILE: src/Exceptions/SoapException.php
================================================
<?php

namespace CodeDredd\Soap\Exceptions;

/**
 * Class SoapException.
 */
class SoapException extends \RuntimeException
{
    /**
     * @param  \Throwable  $throwable
     * @return SoapException
     */
    public static function fromThrowable(\Throwable $throwable): self
    {
        return new self($throwable->getMessage(), (int) $throwable->getCode(), $throwable);
    }
}


================================================
FILE: src/Facades/Soap.php
================================================
<?php

namespace CodeDredd\Soap\Facades;

use CodeDredd\Soap\SoapFactory;
use Illuminate\Support\Facades\Facade;

/**
 * Class Soap.
 *
 * @method static \CodeDredd\Soap\Handler\HttPlugHandle getHandler()
 * @method static \CodeDredd\Soap\SoapClient baseWsdl(string $wsdl)
 * @method static \CodeDredd\Soap\SoapClient stub(callable $callback)
 * @method static \CodeDredd\Soap\SoapClient buildClient(string $setup = '')
 * @method static \CodeDredd\Soap\SoapClient byConfig(string $setup = '')
 * @method static \CodeDredd\Soap\SoapClient withOptions(array $options)
 * @method static \CodeDredd\Soap\SoapClient withHeaders(array $options)
 * @method static \CodeDredd\Soap\SoapClient withGuzzleClientOptions(array $options)
 * @method static \CodeDredd\Soap\SoapClient withWsse(array $config)
 * @method static \CodeDredd\Soap\SoapClient withWsa()
 * @method static \CodeDredd\Soap\SoapClient withRemoveEmptyNodes()
 * @method static \CodeDredd\Soap\SoapClient withBasicAuth(string $username, string $password)
 * @method static \CodeDredd\Soap\SoapClient withCisDHLAuth($user, ?string $signature = null)
 * @method \CodeDredd\Soap\Client\Response call(string $method, array $arguments = [])
 * @method static \GuzzleHttp\Promise\PromiseInterface response($body = null, $status = 200, array $headers = [])
 * @method static \CodeDredd\Soap\Client\ResponseSequence sequence(array $responses = [])
 * @method static \CodeDredd\Soap\Client\ResponseSequence fakeSequence(string $urlPattern = '*')
 * @method static \CodeDredd\Soap\SoapFactory fake($callback = null)
 * @method static assertSent(callable $callback)
 * @method static assertNotSent(callable $callback)
 * @method static assertActionCalled(string $action)
 * @method static assertNothingSent()
 * @method static assertSequencesAreEmpty()
 * @method static assertSentCount($count)
 */
class Soap extends Facade
{
    /**
     * {@inheritdoc}
     */
    protected static function getFacadeAccessor()
    {
        return SoapFactory::class;
    }
}


================================================
FILE: src/Faker/EngineFaker.php
================================================
<?php

declare(strict_types=1);

namespace CodeDredd\Soap\Faker;

use CodeDredd\Soap\Xml\XMLSerializer;
use Soap\Engine\Driver;
use Soap\Engine\Engine;
use Soap\Engine\HttpBinding\SoapRequest;
use Soap\Engine\Metadata\Metadata;
use Soap\Engine\Transport;
use Soap\ExtSoapEngine\ExtSoapOptions;

/**
 * Class EngineFaker.
 */
class EngineFaker implements Engine
{
    private Driver $driver;
    private Transport $transport;
    private ExtSoapOptions $options;

    public function __construct(
        Driver $driver,
        Transport $transport,
        ExtSoapOptions $options
    ) {
        $this->driver = $driver;
        $this->transport = $transport;
        $this->options = $options;
    }

    public function request(string $method, array $arguments)
    {
        $request = new SoapRequest(XMLSerializer::arrayToSoapXml($arguments), $this->options->getWsdl(), $method, $this->options->getOptions()['soap_version'] ?? SOAP_1_1);
        $response = $this->transport->request($request);

        return json_decode($response->getPayload());
    }

    public function getMetadata(): Metadata
    {
        return $this->driver->getMetadata();
    }
}


================================================
FILE: src/Faker/fake.wsdl
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
Cached version of: http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl
-->
<wsdl:definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://ws.cdyne.com/WeatherWS/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" targetNamespace="http://ws.cdyne.com/WeatherWS/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <wsdl:types>
        <s:schema elementFormDefault="qualified" targetNamespace="http://ws.cdyne.com/WeatherWS/">
            <s:element name="GetWeatherInformation">
                <s:complexType />
            </s:element>
            <s:element name="GetWeatherInformationResponse">
                <s:complexType>
                    <s:sequence>
                        <s:element minOccurs="0" maxOccurs="1" name="GetWeatherInformationResult" type="tns:ArrayOfWeatherDescription" />
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:complexType name="ArrayOfWeatherDescription">
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="unbounded" name="WeatherDescription" type="tns:WeatherDescription" />
                </s:sequence>
            </s:complexType>
            <s:complexType name="WeatherDescription">
                <s:sequence>
                    <s:element minOccurs="1" maxOccurs="1" name="WeatherID" type="s:short" />
                    <s:element minOccurs="0" maxOccurs="1" name="Description" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="PictureURL" type="s:string" />
                </s:sequence>
            </s:complexType>
            <s:element name="GetCityForecastByZIP">
                <s:complexType>
                    <s:sequence>
                        <s:element minOccurs="0" maxOccurs="1" name="ZIP" type="s:string" />
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:element name="GetCityForecastByZIPResponse">
                <s:complexType>
                    <s:sequence>
                        <s:element minOccurs="0" maxOccurs="1" name="GetCityForecastByZIPResult" type="tns:ForecastReturn" />
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:complexType name="ForecastReturn">
                <s:sequence>
                    <s:element minOccurs="1" maxOccurs="1" name="Success" type="s:boolean" />
                    <s:element minOccurs="0" maxOccurs="1" name="ResponseText" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="State" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="City" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="WeatherStationCity" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="ForecastResult" type="tns:ArrayOfForecast" />
                </s:sequence>
            </s:complexType>
            <s:complexType name="ArrayOfForecast">
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="unbounded" name="Forecast" nillable="true" type="tns:Forecast" />
                </s:sequence>
            </s:complexType>
            <s:complexType name="Forecast">
                <s:sequence>
                    <s:element minOccurs="1" maxOccurs="1" name="Date" type="s:dateTime" />
                    <s:element minOccurs="1" maxOccurs="1" name="WeatherID" type="s:short" />
                    <s:element minOccurs="0" maxOccurs="1" name="Desciption" type="s:string" />
                    <s:element minOccurs="1" maxOccurs="1" name="Temperatures" type="tns:temp" />
                    <s:element minOccurs="1" maxOccurs="1" name="ProbabilityOfPrecipiation" type="tns:POP" />
                </s:sequence>
            </s:complexType>
            <s:complexType name="temp">
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="1" name="MorningLow" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="DaytimeHigh" type="s:string" />
                </s:sequence>
            </s:complexType>
            <s:complexType name="POP">
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="1" name="Nighttime" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Daytime" type="s:string" />
                </s:sequence>
            </s:complexType>
            <s:element name="GetCityWeatherByZIP">
                <s:complexType>
                    <s:sequence>
                        <s:element minOccurs="0" maxOccurs="1" name="ZIP" type="s:string" />
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:element name="GetCityWeatherByZIPResponse">
                <s:complexType>
                    <s:sequence>
                        <s:element minOccurs="1" maxOccurs="1" name="GetCityWeatherByZIPResult" type="tns:WeatherReturn" />
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:complexType name="WeatherReturn">
                <s:sequence>
                    <s:element minOccurs="1" maxOccurs="1" name="Success" type="s:boolean" />
                    <s:element minOccurs="0" maxOccurs="1" name="ResponseText" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="State" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="City" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="WeatherStationCity" type="s:string" />
                    <s:element minOccurs="1" maxOccurs="1" name="WeatherID" type="s:short" />
                    <s:element minOccurs="0" maxOccurs="1" name="Description" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Temperature" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="RelativeHumidity" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Wind" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Pressure" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Visibility" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="WindChill" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Remarks" type="s:string" />
                </s:sequence>
            </s:complexType>
            <s:element name="ArrayOfWeatherDescription" nillable="true" type="tns:ArrayOfWeatherDescription" />
            <s:element name="ForecastReturn" nillable="true" type="tns:ForecastReturn" />
            <s:element name="WeatherReturn" type="tns:WeatherReturn" />
        </s:schema>
    </wsdl:types>
    <wsdl:message name="GetWeatherInformationSoapIn">
        <wsdl:part name="parameters" element="tns:GetWeatherInformation" />
    </wsdl:message>
    <wsdl:message name="GetWeatherInformationSoapOut">
        <wsdl:part name="parameters" element="tns:GetWeatherInformationResponse" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPSoapIn">
        <wsdl:part name="parameters" element="tns:GetCityForecastByZIP" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPSoapOut">
        <wsdl:part name="parameters" element="tns:GetCityForecastByZIPResponse" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPSoapIn">
        <wsdl:part name="parameters" element="tns:GetCityWeatherByZIP" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPSoapOut">
        <wsdl:part name="parameters" element="tns:GetCityWeatherByZIPResponse" />
    </wsdl:message>
    <wsdl:message name="GetWeatherInformationHttpGetIn" />
    <wsdl:message name="GetWeatherInformationHttpGetOut">
        <wsdl:part name="Body" element="tns:ArrayOfWeatherDescription" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPHttpGetIn">
        <wsdl:part name="ZIP" type="s:string" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPHttpGetOut">
        <wsdl:part name="Body" element="tns:ForecastReturn" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPHttpGetIn">
        <wsdl:part name="ZIP" type="s:string" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPHttpGetOut">
        <wsdl:part name="Body" element="tns:WeatherReturn" />
    </wsdl:message>
    <wsdl:message name="GetWeatherInformationHttpPostIn" />
    <wsdl:message name="GetWeatherInformationHttpPostOut">
        <wsdl:part name="Body" element="tns:ArrayOfWeatherDescription" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPHttpPostIn">
        <wsdl:part name="ZIP" type="s:string" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPHttpPostOut">
        <wsdl:part name="Body" element="tns:ForecastReturn" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPHttpPostIn">
        <wsdl:part name="ZIP" type="s:string" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPHttpPostOut">
        <wsdl:part name="Body" element="tns:WeatherReturn" />
    </wsdl:message>
    <wsdl:portType name="WeatherSoap">
        <wsdl:operation name="GetWeatherInformation">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Gets Information for each WeatherID</wsdl:documentation>
            <wsdl:input message="tns:GetWeatherInformationSoapIn" />
            <wsdl:output message="tns:GetWeatherInformationSoapOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City Forecast Over the Next 7 Days, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityForecastByZIPSoapIn" />
            <wsdl:output message="tns:GetCityForecastByZIPSoapOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City's Weather, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityWeatherByZIPSoapIn" />
            <wsdl:output message="tns:GetCityWeatherByZIPSoapOut" />
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:portType name="WeatherHttpGet">
        <wsdl:operation name="GetWeatherInformation">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Gets Information for each WeatherID</wsdl:documentation>
            <wsdl:input message="tns:GetWeatherInformationHttpGetIn" />
            <wsdl:output message="tns:GetWeatherInformationHttpGetOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City Forecast Over the Next 7 Days, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityForecastByZIPHttpGetIn" />
            <wsdl:output message="tns:GetCityForecastByZIPHttpGetOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City's Weather, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityWeatherByZIPHttpGetIn" />
            <wsdl:output message="tns:GetCityWeatherByZIPHttpGetOut" />
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:portType name="WeatherHttpPost">
        <wsdl:operation name="GetWeatherInformation">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Gets Information for each WeatherID</wsdl:documentation>
            <wsdl:input message="tns:GetWeatherInformationHttpPostIn" />
            <wsdl:output message="tns:GetWeatherInformationHttpPostOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City Forecast Over the Next 7 Days, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityForecastByZIPHttpPostIn" />
            <wsdl:output message="tns:GetCityForecastByZIPHttpPostOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City's Weather, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityWeatherByZIPHttpPostIn" />
            <wsdl:output message="tns:GetCityWeatherByZIPHttpPostOut" />
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="WeatherSoap" type="tns:WeatherSoap">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="GetWeatherInformation">
            <soap:operation soapAction="http://ws.cdyne.com/WeatherWS/GetWeatherInformation" style="document" />
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <soap:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP" style="document" />
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <soap:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP" style="document" />
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:binding name="WeatherSoap12" type="tns:WeatherSoap">
        <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="GetWeatherInformation">
            <soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetWeatherInformation" style="document" />
            <wsdl:input>
                <soap12:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP" style="document" />
            <wsdl:input>
                <soap12:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP" style="document" />
            <wsdl:input>
                <soap12:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:binding name="WeatherHttpGet" type="tns:WeatherHttpGet">
        <http:binding verb="GET" />
        <wsdl:operation name="GetWeatherInformation">
            <http:operation location="/GetWeatherInformation" />
            <wsdl:input>
                <http:urlEncoded />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <http:operation location="/GetCityForecastByZIP" />
            <wsdl:input>
                <http:urlEncoded />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <http:operation location="/GetCityWeatherByZIP" />
            <wsdl:input>
                <http:urlEncoded />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:binding name="WeatherHttpPost" type="tns:WeatherHttpPost">
        <http:binding verb="POST" />
        <wsdl:operation name="GetWeatherInformation">
            <http:operation location="/GetWeatherInformation" />
            <wsdl:input>
                <mime:content type="application/x-www-form-urlencoded" />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <http:operation location="/GetCityForecastByZIP" />
            <wsdl:input>
                <mime:content type="application/x-www-form-urlencoded" />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <http:operation location="/GetCityWeatherByZIP" />
            <wsdl:input>
                <mime:content type="application/x-www-form-urlencoded" />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="Weather">
        <wsdl:port name="WeatherSoap" binding="tns:WeatherSoap">
            <soap:address location="http://wsf.cdyne.com/WeatherWS/Weather.asmx" />
        </wsdl:port>
        <wsdl:port name="WeatherSoap12" binding="tns:WeatherSoap12">
            <soap12:address location="http://wsf.cdyne.com/WeatherWS/Weather.asmx" />
        </wsdl:port>
        <wsdl:port name="WeatherHttpGet" binding="tns:WeatherHttpGet">
            <http:address location="http://wsf.cdyne.com/WeatherWS/Weather.asmx" />
        </wsdl:port>
        <wsdl:port name="WeatherHttpPost" binding="tns:WeatherHttpPost">
            <http:address location="http://wsf.cdyne.com/WeatherWS/Weather.asmx" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>


================================================
FILE: src/Middleware/CisDhlMiddleware.php
================================================
<?php

namespace CodeDredd\Soap\Middleware;

use Http\Client\Common\Plugin;
use Http\Promise\Promise;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Soap\Psr18Transport\Xml\XmlMessageManipulator;
use Soap\Xml\Builder\SoapHeader;
use Soap\Xml\Builder\SoapHeaders;
use Soap\Xml\Manipulator\PrependSoapHeaders;
use VeeWee\Xml\Dom\Document;

use function VeeWee\Xml\Dom\Builder\children;
use function VeeWee\Xml\Dom\Builder\namespaced_element;
use function VeeWee\Xml\Dom\Builder\value;

class CisDhlMiddleware implements Plugin
{
    /**
     * @var string
     */
    public const CIS_NS = 'http://dhl.de/webservice/cisbase';

    /**
     * @var string
     */
    private $user;

    /**
     * @var string
     */
    private $signature;

    public function __construct(string $user, string $signature)
    {
        $this->user = $user;
        $this->signature = $signature;
    }

    public function getName(): string
    {
        return 'cis_dhl_middleware';
    }

    public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
    {
        return $next(
            (new XmlMessageManipulator())(
                $request,
                function (Document $document) {
                    $builder = new SoapHeaders(
                        new SoapHeader(
                            self::CIS_NS,
                            'cis:Authentification',
                            children(
                                namespaced_element(self::CIS_NS, 'user', value($this->user)),
                                namespaced_element(self::CIS_NS, 'signature', value($this->signature))
                            )
                        )
                    );

                    $headers = $document->build($builder);

                    return $document->manipulate(new PrependSoapHeaders(...$headers));
                }
            )
        );
    }

    /**
     * @param  ResponseInterface  $response
     * @return ResponseInterface
     */
    public function afterResponse(ResponseInterface $response): ResponseInterface
    {
        return $response;
    }
}


================================================
FILE: src/Middleware/WsseMiddleware.php
================================================
<?php

namespace CodeDredd\Soap\Middleware;

use Http\Client\Common\Plugin;
use Http\Promise\Promise;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use RobRichards\WsePhp\WSSESoap;
use RobRichards\XMLSecLibs\XMLSecurityKey;
use Soap\Psr18Transport\Xml\XmlMessageManipulator;
use VeeWee\Xml\Dom\Document;

class WsseMiddleware implements Plugin
{
    private string $privateKeyFile;
    private string $publicKeyFile;
    private string $serverCertificateFile = '';
    private int $timestamp = 3600;
    private bool $signAllHeaders = false;
    private string $digitalSignMethod = XMLSecurityKey::RSA_SHA1;
    private string $userTokenName = '';
    private string $userTokenPassword = '';
    private bool $userTokenDigest = false;
    private bool $encrypt = false;
    private bool $hasUserToken = false;
    private bool $serverCertificateHasSubjectKeyIdentifier = true;
    private bool $mustUnderstand = true;

    public function __construct(array $properties)
    {
        foreach ($properties as $key => $value) {
            $this->{$key} = $value;
        }
    }

    public function getName(): string
    {
        return 'wsse_middleware';
    }

    public function withTimestamp(int $timestamp = 3600): self
    {
        $this->timestamp = $timestamp;

        return $this;
    }

    public function withAllHeadersSigned(): self
    {
        $this->signAllHeaders = true;

        return $this;
    }

    public function withDigitalSignMethod(string $digitalSignMethod): self
    {
        $this->digitalSignMethod = $digitalSignMethod;

        return $this;
    }

    public function withUserToken(string $username, string $password = null, $digest = false): self
    {
        $this->userTokenName = $username;
        $this->userTokenPassword = $password;
        $this->userTokenDigest = $digest;

        return $this;
    }

    public function withEncryption(string $serverCertificateFile): self
    {
        $this->encrypt = true;
        $this->serverCertificateFile = $serverCertificateFile;

        return $this;
    }

    public function withServerCertificateHasSubjectKeyIdentifier(bool $hasSubjectKeyIdentifier): self
    {
        $this->serverCertificateHasSubjectKeyIdentifier = $hasSubjectKeyIdentifier;

        return $this;
    }

    public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
    {
        return $this->beforeRequest($next, $request)->then(
            fn (ResponseInterface $response): ResponseInterface => $this->afterResponse($response)
        );
    }

    public function beforeRequest(callable $handler, RequestInterface $request): Promise
    {
        $request = (new XmlMessageManipulator())(
            $request,
            function (Document $xml) {
                $wsse = new WSSESoap($xml->toUnsafeDocument(), $this->mustUnderstand);

                // Prepare the WSSE soap class:
                $wsse->signAllHeaders = $this->signAllHeaders;
                $wsse->addTimestamp($this->timestamp);

                // Add a user token if this is configured.
                if ($this->hasUserToken) {
                    $wsse->addUserToken($this->userTokenName, $this->userTokenPassword, $this->userTokenDigest);
                }

                if (! empty($this->privateKeyFile) && ! empty($this->publicKeyFile)) {
                    //  Add certificate (BinarySecurityToken) to the message
                    $token = $wsse->addBinaryToken(file_get_contents($this->publicKeyFile));

                    // Create new XMLSec Key using the dsigType and type is private key
                    $key = new XMLSecurityKey($this->digitalSignMethod, ['type' => 'private']);
                    $key->loadKey($this->privateKeyFile, true);
                    $wsse->signSoapDoc($key);

                    //  Attach token pointer to Signature:
                    $wsse->attachTokentoSig($token);
                }

                // Add end-to-end encryption if configured:
                if ($this->encrypt) {
                    $key = new XMLSecurityKey(XMLSecurityKey::AES256_CBC);
                    $key->generateSessionKey();
                    $siteKey = new XMLSecurityKey(XMLSecurityKey::RSA_OAEP_MGF1P, ['type' => 'public']);
                    $siteKey->loadKey($this->serverCertificateFile, true, true);
                    $wsse->encryptSoapDoc($siteKey, $key, [
                        'KeyInfo' => [
                            'X509SubjectKeyIdentifier' => $this->serverCertificateHasSubjectKeyIdentifier,
                        ],
                    ]);
                }
            }
        );

        return $handler($request);
    }

    public function afterResponse(ResponseInterface $response): ResponseInterface
    {
        if (! $this->encrypt) {
            return $response;
        }

        return (new XmlMessageManipulator())(
            $response,
            function (Document $xml) {
                $wsse = new WSSESoap($xml->toUnsafeDocument());
                $wsse->decryptSoapDoc(
                    $xml->toUnsafeDocument(),
                    [
                        'keys' => [
                            'private' => [
                                'key' => $this->privateKeyFile,
                                'isFile' => true,
                                'isCert' => false,
                            ],
                        ],
                    ]
                );
            }
        );
    }
}


================================================
FILE: src/Ray/LaravelRay.php
================================================
<?php

namespace CodeDredd\Soap\Ray;

use Closure;
use Spatie\LaravelRay\RayProxy;
use Spatie\LaravelRay\Watchers\Watcher;
use Spatie\Ray\Ray as SpatieRay;

class LaravelRay
{
    public function register()
    {
        SpatieRay::macro('showSoapClientRequests', function ($callable = null): RayProxy {
            $watcher = app(SoapClientWatcher::class);

            return $this->handleWatcherCallable($watcher, $callable);
        });
        SpatieRay::macro('stopShowingSoapClientRequests', fn () => app(SoapClientWatcher::class)->disable());
    }

    protected function handleWatcherCallable(Watcher $watcher, Closure $callable = null): RayProxy
    {
        $rayProxy = new RayProxy();

        $wasEnabled = $watcher->enabled();

        $watcher->enable();

        $watcher->setRayProxy($rayProxy);

        if ($callable) {
            $callable();

            if (! $wasEnabled) {
                $watcher->disable();
            }
        }

        return $rayProxy;
    }
}


================================================
FILE: src/Ray/SoapClientWatcher.php
================================================
<?php

namespace CodeDredd\Soap\Ray;

use CodeDredd\Soap\Client\Events\RequestSending;
use CodeDredd\Soap\Client\Events\ResponseReceived;
use CodeDredd\Soap\Client\Request;
use CodeDredd\Soap\Client\Response;
use Illuminate\Support\Facades\Event;
use Spatie\LaravelRay\Ray;
use Spatie\LaravelRay\Watchers\Watcher;
use Spatie\Ray\Payloads\TablePayload;

class SoapClientWatcher extends Watcher
{
    public function register(): void
    {
        $this->enabled = config('soap.ray.send_soap_client_requests', false);

        Event::listen(RequestSending::class, function (RequestSending $event) {
            if (! $this->enabled()) {
                return;
            }

            $ray = $this->handleRequest($event->request);

            optional($this->rayProxy)->applyCalledMethods($ray);
        });

        Event::listen(ResponseReceived::class, function (ResponseReceived $event) {
            if (! $this->enabled()) {
                return;
            }

            $ray = $this->handleResponse($event->request, $event->response);

            optional($this->rayProxy)->applyCalledMethods($ray);
        });
    }

    protected function handleRequest(Request $request)
    {
        $payload = new TablePayload([
            'Action' => $request->action(),
            'URL' => $request->url(),
            'Headers' => $request->headers(),
            'Data' => $request->arguments(),
            'Body' => $request->body(),
        ], 'SOAP');

        return app(Ray::class)->sendRequest($payload);
    }

    protected function handleResponse(Request $request, Response $response)
    {
        $payload = new TablePayload([
            'URL' => $request->url(),
            'Real Request' => ! empty($response->handlerStats()),
            'Success' => $response->successful(),
            'Status' => $response->status(),
            'Headers' => $response->headers(),
            'Body' => rescue(function () use ($response) {
                return $response->json();
            }, $response->body(), false),
            'Size' => $response->handlerStats()['size_download'] ?? null,
            'Connection time' => $response->handlerStats()['connect_time'] ?? null,
            'Duration' => $response->handlerStats()['total_time'] ?? null,
            'Request Size' => $response->handlerStats()['request_size'] ?? null,
        ], 'SOAP');

        return app(Ray::class)->sendRequest($payload);
    }
}


================================================
FILE: src/SoapClient.php
================================================
<?php

namespace CodeDredd\Soap;

use Closure;
use CodeDredd\Soap\Client\Events\ConnectionFailed;
use CodeDredd\Soap\Client\Events\RequestSending;
use CodeDredd\Soap\Client\Events\ResponseReceived;
use CodeDredd\Soap\Client\Request;
use CodeDredd\Soap\Client\Response;
use CodeDredd\Soap\Driver\ExtSoap\ExtSoapEngineFactory;
use CodeDredd\Soap\Exceptions\NotFoundConfigurationException;
use CodeDredd\Soap\Exceptions\SoapException;
use CodeDredd\Soap\Middleware\CisDhlMiddleware;
use CodeDredd\Soap\Middleware\WsseMiddleware;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response as Psr7Response;
use GuzzleHttp\TransferStats;
use Http\Client\Common\PluginClient;
use Http\Client\Exception\HttpException;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Conditionable;
use Illuminate\Support\Traits\Macroable;
use Phpro\SoapClient\Type\ResultInterface;
use Phpro\SoapClient\Type\ResultProviderInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Soap\Engine\Engine;
use Soap\Engine\Transport;
use Soap\ExtSoapEngine\AbusedClient;
use Soap\ExtSoapEngine\ExtSoapOptions;
use Soap\ExtSoapEngine\Transport\TraceableTransport;
use Soap\ExtSoapEngine\Wsdl\WsdlProvider;
use Soap\Psr18Transport\Middleware\RemoveEmptyNodesMiddleware;
use Soap\Psr18Transport\Psr18Transport;
use Soap\Psr18Transport\Wsdl\Psr18Loader;
use Soap\Psr18WsseMiddleware\WsaMiddleware;
use Soap\Psr18WsseMiddleware\WsaMiddleware2005;
use Soap\Wsdl\Loader\FlatteningLoader;
use Soap\Wsdl\Loader\StreamWrapperLoader;

/**
 * Class SoapClient.
 */
class SoapClient
{
    use Macroable {
        __call as macroCall;
    }
    use Conditionable;

    protected ClientInterface $client;

    protected PluginClient $pluginClient;

    protected Engine $engine;

    protected array $options = [];

    protected ExtSoapOptions $extSoapOptions;

    protected TraceableTransport|Transport $transport;

    protected array $guzzleClientOptions = [];

    /**
     * The transfer stats for the request.
     */
    protected ?TransferStats $transferStats = null;

    protected string $wsdl = '';

    protected bool $isClientBuilded = false;

    protected array $middlewares = [];

    protected SoapFactory|null $factory;

    protected FlatteningLoader|WsdlProvider $wsdlProvider;

    protected array $cookies = [];

    /**
     * The callbacks that should execute before the request is sent.
     */
    protected \Illuminate\Support\Collection $beforeSendingCallbacks;

    /**
     * The stub callables that will handle requests.
     */
    protected \Illuminate\Support\Collection|null $stubCallbacks;

    /**
     * The sent request object, if a request has been made.
     *
     * @var Request|null
     */
    protected $request;

    /**
     * Create a new Soap Client instance.
     *
     * @param  \CodeDredd\Soap\SoapFactory|null  $factory
     * @return void
     */
    public function __construct(SoapFactory $factory = null)
    {
        $this->factory = $factory;
        $this->client = new Client($this->guzzleClientOptions);
        $this->pluginClient = new PluginClient($this->client, $this->middlewares);
        $this->wsdlProvider = new FlatteningLoader(Psr18Loader::createForClient($this->pluginClient));
        $this->beforeSendingCallbacks = collect([function (Request $request, array $options, SoapClient $soapClient) {
            $soapClient->request = $request;
            $soapClient->cookies = Arr::wrap($options['cookies']);

            $soapClient->dispatchRequestSendingEvent();
        }]);
    }

    public function refreshWsdlProvider()
    {
        $this->wsdlProvider = new FlatteningLoader(Psr18Loader::createForClient($this->pluginClient));

        return $this;
    }

    public function refreshPluginClient(): static
    {
        $this->pluginClient = new PluginClient($this->client, $this->middlewares);

        return $this;
    }

    public function getPluginClient(): PluginClient
    {
        return $this->pluginClient;
    }

    protected function setTransport(Transport $handler = null): static
    {
        $soapClient = AbusedClient::createFromOptions(
            ExtSoapOptions::defaults($this->wsdl, $this->options)
        );
        $transport = $handler ?? Psr18Transport::createForClient($this->pluginClient);

        $this->transport = $handler ?? new TraceableTransport(
            $soapClient,
            $transport
        );

        return $this;
    }

    /**
     * Add the given headers to the request.
     */
    public function withHeaders(array $headers): static
    {
        return $this->withGuzzleClientOptions(array_merge_recursive($this->options, [
            'headers' => $headers,
        ]));
    }

    public function getTransport(): TraceableTransport|Transport
    {
        return $this->transport;
    }

    public function getClient(): Client|ClientInterface
    {
        return $this->client;
    }

    public function withGuzzleClientOptions(array ...$options): static
    {
        $this->guzzleClientOptions = array_merge_recursive($this->guzzleClientOptions, ...$options);
        $this->client = new Client($this->guzzleClientOptions);

        return $this;
    }

    public function getEngine(): Engine
    {
        return $this->engine;
    }

    /**
     * @return $this
     */
    public function withRemoveEmptyNodes()
    {
        $this->middlewares = array_merge_recursive($this->middlewares, [
            new RemoveEmptyNodesMiddleware(),
        ]);

        return $this;
    }

    /**
     * @param  string|array  $username
     * @param  string|null  $password
     * @return $this
     */
    public function withBasicAuth($username, ?string $password = null)
    {
        if (is_array($username)) {
            ['username' => $username, 'password' => $password] = $username;
        }

        $this->withHeaders([
            'Authorization' => sprintf('Basic %s', base64_encode(
                sprintf('%s:%s', $username, $password)
            )),
        ]);

        return $this;
    }

    /**
     * @param  string|array  $user
     * @param  string|null  $signature
     * @return $this
     */
    public function withCisDHLAuth($user, ?string $signature = null)
    {
        if (is_array($user)) {
            ['username' => $user, 'password' => $signature] = $user;
        }

        $this->middlewares = array_merge_recursive($this->middlewares, [
            new CisDhlMiddleware($user, $signature),
        ]);

        return $this;
    }

    /**
     * @return $this
     */
    public function withWsa()
    {
        $this->middlewares = array_merge_recursive($this->middlewares, [
            new WsaMiddleware(),
        ]);

        return $this;
    }

    /**
     * @return $this
     */
    public function withWsa2005()
    {
        $this->middlewares = array_merge_recursive($this->middlewares, [
            new WsaMiddleware2005(),
        ]);

        return $this;
    }

    public function withWsse(array $options): static
    {
        $this->middlewares = array_merge_recursive($this->middlewares, [
            new WsseMiddleware($options),
        ]);

        return $this;
    }

    /**
     * Merge new options into the client.
     *
     * @param  array  $options
     * @return $this
     */
    public function withOptions(array $options)
    {
        return tap($this, function ($request) use ($options) {
            return $this->options = array_merge_recursive($this->options, $options);
        });
    }

    /**
     * Merge the given options with the current request options.
     *
     * @param  array  $options
     * @return array
     */
    public function mergeOptions(...$options)
    {
        return array_merge_recursive($this->options, ...$options);
    }

    /**
     * Make it possible to debug the last request.
     */
    public function debugLastSoapRequest(): array
    {
        if ($this->transport instanceof TraceableTransport) {
            $lastRequestInfo = $this->transport->collectLastRequestInfo();

            return [
                'request' => [
                    'headers' => trim($lastRequestInfo->getLastRequestHeaders()),
                    'body' => $lastRequestInfo->getLastRequest(),
                ],
                'response' => [
                    'headers' => trim($lastRequestInfo->getLastResponseHeaders()),
                    'body' => $lastRequestInfo->getLastResponse(),
                ],
            ];
        }

        return [];
    }

    /**
     * Handle a failed validation attempt.
     *
     * @param  \Illuminate\Contracts\Validation\Validator  $validator
     * @return Response
     */
    protected function failedValidation(Validator $validator)
    {
        return Response::fromSoapResponse([
            'success' => false,
            'message' => __('Invalid data.'),
            'errors' => $validator->errors(),
        ]);
    }

    public function __call($method, $parameters)
    {
        if (static::hasMacro($method)) {
            return $this->macroCall($method, $parameters);
        }

        return $this->call($method, $parameters[0] ?? $parameters);
    }

    public function call(string $method, Validator|array $arguments = []): Response
    {
        try {
            if (! $this->isClientBuilded) {
                $this->buildClient();
            }
            $this->refreshEngine();
            if ($arguments instanceof Validator) {
                if ($arguments->fails()) {
                    return $this->failedValidation($arguments);
                }
                $arguments = $arguments->validated();
            }
            $arguments = config()->get('soap.call.wrap_arguments_in_array', true) ? [$arguments] : $arguments;
            $result = $this->engine->request($method, $arguments);
            if ($result instanceof ResultProviderInterface) {
                return $this->buildResponse(Response::fromSoapResponse($result->getResult()));
            }
            if (! $result instanceof ResultInterface) {
                return $this->buildResponse(Response::fromSoapResponse($result));
            }

            return $this->buildResponse(new Response(new Psr7Response(200, [], $result)));
        } catch (\Exception $exception) {
            if ($exception instanceof \SoapFault) {
                return $this->buildResponse(Response::fromSoapFault($exception));
            }
            $previous = $exception->getPrevious();
            $this->dispatchConnectionFailedEvent();
            if ($previous instanceof HttpException) {
                /** @var HttpException $previous */
                return new Response($previous->getResponse());
            }

            throw SoapException::fromThrowable($exception);
        }
    }

    protected function buildResponse($response)
    {
        return tap($response, function ($result) {
            $this->populateResponse($result);
            $this->dispatchResponseReceivedEvent($result);
        });
    }

    /**
     * Build the Soap client.
     *
     * @param  string  $setup
     * @return SoapClient
     *
     * @throws NotFoundConfigurationException
     */
    public function buildClient(string $setup = '')
    {
        $this->byConfig($setup);
        $this->withGuzzleClientOptions([
            'handler' => $this->buildHandlerStack(),
            'on_stats' => function ($transferStats) {
                $this->transferStats = $transferStats;
            },
        ]);
        $this->isClientBuilded = true;

        return $this;
    }

    /**
     * @param  string  $setup
     * @return $this
     *
     * @throws NotFoundConfigurationException
     */
    public function byConfig(string $setup)
    {
        if (! empty($setup)) {
            $setup = config()->get('soap.clients.'.$setup);
            if (! $setup) {
                throw new NotFoundConfigurationException($setup);
            }
            foreach ($setup as $setupItem => $setupItemConfig) {
                if (is_bool($setupItemConfig)) {
                    $this->{Str::camel($setupItem)}();
                } elseif (is_array($setupItemConfig)) {
                    $this->{Str::camel($setupItem)}($this->arrayKeysToCamel($setupItemConfig));
                } elseif (is_string($setupItemConfig)) {
                    $this->{Str::camel($setupItem)}($setupItemConfig);
                }
            }
        }

        return $this;
    }

    /**
     * @param  array  $items
     * @return array
     */
    protected function arrayKeysToCamel(array $items)
    {
        $changedItems = [];
        foreach ($items as $key => $value) {
            $changedItems[Str::camel($key)] = $value;
        }

        return $changedItems;
    }

    /**
     * Build the before sending handler stack.
     *
     * @return \GuzzleHttp\HandlerStack
     */
    public function buildHandlerStack()
    {
        return tap(HandlerStack::create(), function ($stack) {
            $stack->push($this->buildBeforeSendingHandler(), 'before_sending');
            $stack->push($this->buildRecorderHandler(), 'recorder');
            $stack->push($this->buildStubHandler(), 'stub');
        });
    }

    /**
     * Build the before sending handler.
     */
    public function buildBeforeSendingHandler(): Closure
    {
        return function ($handler) {
            return function ($request, $options) use ($handler) {
                return $handler($this->runBeforeSendingCallbacks($request, $options), $options);
            };
        };
    }

    /**
     * Execute the "before sending" callbacks.
     */
    public function runBeforeSendingCallbacks(RequestInterface $request, array $options): mixed
    {
        return tap($request, function ($request) use ($options) {
            $this->beforeSendingCallbacks->each->__invoke(
                new Request($request),
                $options,
                $this
            );
        });
    }

    /**
     * Populate the given response with additional data.
     *
     * @param  \CodeDredd\Soap\Client\Response  $response
     * @return \CodeDredd\Soap\Client\Response
     */
    protected function populateResponse(Response $response)
    {
        $response->setCookies($this->cookies);
        $response->setTransferStats($this->transferStats);

        return $response;
    }

    /**
     * Dispatch the RequestSending event if a dispatcher is available.
     *
     * @return void
     */
    protected function dispatchRequestSendingEvent()
    {
        event(new RequestSending($this->request));
    }

    /**
     * Dispatch the ResponseReceived event if a dispatcher is available.
     *
     * @param  \CodeDredd\Soap\Client\Response  $response
     * @return void
     */
    protected function dispatchResponseReceivedEvent(Response $response)
    {
        if (! $this->request) {
            return;
        }

        event(new ResponseReceived($this->request, $response));
    }

    /**
     * Dispatch the ConnectionFailed event if a dispatcher is available.
     *
     * @return void
     */
    protected function dispatchConnectionFailedEvent()
    {
        event(new ConnectionFailed($this->request));
    }

    /**
     * Build the recorder handler.
     *
     * @return Closure
     */
    public function buildRecorderHandler()
    {
        return function ($handler) {
            return function ($request, $options) use ($handler) {
                $promise = $handler($request, $options);

                return $promise->then(function ($response) use ($request) {
                    optional($this->factory)->recordRequestResponsePair(
                        new Request($request),
                        new Response($response)
                    );

                    return $response;
                });
            };
        };
    }

    /**
     * Build the stub handler.
     *
     * @return Closure
     */
    public function buildStubHandler()
    {
        return function (callable $handler) {
            return function ($request, $options) use ($handler) {
                $response = ($this->stubCallbacks ?? collect())
                    ->map
                    ->__invoke(new Request($request), $options)
                    ->filter()
                    ->first();
                if (is_null($response)) {
                    return $handler($request, $options);
                } elseif (is_array($response)) {
                    return SoapFactory::response($response);
                }

                return $response;
            };
        };
    }

    /**
     * @return $this
     */
    protected function refreshEngine()
    {
        $this->refreshPluginClient();
        $this->setTransport();
        $this->refreshExtSoapOptions();
        $this->engine = ExtSoapEngineFactory::fromOptionsWithHandler(
            $this->extSoapOptions,
            $this->transport,
            $this->factory->isRecording()
        );
        $this->refreshWsdlProvider();

        return $this;
    }

    protected function refreshExtSoapOptions()
    {
        $this->extSoapOptions = ExtSoapOptions::defaults($this->wsdl, $this->options);
        if ($this->factory->isRecording()) {
            $this->wsdlProvider = new FlatteningLoader(new StreamWrapperLoader());
//            $this->extSoapOptions->withWsdlProvider($this->wsdlProvider);
        }
    }

    /**
     * @param  string  $wsdl
     * @return $this
     */
    public function baseWsdl(string $wsdl)
    {
        $this->wsdl = $wsdl;

        return $this;
    }

    /**
     * Register a stub callable that will intercept requests and be able to return stub responses.
     *
     * @param  callable  $callback
     * @return $this
     */
    public function stub($callback)
    {
        $this->stubCallbacks = collect($callback);

        return $this;
    }
}


================================================
FILE: src/SoapFactory.php
================================================
<?php

namespace CodeDredd\Soap;

use Closure;
use CodeDredd\Soap\Client\ResponseSequence;
use GuzzleHttp\Promise\Create;
use GuzzleHttp\Psr7\Response as Psr7Response;
use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable;

class SoapFactory
{
    use Macroable {
        __call as macroCall;
    }

    /**
     * The client class name.
     *
     * @var string
     */
    public static $clientClass = 'CodeDredd\Soap\SoapClient';

    /**
     * The stub callables that will handle requests.
     *
     * @var \Illuminate\Support\Collection
     */
    protected $stubCallbacks;

    /**
     * Indicates if the factory is recording requests and responses.
     *
     * @var bool
     */
    protected $recording = false;

    /**
     * The recorded response array.
     *
     * @var array
     */
    protected $recorded = [];

    /**
     * All created response sequences.
     *
     * @var array
     */
    protected $responseSequences = [];

    protected $fakeWsdlLocation;

    /**
     * Create a new factory instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->stubCallbacks = collect();
//        $this->fakeWsdlLocation = __DIR__.'/Faker/fake.wsdl';
    }

    /**
     * Execute a method against a new pending request instance.
     *
     * @param  string  $method
     * @param  array  $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        if (static::hasMacro($method)) {
            return $this->macroCall($method, $parameters);
        }

        if (Str::contains($method, 'assert')) {
            return (new SoapTesting($this))->{$method}(...$parameters);
        }

        return tap($this->client(), function ($request) {
            $request->stub($this->stubCallbacks);
        })->{$method}(...$parameters);
    }

    public function isRecording()
    {
        return $this->recording;
    }

    public function getResponseSequences()
    {
        return $this->responseSequences;
    }

    public function getRecorded()
    {
        return $this->recorded;
    }

    /**
     * Record a request response pair.
     *
     * @param  \CodeDredd\Soap\Client\Request  $request
     * @param  \CodeDredd\Soap\Client\Response  $response
     * @return void
     */
    public function recordRequestResponsePair($request, $response)
    {
        if ($this->recording) {
            $this->recorded[] = [$request, $response];
        }
    }

    public function fakeWsdl(string $wsdl)
    {
        $this->fakeWsdlLocation = $wsdl;

        return $this;
    }

    public function getFakeWsdl()
    {
        return $this->fakeWsdlLocation;
    }

    /**
     * Register a response sequence for the given URL pattern.
     *
     * @param  string  $url
     * @return \CodeDredd\Soap\Client\ResponseSequence
     */
    public function fakeSequence($url = '*')
    {
        return tap($this->sequence(), function ($sequence) use ($url) {
            $this->fake([$url => $sequence]);
        });
    }

    /**
     * Get an invokable object that returns a sequence of responses in order for use during stubbing.
     *
     * @param  array  $responses
     * @return \CodeDredd\Soap\Client\ResponseSequence
     */
    public function sequence(array $responses = [])
    {
        return $this->responseSequences[] = new ResponseSequence($responses);
    }

    /**
     * Register a stub callable that will intercept requests and be able to return stub responses.
     *
     * @param  callable|array  $callback
     * @return $this
     */
    public function fake($callback = null)
    {
        $this->record();

        if (is_null($callback)) {
            $callback = function () {
                return static::response();
            };
        }

        if (is_array($callback)) {
            $callback['*'] = $callback['*'] ?? self::response();
            foreach ($callback as $method => $callable) {
                $this->stubMethod($method, $callable);
            }

            return $this;
        }
        $this->stubCallbacks = $this->stubCallbacks->merge(collect([
            $callback instanceof Closure
                ? $callback
                : function () use ($callback) {
                    return $callback;
                },
        ]));

        return $this;
    }

    /**
     * Begin recording request / response pairs.
     *
     * @return $this
     */
    protected function record()
    {
        $this->recording = true;

        return $this;
    }

    /**
     * Create a new response instance for use during stubbing.
     *
     * @param  array|string|null  $body
     * @param  int  $status
     * @param  array  $headers
     * @return \GuzzleHttp\Promise\PromiseInterface
     */
    public static function response($body = null, $status = 200, $headers = [])
    {
        if (is_array($body)) {
            $body = json_encode($body);
        } elseif (is_string($body)) {
            $body = json_encode([
                'response' => $body,
            ]);
        }

        return Create::promiseFor(new Psr7Response($status, $headers, $body));
    }

    /**
     * Stub the given URL using the given callback.
     *
     * @param  string  $method
     * @param  \CodeDredd\Soap\Client\Response|\GuzzleHttp\Promise\PromiseInterface|callable  $callback
     * @return $this
     */
    public function stubMethod($method, $callback)
    {
        return $this->fake(function ($request, $options) use ($method, $callback) {
            if (! Str::is(Str::start($method, '*'), $request->action())) {
                return;
            }

            return $callback instanceof Closure || $callback instanceof ResponseSequence
                ? $callback($request, $options)
                : $callback;
        });
    }

    /**
     * Get a collection of the request / response pairs matching the given truth test.
     *
     * @param  callable  $callback
     * @return \Illuminate\Support\Collection
     */
    public function recorded($callback)
    {
        if (empty($this->recorded)) {
            return collect();
        }

        $callback = $callback ?: function () {
            return true;
        };

        return collect($this->recorded)->filter(function ($pair) use ($callback) {
            return $callback($pair[0], $pair[1]);
        });
    }

    /**
     * Get a new client class instance.
     *
     * @return SoapClient
     */
    public function client()
    {
        return new static::$clientClass($this);
    }

    /**
     * Set the client class name.
     *
     * @param  string  $clientClass
     */
    public static function useClientClass(string $clientClass): void
    {
        static::$clientClass = $clientClass;
    }
}


================================================
FILE: src/SoapServiceProvider.php
================================================
<?php

namespace CodeDredd\Soap;

use CodeDredd\Soap\Ray\LaravelRay;
use CodeDredd\Soap\Ray\SoapClientWatcher;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelRay\Watchers\Watcher;

class SoapServiceProvider extends PackageServiceProvider
{
    public function configurePackage(Package $package): void
    {
        /*
         * This class is a Package Service Provider
         *
         * More info: https://github.com/spatie/laravel-package-tools
         */
        $package
            ->name('soap')
            ->hasConfigFile('soap');
    }

    public function packageRegistered()
    {
        $this->registerService();
        $this->registerRay();
    }

    public function packageBooted()
    {
        $this->bootRay();
    }

    /**
     * Register Soap's services in the container.
     *
     * @return void
     */
    protected function registerService()
    {
        $this->app->bind('Soap', function () {
            return new SoapFactory();
        });
    }

    protected function registerRay()
    {
        if (! class_exists('Spatie\\LaravelRay\\Ray')) {
            return;
        }
        /** @var LaravelRay $macros */
        $macros = app(LaravelRay::class);

        $macros->register();

        $this->app->singleton(SoapClientWatcher::class);
    }

    protected function bootRay()
    {
        if (! class_exists('Spatie\\LaravelRay\\Ray')) {
            return;
        }

        /** @var Watcher $watcher */
        $watcher = app(SoapClientWatcher::class);

        $watcher->register();
    }
}


================================================
FILE: src/SoapTesting.php
================================================
<?php

namespace CodeDredd\Soap;

use CodeDredd\Soap\Client\Request;
use PHPUnit\Framework\Assert as PHPUnit;

class SoapTesting
{
    /**
     * @var SoapFactory|null
     */
    protected $factory;

    /**
     * Create a new Soap Testing instance.
     *
     * @param  \CodeDredd\Soap\SoapFactory|null  $factory
     * @return void
     */
    public function __construct(SoapFactory $factory = null)
    {
        $this->factory = $factory;
    }

    /**
     * Assert that a request / response pair was not recorded matching a given truth test.
     *
     * @param  callable  $callback
     * @return void
     */
    public function assertNotSent($callback)
    {
        PHPUnit::assertFalse(
            $this->factory->recorded($callback)->count() > 0,
            'Unexpected request was recorded.'
        );
    }

    /**
     * Assert that no request / response pair was recorded.
     *
     * @return void
     */
    public function assertNothingSent()
    {
        PHPUnit::assertEmpty(
            $this->factory->getRecorded(),
            'Requests were recorded.'
        );
    }

    /**
     * Assert that every created response sequence is empty.
     *
     * @return void
     */
    public function assertSequencesAreEmpty()
    {
        foreach ($this->factory->getResponseSequences() as $responseSequence) {
            PHPUnit::assertTrue(
                $responseSequence->isEmpty(),
                'Not all response sequences are empty.'
            );
        }
    }

    /**
     * Assert that a given soap action is called with optional arguments.
     *
     * @param  string  $action
     * @return void
     */
    public function assertActionCalled(string $action)
    {
        $this->assertSent(function (Request $request) use ($action) {
            return $request->action() === $action;
        });
    }

    /**
     * Assert that a request / response pair was recorded matching a given truth test.
     *
     * @param  callable  $callback
     * @return void
     */
    public function assertSent($callback)
    {
        PHPUnit::assertTrue(
            $this->factory->recorded($callback)->count() > 0,
            'An expected request was not recorded.'
        );
    }

    /**
     * Assert how many requests have been recorded.
     */
    public function assertSentCount(int $count): void
    {
        PHPUnit::assertCount($count, $this->factory->getRecorded());
    }

    /**
     * Assert that the given request was sent in the given order.
     *
     * @param  array  $callbacks
     * @return void
     */
    public function assertSentInOrder($callbacks)
    {
        $this->assertSentCount(count($callbacks));

        foreach ($callbacks as $index => $url) {
            $callback = is_callable($url) ? $url : function ($request) use ($url) {
                return $request->url() == $url;
            };

            PHPUnit::assertTrue($callback(
                $this->factory->getRecorded()[$index][0],
                $this->factory->getRecorded()[$index][1]
            ), 'An expected request (#'.($index + 1).') was not recorded.');
        }
    }
}


================================================
FILE: src/Xml/XMLSerializer.php
================================================
<?php

namespace CodeDredd\Soap\Xml;

use DOMDocument;
use DOMNode;
use Illuminate\Support\Str;
use SimpleXMLElement;

/**
 * Class XMLSerializer.
 */
class XMLSerializer
{
    /**
     * Recursive function to turn a DOMDocument element to an array.
     *
     * @param  DOMDocument|DomNode  $node  the document (might also be a DOMElement/DOMNode?)
     * @return array
     */
    public static function domNodeToArray($node)
    {
        $output = [];
        switch ($node->nodeType) {
            case XML_CDATA_SECTION_NODE:
            case XML_TEXT_NODE:
                $output = trim($node->textContent);

                break;
            case XML_ELEMENT_NODE:
                for ($i = 0, $m = $node->childNodes->length; $i < $m; $i++) {
                    $child = $node->childNodes->item($i);
                    $v = self::domNodeToArray($child);
                    if (isset($child->tagName)) {
                        $t = Str::after($child->tagName, ':');
                        if (! isset($output[$t])) {
                            $output[$t] = [];
                        }
                        $output[$t][] = $v;
                    } elseif ($v || $v === '0') {
                        $output = is_array($v) ? json_encode($v) : $v;
                    }
                }
                if ($node->attributes->length && ! is_array($output)) { // Has attributes but isn't an array
                    $output = ['@content' => $output]; // Change output into an array.
                }
                if (is_array($output)) {
                    if ($node->attributes->length) {
                        $a = [];
                        foreach ($node->attributes as $attrName => $attrNode) {
                            $a[$attrName] = (string) $attrNode->value;
                        }
                        $output['@attributes'] = $a;
                    }
                    foreach ($output as $t => $v) {
                        if (is_array($v) && count($v) == 1 && $t != '@attributes') {
                            $output[$t] = $v[0];
                        }
                    }
                }

                break;
        }

        return $output;
    }

    /**
     * Return a valid SOAP Xml.
     *
     * @param  array  $array
     * @return mixed
     */
    public static function arrayToSoapXml(array $array)
    {
        $array = [
            'SOAP-ENV:Body' => $array,
        ];
        $xml = new SimpleXMLElement('<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"/>');
        self::addArrayToXml($array, $xml);

        return $xml->asXML();
    }

    public static function addArrayToXml(array $array, &$xml)
    {
        foreach ($array as $key => $value) {
            if (is_array($value)) {
                if (is_int($key)) {
                    $key = 'node';
                }
                $label = $xml->addChild($key);
                self::addArrayToXml($value, $label);
            } else {
                $xml->addChild($key, $value);
            }
        }
    }
}


================================================
FILE: tests/Fixtures/CustomSoapClient.php
================================================
<?php

namespace CodeDredd\Soap\Tests\Fixtures;

use CodeDredd\Soap\SoapClient;

class CustomSoapClient extends SoapClient
{
    public function buildClient(string $setup = '')
    {
        $this->baseWsdl(__DIR__.'/Wsdl/weather.wsdl');
        $this->withGuzzleClientOptions([
            'handler' => $this->buildHandlerStack(),
        ]);
        $this->refreshEngine();
        $this->isClientBuilded = true;

        return $this;
    }
}


================================================
FILE: tests/Fixtures/Responses/SoapFault.xml
================================================
<?xml version='1.0' encoding='UTF-8'?>
<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope'>
    <soap:Body>
        <soap:Fault>
            <faultcode>soap:VersionMismatch</faultcode>
            <faultstring xml:lang='en'>
                Message was not SOAP 1.1 compliant
            </faultstring>
            <faultactor>
                http://sample.org.ocm/jws/authnticator
            </faultactor>
        </soap:Fault>
    </soap:Body>
</soap:Envelope>


================================================
FILE: tests/Fixtures/Wsdl/weather.wsdl
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
Cached version of: http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl
-->
<wsdl:definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://ws.cdyne.com/WeatherWS/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" targetNamespace="http://ws.cdyne.com/WeatherWS/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
    <wsdl:types>
        <s:schema elementFormDefault="qualified" targetNamespace="http://ws.cdyne.com/WeatherWS/">
            <s:element name="GetWeatherInformation">
                <s:complexType />
            </s:element>
            <s:element name="GetWeatherInformationResponse">
                <s:complexType>
                    <s:sequence>
                        <s:element minOccurs="0" maxOccurs="1" name="GetWeatherInformationResult" type="tns:ArrayOfWeatherDescription" />
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:complexType name="ArrayOfWeatherDescription">
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="unbounded" name="WeatherDescription" type="tns:WeatherDescription" />
                </s:sequence>
            </s:complexType>
            <s:complexType name="WeatherDescription">
                <s:sequence>
                    <s:element minOccurs="1" maxOccurs="1" name="WeatherID" type="s:short" />
                    <s:element minOccurs="0" maxOccurs="1" name="Description" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="PictureURL" type="s:string" />
                </s:sequence>
            </s:complexType>
            <s:element name="GetCityForecastByZIP">
                <s:complexType>
                    <s:sequence>
                        <s:element minOccurs="0" maxOccurs="1" name="ZIP" type="s:string" />
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:element name="GetCityForecastByZIPResponse">
                <s:complexType>
                    <s:sequence>
                        <s:element minOccurs="0" maxOccurs="1" name="GetCityForecastByZIPResult" type="tns:ForecastReturn" />
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:complexType name="ForecastReturn">
                <s:sequence>
                    <s:element minOccurs="1" maxOccurs="1" name="Success" type="s:boolean" />
                    <s:element minOccurs="0" maxOccurs="1" name="ResponseText" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="State" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="City" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="WeatherStationCity" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="ForecastResult" type="tns:ArrayOfForecast" />
                </s:sequence>
            </s:complexType>
            <s:complexType name="ArrayOfForecast">
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="unbounded" name="Forecast" nillable="true" type="tns:Forecast" />
                </s:sequence>
            </s:complexType>
            <s:complexType name="Forecast">
                <s:sequence>
                    <s:element minOccurs="1" maxOccurs="1" name="Date" type="s:dateTime" />
                    <s:element minOccurs="1" maxOccurs="1" name="WeatherID" type="s:short" />
                    <s:element minOccurs="0" maxOccurs="1" name="Desciption" type="s:string" />
                    <s:element minOccurs="1" maxOccurs="1" name="Temperatures" type="tns:temp" />
                    <s:element minOccurs="1" maxOccurs="1" name="ProbabilityOfPrecipiation" type="tns:POP" />
                </s:sequence>
            </s:complexType>
            <s:complexType name="temp">
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="1" name="MorningLow" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="DaytimeHigh" type="s:string" />
                </s:sequence>
            </s:complexType>
            <s:complexType name="POP">
                <s:sequence>
                    <s:element minOccurs="0" maxOccurs="1" name="Nighttime" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Daytime" type="s:string" />
                </s:sequence>
            </s:complexType>
            <s:element name="GetCityWeatherByZIP">
                <s:complexType>
                    <s:sequence>
                        <s:element minOccurs="0" maxOccurs="1" name="ZIP" type="s:string" />
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:element name="GetCityWeatherByZIPResponse">
                <s:complexType>
                    <s:sequence>
                        <s:element minOccurs="1" maxOccurs="1" name="GetCityWeatherByZIPResult" type="tns:WeatherReturn" />
                    </s:sequence>
                </s:complexType>
            </s:element>
            <s:complexType name="WeatherReturn">
                <s:sequence>
                    <s:element minOccurs="1" maxOccurs="1" name="Success" type="s:boolean" />
                    <s:element minOccurs="0" maxOccurs="1" name="ResponseText" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="State" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="City" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="WeatherStationCity" type="s:string" />
                    <s:element minOccurs="1" maxOccurs="1" name="WeatherID" type="s:short" />
                    <s:element minOccurs="0" maxOccurs="1" name="Description" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Temperature" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="RelativeHumidity" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Wind" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Pressure" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Visibility" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="WindChill" type="s:string" />
                    <s:element minOccurs="0" maxOccurs="1" name="Remarks" type="s:string" />
                </s:sequence>
            </s:complexType>
            <s:element name="ArrayOfWeatherDescription" nillable="true" type="tns:ArrayOfWeatherDescription" />
            <s:element name="ForecastReturn" nillable="true" type="tns:ForecastReturn" />
            <s:element name="WeatherReturn" type="tns:WeatherReturn" />
        </s:schema>
    </wsdl:types>
    <wsdl:message name="GetWeatherInformationSoapIn">
        <wsdl:part name="parameters" element="tns:GetWeatherInformation" />
    </wsdl:message>
    <wsdl:message name="GetWeatherInformationSoapOut">
        <wsdl:part name="parameters" element="tns:GetWeatherInformationResponse" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPSoapIn">
        <wsdl:part name="parameters" element="tns:GetCityForecastByZIP" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPSoapOut">
        <wsdl:part name="parameters" element="tns:GetCityForecastByZIPResponse" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPSoapIn">
        <wsdl:part name="parameters" element="tns:GetCityWeatherByZIP" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPSoapOut">
        <wsdl:part name="parameters" element="tns:GetCityWeatherByZIPResponse" />
    </wsdl:message>
    <wsdl:message name="GetWeatherInformationHttpGetIn" />
    <wsdl:message name="GetWeatherInformationHttpGetOut">
        <wsdl:part name="Body" element="tns:ArrayOfWeatherDescription" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPHttpGetIn">
        <wsdl:part name="ZIP" type="s:string" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPHttpGetOut">
        <wsdl:part name="Body" element="tns:ForecastReturn" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPHttpGetIn">
        <wsdl:part name="ZIP" type="s:string" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPHttpGetOut">
        <wsdl:part name="Body" element="tns:WeatherReturn" />
    </wsdl:message>
    <wsdl:message name="GetWeatherInformationHttpPostIn" />
    <wsdl:message name="GetWeatherInformationHttpPostOut">
        <wsdl:part name="Body" element="tns:ArrayOfWeatherDescription" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPHttpPostIn">
        <wsdl:part name="ZIP" type="s:string" />
    </wsdl:message>
    <wsdl:message name="GetCityForecastByZIPHttpPostOut">
        <wsdl:part name="Body" element="tns:ForecastReturn" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPHttpPostIn">
        <wsdl:part name="ZIP" type="s:string" />
    </wsdl:message>
    <wsdl:message name="GetCityWeatherByZIPHttpPostOut">
        <wsdl:part name="Body" element="tns:WeatherReturn" />
    </wsdl:message>
    <wsdl:portType name="WeatherSoap">
        <wsdl:operation name="GetWeatherInformation">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Gets Information for each WeatherID</wsdl:documentation>
            <wsdl:input message="tns:GetWeatherInformationSoapIn" />
            <wsdl:output message="tns:GetWeatherInformationSoapOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City Forecast Over the Next 7 Days, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityForecastByZIPSoapIn" />
            <wsdl:output message="tns:GetCityForecastByZIPSoapOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City's Weather, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityWeatherByZIPSoapIn" />
            <wsdl:output message="tns:GetCityWeatherByZIPSoapOut" />
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:portType name="WeatherHttpGet">
        <wsdl:operation name="GetWeatherInformation">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Gets Information for each WeatherID</wsdl:documentation>
            <wsdl:input message="tns:GetWeatherInformationHttpGetIn" />
            <wsdl:output message="tns:GetWeatherInformationHttpGetOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City Forecast Over the Next 7 Days, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityForecastByZIPHttpGetIn" />
            <wsdl:output message="tns:GetCityForecastByZIPHttpGetOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City's Weather, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityWeatherByZIPHttpGetIn" />
            <wsdl:output message="tns:GetCityWeatherByZIPHttpGetOut" />
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:portType name="WeatherHttpPost">
        <wsdl:operation name="GetWeatherInformation">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Gets Information for each WeatherID</wsdl:documentation>
            <wsdl:input message="tns:GetWeatherInformationHttpPostIn" />
            <wsdl:output message="tns:GetWeatherInformationHttpPostOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City Forecast Over the Next 7 Days, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityForecastByZIPHttpPostIn" />
            <wsdl:output message="tns:GetCityForecastByZIPHttpPostOut" />
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">Allows you to get your City's Weather, which is updated hourly. U.S. Only</wsdl:documentation>
            <wsdl:input message="tns:GetCityWeatherByZIPHttpPostIn" />
            <wsdl:output message="tns:GetCityWeatherByZIPHttpPostOut" />
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="WeatherSoap" type="tns:WeatherSoap">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="GetWeatherInformation">
            <soap:operation soapAction="http://ws.cdyne.com/WeatherWS/GetWeatherInformation" style="document" />
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <soap:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP" style="document" />
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <soap:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP" style="document" />
            <wsdl:input>
                <soap:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:binding name="WeatherSoap12" type="tns:WeatherSoap">
        <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
        <wsdl:operation name="GetWeatherInformation">
            <soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetWeatherInformation" style="document" />
            <wsdl:input>
                <soap12:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityForecastByZIP" style="document" />
            <wsdl:input>
                <soap12:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <soap12:operation soapAction="http://ws.cdyne.com/WeatherWS/GetCityWeatherByZIP" style="document" />
            <wsdl:input>
                <soap12:body use="literal" />
            </wsdl:input>
            <wsdl:output>
                <soap12:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:binding name="WeatherHttpGet" type="tns:WeatherHttpGet">
        <http:binding verb="GET" />
        <wsdl:operation name="GetWeatherInformation">
            <http:operation location="/GetWeatherInformation" />
            <wsdl:input>
                <http:urlEncoded />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <http:operation location="/GetCityForecastByZIP" />
            <wsdl:input>
                <http:urlEncoded />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <http:operation location="/GetCityWeatherByZIP" />
            <wsdl:input>
                <http:urlEncoded />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:binding name="WeatherHttpPost" type="tns:WeatherHttpPost">
        <http:binding verb="POST" />
        <wsdl:operation name="GetWeatherInformation">
            <http:operation location="/GetWeatherInformation" />
            <wsdl:input>
                <mime:content type="application/x-www-form-urlencoded" />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityForecastByZIP">
            <http:operation location="/GetCityForecastByZIP" />
            <wsdl:input>
                <mime:content type="application/x-www-form-urlencoded" />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
        <wsdl:operation name="GetCityWeatherByZIP">
            <http:operation location="/GetCityWeatherByZIP" />
            <wsdl:input>
                <mime:content type="application/x-www-form-urlencoded" />
            </wsdl:input>
            <wsdl:output>
                <mime:mimeXml part="Body" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="Weather">
        <wsdl:port name="WeatherSoap" binding="tns:WeatherSoap">
            <soap:address location="http://wsf.cdyne.com/WeatherWS/Weather.asmx" />
        </wsdl:port>
        <wsdl:port name="WeatherSoap12" binding="tns:WeatherSoap12">
            <soap12:address location="http://wsf.cdyne.com/WeatherWS/Weather.asmx" />
        </wsdl:port>
        <wsdl:port name="WeatherHttpGet" binding="tns:WeatherHttpGet">
            <http:address location="http://wsf.cdyne.com/WeatherWS/Weather.asmx" />
        </wsdl:port>
        <wsdl:port name="WeatherHttpPost" binding="tns:WeatherHttpPost">
            <http:address location="http://wsf.cdyne.com/WeatherWS/Weather.asmx" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>


================================================
FILE: tests/TestCase.php
================================================
<?php

namespace CodeDredd\Soap\Tests;

use CodeDredd\Soap\Facades\Soap;
use CodeDredd\Soap\SoapServiceProvider;
use Orchestra\Testbench\TestCase as OrchestraTestCase;
use Spatie\LaravelRay\RayServiceProvider;

abstract class TestCase extends OrchestraTestCase
{
    protected $loadEnvironmentVariables = true;

    /**
     * Load package service provider.
     *
     * @param  \Illuminate\Foundation\Application  $app
     * @return string[]
     */
    protected function getPackageProviders($app)
    {
        return [
            SoapServiceProvider::class,
            RayServiceProvider::class,
        ];
    }

    /**
     * Load package alias.
     *
     * @param  \Illuminate\Foundation\Application  $app
     * @return array
     */
    protected function getPackageAliases($app)
    {
        return [
            'Soap' => Soap::class,
        ];
    }

    /**
     * Define environment setup.
     *
     * @param  \Illuminate\Foundation\Application  $app
     * @return void
     */
    protected function getEnvironmentSetUp($app)
    {
        // Setup default wsse
        $app['config']->set('soap.clients.laravel_soap', [
            'base_wsdl' => __DIR__.'/Fixtures/Wsdl/weather.wsdl',
            'with_wsse' => [
                'user_token_name' => 'username',
                'user_token_password' => 'password',
            ],
        ]);
        $app['config']->set('soap.code.path', __DIR__.'/app');
        $app['config']->set('soap.code.namespace', 'App\\Soap');
    }
}


================================================
FILE: tests/Unit/Client/ResponseTest.php
================================================
<?php

namespace CodeDredd\Soap\Tests\Unit\Client;

use CodeDredd\Soap\Client\Response;
use CodeDredd\Soap\Tests\TestCase;
use GuzzleHttp\Psr7\Response as Psr7Response;

class ResponseTest extends TestCase
{
    public function testBodyFromSoapError()
    {
        $xml = file_get_contents(dirname(__DIR__, 2).'/Fixtures/Responses/SoapFault.xml');
        $soapResponse = new Response(new Psr7Response(400, [], $xml));
        self::assertEquals('Message was not SOAP 1.1 compliant', $soapResponse->body());
    }
}


================================================
FILE: tests/Unit/Commands/MakeClientCommandTest.php
================================================
<?php

namespace CodeDredd\Soap\Tests\Unit\Commands;

use CodeDredd\Soap\Tests\TestCase;

class MakeClientCommandTest extends TestCase
{
    public function testConsoleCommand()
    {
        $this->markTestSkipped('wsdl2Php package has been removed. Needs refactoring');
        $this->artisan('soap:make:client --dry-run')
            ->expectsQuestion('Please type the wsdl or the name of your client configuration if u have defined one in the config "soap.php"', 'laravel_soap');
    }
}


================================================
FILE: tests/Unit/Commands/MakeValidationCommandTest.php
================================================
<?php

namespace CodeDredd\Soap\Tests\Unit\Commands;

use CodeDredd\Soap\Tests\TestCase;

class MakeValidationCommandTest extends TestCase
{
    public function testConsoleCommand()
    {
        $this->markTestSkipped('wsdl2Php package has been removed. Needs refactoring');
        $this->artisan('soap:make:validation --dry-run')
            ->expectsQuestion('Please type the wsdl or the name of your client configuration if u have defined one in the config "soap.php"', 'laravel_soap')
            ->expectsConfirmation('Do you want to generate for every client method a validation?', 'no')
            ->expectsQuestion('Which method do you want to generate?', 'GetWeatherInformation');
    }
}


================================================
FILE: tests/Unit/Middleware/CisDhlMiddlewareTest.php
================================================
<?php

namespace CodeDredd\Soap\Tests\Unit\Middleware;

use CodeDredd\Soap\Client\Request;
use CodeDredd\Soap\Facades\Soap;
use CodeDredd\Soap\Tests\TestCase;
use Illuminate\Support\Str;

class CisDhlMiddlewareTest extends TestCase
{
    public function testCisDHLMiddleware()
    {
        Soap::fake();
        $client = Soap::withCisDHLAuth('test', 'dhl')->baseWsdl(dirname(__DIR__, 2).'/Fixtures/Wsdl/weather.wsdl');
        $response = $client->call('GetWeatherInformation');
        Soap::assertSent(function (Request $request) {
            return Str::contains($request->xmlContent(), '<cis:Authentification');
        });

        self::assertTrue($response->ok());
    }
}


================================================
FILE: tests/Unit/SoapClientTest.php
================================================
<?php

namespace CodeDredd\Soap\Tests\Unit;

use CodeDredd\Soap\Client\Events\ConnectionFailed;
use CodeDredd\Soap\Client\Events\RequestSending;
use CodeDredd\Soap\Client\Events\ResponseReceived;
use CodeDredd\Soap\Client\Request;
use CodeDredd\Soap\Client\Response;
use CodeDredd\Soap\Facades\Soap;
use CodeDredd\Soap\SoapClient;
use CodeDredd\Soap\SoapFactory;
use CodeDredd\Soap\Tests\Fixtures\CustomSoapClient;
use CodeDredd\Soap\Tests\TestCase;
use GuzzleHttp\RedirectMiddleware;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Str;
use JetBrains\PhpStorm\ArrayShape;

class SoapClientTest extends TestCase
{
    protected function setUp(): void
    {
        parent::setUp();
    }

    /**
     * Test that a "fake" terminal returns an instance of BuilderFake.
     *
     * @return void
     */
    public function testSimpleCall()
    {
        Soap::fake();
        Event::fake();
        Soap::assertNothingSent();
        $response = Soap::baseWsdl(dirname(__DIR__, 1).'/Fixtures/Wsdl/weather.wsdl')
            ->call('GetWeatherInformation');
        self::assertTrue($response->ok());
        Soap::assertSent(function (Request $request) {
            return $request->action() === 'GetWeatherInformation';
        });
        Soap::assertNotSent(function (Request $request) {
            return $request->action() === 'GetCityWeatherByZIPSoapOut';
        });
        Soap::assertActionCalled('GetWeatherInformation');
    }

    public function testMagicCallByConfig()
    {
        Soap::fake();
        Event::fake();
        $response = Soap::buildClient('laravel_soap')->GetWeatherInformation();
        self::assertTrue($response->ok());
    }

    public function testWsseWithWsaCall()
    {
        Soap::fake();
        ray()->showSoapClientRequests();
        $client = Soap::baseWsdl(dirname(__DIR__, 1).'/Fixtures/Wsdl/weather.wsdl')->withWsse([
            'userTokenName' => 'Test',
            'userTokenPassword' => 'passwordTest',
            'mustUnderstand' => false,
        ])->withWsa();
        $response = $client->GetWeatherInformation();
        Soap::assertSent(function (Request $request) {
            return ! Str::contains($request->xmlContent(), 'mustUnderstand');
        });
        self::assertTrue($response->ok());
    }

    public function testWsseWithWsa2005Call()
    {
        Soap::fake();
        ray()->showSoapClientRequests();
        $client = Soap::baseWsdl(dirname(__DIR__, 1).'/Fixtures/Wsdl/weather.wsdl')->withWsse([
            'userTokenName' => 'Test',
            'userTokenPassword' => 'passwordTest',
            'mustUnderstand' => false,
        ])->withWsa2005();
        $response = $client->GetWeatherInformation();
        Soap::assertSent(function (Request $request) {
            return ! Str::contains($request->xmlContent(), 'mustUnderstand');
        });
        self::assertTrue($response->ok());
    }

    public function testArrayAccessResponse()
    {
        Soap::fakeSequence()->push('test');
        Event::fake();
        $response = Soap::buildClient('laravel_soap')->GetWeatherInformation()['response'];
        self::assertEquals('test', $response);
    }

    public function testRequestWithArguments()
    {
        Soap::fake();
        Event::fake();

        $arguments = [
            'prename' => 'Corona',
            'lastname' => 'Pandemic',
        ];

        /** @var Response $response */
        $response = Soap::buildClient('laravel_soap')->Submit_User($arguments);

        Event::assertDispatched(RequestSending::class);
        Event::assertDispatched(ResponseReceived::class);
        self::assertTrue($response->ok());
        Soap::assertSent(function (Request $request) use ($arguments) {
            return $request->arguments() === $arguments &&
                $request->action() === 'Submit_User';
        });
    }

    public function testSequenceFake()
    {
        $responseFake = ['user' => 'test'];
        $responseFake2 = ['user' => 'test2'];
        Event::fake();
        Soap::fakeSequence()
            ->push($responseFake)
            ->whenEmpty(Soap::response($responseFake2));
        $client = Soap::buildClient('laravel_soap');
        $response = $client->Get_User();
        $response2 = $client->Get_User();
        $response3 = $client->Get_User();
        self::assertTrue($response->ok());
        self::assertEquals($responseFake, $response->json());
        self::assertEquals($responseFake2, $response2->json());
        self::assertEquals($responseFake2, $response3->json());

        Soap::assertSentCount(3);
    }

    /**
     * @dataProvider soapActionProvider
     *
     * @param $action
     * @param $fake
     * @param $exspected
     */
    public function testSoapFake($action, $fake, $exspected)
    {
        $fake = collect($fake)->map(function ($item) {
            return Soap::response($item);
        })->all();

        Soap::fake($fake);
        Event::fake();
        Event::assertNotDispatched(RequestSending::class);
        Event::assertNotDispatched(ResponseReceived::class);
        $response = Soap::baseWsdl(dirname(__DIR__, 1).'/Fixtures/Wsdl/weather.wsdl')
            ->call($action);
        Event::assertDispatched(RequestSending::class);
        Event::assertDispatched(ResponseReceived::class);
        Event::assertNotDispatched(ConnectionFailed::class);
        self::assertEquals($exspected, $response->json());
    }

    #[ArrayShape([
        'without_fake_array' => 'array',
        'with_fake_array_wrong_method' => 'array',
        'with_fake_array' => 'array',
        'with_fake_string' => 'array',
    ])]
    public static function soapActionProvider(): array
    {
        $fakeResponse = [
            'GetWeatherInformation' => [
                'Response_Data' => [
                    'Users' => [
                        [
                            'name' => 'test',
                            'field' => 'bla',
                        ],
                    ],
                ],
            ],
            'GetCityForecastByZIP' => 'Test',
        ];

        return [
            'without_fake_array' => ['GetCityWeatherByZIP', null, null],
            'with_fake_array_wrong_method' => ['GetCityWeatherByZIP', $fakeResponse, null],
            'with_fake_array' => ['GetWeatherInformation', $fakeResponse, $fakeResponse['GetWeatherInformation']],
            'with_fake_string' => ['GetCityForecastByZIP', $fakeResponse, ['response' => 'Test']],
        ];
    }

    public function testSoapOptions(): void
    {
        Soap::fake();
        Event::fake();
        $client = Soap::withOptions(['soap_version' => SOAP_1_2])
            ->baseWsdl(dirname(__DIR__, 1).'/Fixtures/Wsdl/weather.wsdl');
        $response = $client->call('GetWeatherInformation');
        self::assertTrue($response->ok());
        Soap::assertSent(function (Request $request) {
            return Str::contains(
                $request->getRequest()->getHeaderLine('Content-Type'),
                'application/soap+xml; charset="utf-8"'
            );
        });
        Soap::assertActionCalled('GetWeatherInformation');
    }

    public function testRealSoapCall(): void
    {
        $this->markTestSkipped('Real Soap Call Testing. Comment the line out for testing');
        ray()->showSoapClientRequests();
        // location has to be set because the wsdl has a wrong location declaration
        $client = Soap::baseWsdl('https://www.w3schools.com/xml/tempconvert.asmx?wsdl')
            ->withOptions([
                'soap_version' => SOAP_1_2,
                'location' => 'https://www.w3schools.com/xml/tempconvert.asmx?wsdl',
            ]);
        $result = $client->call('FahrenheitToCelsius', [
            'Fahrenheit' => 75,
        ]);
        self::assertArrayHasKey('FahrenheitToCelsiusResult', $result->json());

        $result = $client->FahrenheitToCelsius([
            'Fahrenheit' => 75,
        ]);
        self::assertArrayHasKey('FahrenheitToCelsiusResult', $result->json());
    }

    public function testRealSoapCallBank(): void
    {
        $this->markTestSkipped('Real Soap Call Testing. Comment the line out for testing');
        ray()->showSoapClientRequests();
        // location has to be set because the wsdl has a wrong location declaration
        $client = Soap::baseWsdl('http://www.thomas-bayer.com/axis2/services/BLZService?wsdl')
            ->withOptions([
                'soap_version' => SOAP_1_2,
                'location' => 'http://www.thomas-bayer.com/axis2/services/BLZService?wsdl',
            ]);
        $result = $client->call('getBank', [
            'blz' => '74120071',
        ]);
        dd($result->json());
        self::assertArrayHasKey('FahrenheitToCelsiusResult', $result->json());

        $result = $client->FahrenheitToCelsius([
            'Fahrenheit' => 75,
        ]);
        self::assertArrayHasKey('FahrenheitToCelsiusResult', $result->json());
    }

    /**
     * @dataProvider soapHeaderProvider
     *
     * @param $header
     * @param $exspected
     */
    public function testSoapWithDifferentHeaders($header, $exspected): void
    {
        Soap::fake();
        Event::fake();
        $client = Soap::withHeaders($header)->baseWsdl(dirname(__DIR__, 1).'/Fixtures/Wsdl/weather.wsdl');
        $response = $client->call('GetWeatherInformation');
        Soap::assertSent(function (Request $request) use ($exspected) {
            return $request->getRequest()->getHeaderLine('test') === $exspected;
        });
        self::assertTrue($response->ok());
        Soap::assertActionCalled('GetWeatherInformation');
    }

    public function testArgumentsCanBeCalledTwice(): void
    {
        Soap::fake();
        Event::fake();
        Soap::assertNothingSent();
        $response = Soap::baseWsdl(dirname(__DIR__, 1).'/Fixtures/Wsdl/weather.wsdl')
            ->call('GetWeatherInformation');
        self::assertTrue($response->ok());
        Soap::assertSent(function (Request $request) {
            return $request->arguments() === $request->arguments();
        });
    }

    public static function soapHeaderProvider(): array
    {
        $header = [
            'test' => 'application/soap+xml; charset="utf-8"',
        ];

        return [
            'without_header' => [[], ''],
            'with_header' => [$header, $header['test']],
        ];
    }

    public function testSoapClientClassMayBeCustomized(): void
    {
        Soap::fake();
        Event::fake();
        $client = Soap::buildClient('laravel_soap');
        $this->assertInstanceOf(SoapClient::class, $client);
        SoapFactory::useClientClass(CustomSoapClient::class);
        $client = Soap::buildClient('laravel_soap');
        $this->assertInstanceOf(CustomSoapClient::class, $client);
    }

    public function testHandlerOptions(): void
    {
        Soap::fake();
        Event::fake();
        $client = Soap::baseWsdl(dirname(__DIR__, 1).'/Fixtures/Wsdl/weather.wsdl');
        $response = $client->call('GetWeatherInformation');
        self::assertTrue($response->ok());
        self::assertEquals(true, $client->getClient()->getConfig()['verify']);
        $client = $client->withGuzzleClientOptions([
            'allow_redirects' => RedirectMiddleware::$defaultSettings,
            'http_errors' => true,
            'decode_content' => true,
            'verify' => false,
            'cookies' => false,
            'idn_conversion' => false,
        ]);
        $response = $client->call('GetWeatherInformation');
        self::assertTrue($response->ok());
        self::assertEquals(false, $client->getClient()->getConfig()['verify']);
    }
}


================================================
FILE: tests/Unit/Xml/XmlSerializerTest.php
================================================
<?php

namespace CodeDredd\Soap\Tests\Unit\Xml;

use CodeDredd\Soap\Tests\TestCase;
use CodeDredd\Soap\Xml\XMLSerializer;

class XmlSerializerTest extends TestCase
{
    protected $xml = <<<'XML'
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
        <SOAP-ENV:prename>Code</SOAP-ENV:prename>
        <SOAP-ENV:lastname>dredd</SOAP-ENV:lastname>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XML;

    protected $array = [
        'prename' => 'Code',
        'lastname' => 'dredd',
    ];

    public function testArrayToSoapXml()
    {
        $soapXml = XMLSerializer::arrayToSoapXml($this->array);

        self::assertXmlStringEqualsXmlString($this->xml, $soapXml);
    }
}
Download .txt
gitextract_95jpnp3a/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── CODE_OF_CONDUCT.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   ├── config.yml
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   ├── pr-labeler.yml
│   ├── release-drafter.yml
│   ├── stale.yml
│   └── workflows/
│       ├── dependabot-auto-merge.yml
│       ├── mkdocs.yml
│       ├── php-cs-fixer.yml
│       ├── phpstan.yml
│       ├── pr-labeler.yml
│       ├── releaseDrafter.yml
│       └── tests.yml
├── .gitignore
├── .mailmap
├── .php_cs.dist.php
├── .styleci.yml
├── LICENSE
├── README.md
├── composer.json
├── config/
│   └── soap.php
├── mkdocs.yml
├── src/
│   ├── Client/
│   │   ├── Events/
│   │   │   ├── ConnectionFailed.php
│   │   │   ├── RequestSending.php
│   │   │   └── ResponseReceived.php
│   │   ├── Request.php
│   │   ├── Response.php
│   │   └── ResponseSequence.php
│   ├── Driver/
│   │   └── ExtSoap/
│   │       └── ExtSoapEngineFactory.php
│   ├── Exceptions/
│   │   ├── NotFoundConfigurationException.php
│   │   ├── RequestException.php
│   │   └── SoapException.php
│   ├── Facades/
│   │   └── Soap.php
│   ├── Faker/
│   │   ├── EngineFaker.php
│   │   └── fake.wsdl
│   ├── Middleware/
│   │   ├── CisDhlMiddleware.php
│   │   └── WsseMiddleware.php
│   ├── Ray/
│   │   ├── LaravelRay.php
│   │   └── SoapClientWatcher.php
│   ├── SoapClient.php
│   ├── SoapFactory.php
│   ├── SoapServiceProvider.php
│   ├── SoapTesting.php
│   └── Xml/
│       └── XMLSerializer.php
└── tests/
    ├── Fixtures/
    │   ├── CustomSoapClient.php
    │   ├── Responses/
    │   │   └── SoapFault.xml
    │   └── Wsdl/
    │       └── weather.wsdl
    ├── TestCase.php
    └── Unit/
        ├── Client/
        │   └── ResponseTest.php
        ├── Commands/
        │   ├── MakeClientCommandTest.php
        │   └── MakeValidationCommandTest.php
        ├── Middleware/
        │   └── CisDhlMiddlewareTest.php
        ├── SoapClientTest.php
        └── Xml/
            └── XmlSerializerTest.php
Download .txt
SYMBOL INDEX (215 symbols across 29 files)

FILE: src/Client/Events/ConnectionFailed.php
  class ConnectionFailed (line 7) | class ConnectionFailed
    method __construct (line 22) | public function __construct(Request $request)

FILE: src/Client/Events/RequestSending.php
  class RequestSending (line 7) | class RequestSending
    method __construct (line 20) | public function __construct(Request $request)

FILE: src/Client/Events/ResponseReceived.php
  class ResponseReceived (line 8) | class ResponseReceived
    method __construct (line 31) | public function __construct(Request $request, Response $response)

FILE: src/Client/Request.php
  class Request (line 22) | class Request
    method __construct (line 42) | public function __construct($request)
    method action (line 50) | public function action(): string
    method getRequest (line 55) | public function getRequest(): RequestInterface
    method url (line 63) | public function url(): string
    method hasHeader (line 75) | public function hasHeader($key, $value = null)
    method hasHeaders (line 98) | public function hasHeaders($headers)
    method header (line 119) | public function header($key)
    method headers (line 129) | public function headers()
    method body (line 139) | public function body()
    method xmlContent (line 147) | public function xmlContent(): string
    method arguments (line 157) | public function arguments(): array
    method withData (line 176) | public function withData(array $data)
    method toPsrRequest (line 188) | public function toPsrRequest()
    method offsetExists (line 201) | public function offsetExists(string $offset): bool
    method offsetGet (line 214) | public function offsetGet($offset): mixed
    method offsetSet (line 228) | public function offsetSet($offset, $value): void
    method offsetUnset (line 241) | public function offsetUnset($offset): void

FILE: src/Client/Response.php
  class Response (line 22) | class Response implements ResultInterface, ArrayAccess
    method __construct (line 46) | public function __construct(ResponseInterface $response)
    method setCookies (line 51) | public function setCookies(array $cookies): void
    method setTransferStats (line 56) | public function setTransferStats(?TransferStats $transferStats): void
    method fromSoapResponse (line 61) | public static function fromSoapResponse(mixed $result, int $status = 2...
    method fromSoapFault (line 66) | public static function fromSoapFault(\SoapFault $soapFault): Response
    method toPsrResponse (line 74) | public function toPsrResponse(): ResponseInterface
    method object (line 84) | public function object(): object
    method collect (line 95) | public function collect(string $key = null): Collection
    method body (line 107) | public function body(bool $transformXml = true, bool $sanitizeXmlFault...
    method successful (line 127) | public function successful(): bool
    method status (line 137) | public function status(): int
    method ok (line 145) | public function ok(): bool
    method failed (line 153) | public function failed(): bool
    method redirect (line 161) | public function redirect(): bool
    method throw (line 173) | public function throw()
    method throwIf (line 196) | public function throwIf(bool $condition): Response|static
    method serverError (line 204) | public function serverError(): bool
    method clientError (line 212) | public function clientError(): bool
    method onError (line 223) | public function onError(callable $callback)
    method handlerStats (line 237) | public function handlerStats()
    method json (line 245) | public function json($key = null, $default = null): ?array
    method header (line 261) | public function header(string $header): string
    method headers (line 269) | public function headers(): array
    method offsetExists (line 280) | public function offsetExists($offset): bool
    method offsetGet (line 291) | public function offsetGet($offset): mixed
    method offsetSet (line 305) | public function offsetSet($offset, $value): void
    method offsetUnset (line 318) | public function offsetUnset($offset): void
    method __toString (line 328) | public function __toString()
    method __call (line 340) | public function __call($method, $parameters)

FILE: src/Client/ResponseSequence.php
  class ResponseSequence (line 8) | class ResponseSequence
    method __construct (line 37) | public function __construct(array $responses)
    method push (line 50) | public function push($body = '', int $status = 200, array $headers = [])
    method pushResponse (line 63) | public function pushResponse($response)
    method pushStatus (line 77) | public function pushStatus(int $status, array $headers = [])
    method pushFile (line 92) | public function pushFile(string $filePath, int $status = 200, array $h...
    method dontFailWhenEmpty (line 106) | public function dontFailWhenEmpty()
    method whenEmpty (line 117) | public function whenEmpty($response)
    method isEmpty (line 130) | public function isEmpty()
    method __invoke (line 140) | public function __invoke()

FILE: src/Driver/ExtSoap/ExtSoapEngineFactory.php
  class ExtSoapEngineFactory (line 13) | class ExtSoapEngineFactory
    method fromOptionsWithHandler (line 15) | public static function fromOptionsWithHandler(

FILE: src/Exceptions/NotFoundConfigurationException.php
  class NotFoundConfigurationException (line 7) | class NotFoundConfigurationException extends RuntimeException
    method fromThrowable (line 13) | public static function fromThrowable(\Throwable $throwable): self

FILE: src/Exceptions/RequestException.php
  class RequestException (line 8) | class RequestException extends Exception
    method __construct (line 23) | public function __construct(Response $response)

FILE: src/Exceptions/SoapException.php
  class SoapException (line 8) | class SoapException extends \RuntimeException
    method fromThrowable (line 14) | public static function fromThrowable(\Throwable $throwable): self

FILE: src/Facades/Soap.php
  class Soap (line 36) | class Soap extends Facade
    method getFacadeAccessor (line 41) | protected static function getFacadeAccessor()

FILE: src/Faker/EngineFaker.php
  class EngineFaker (line 18) | class EngineFaker implements Engine
    method __construct (line 24) | public function __construct(
    method request (line 34) | public function request(string $method, array $arguments)
    method getMetadata (line 42) | public function getMetadata(): Metadata

FILE: src/Middleware/CisDhlMiddleware.php
  class CisDhlMiddleware (line 19) | class CisDhlMiddleware implements Plugin
    method __construct (line 36) | public function __construct(string $user, string $signature)
    method getName (line 42) | public function getName(): string
    method handleRequest (line 47) | public function handleRequest(RequestInterface $request, callable $nex...
    method afterResponse (line 76) | public function afterResponse(ResponseInterface $response): ResponseIn...

FILE: src/Middleware/WsseMiddleware.php
  class WsseMiddleware (line 14) | class WsseMiddleware implements Plugin
    method __construct (line 30) | public function __construct(array $properties)
    method getName (line 37) | public function getName(): string
    method withTimestamp (line 42) | public function withTimestamp(int $timestamp = 3600): self
    method withAllHeadersSigned (line 49) | public function withAllHeadersSigned(): self
    method withDigitalSignMethod (line 56) | public function withDigitalSignMethod(string $digitalSignMethod): self
    method withUserToken (line 63) | public function withUserToken(string $username, string $password = nul...
    method withEncryption (line 72) | public function withEncryption(string $serverCertificateFile): self
    method withServerCertificateHasSubjectKeyIdentifier (line 80) | public function withServerCertificateHasSubjectKeyIdentifier(bool $has...
    method handleRequest (line 87) | public function handleRequest(RequestInterface $request, callable $nex...
    method beforeRequest (line 94) | public function beforeRequest(callable $handler, RequestInterface $req...
    method afterResponse (line 141) | public function afterResponse(ResponseInterface $response): ResponseIn...

FILE: src/Ray/LaravelRay.php
  class LaravelRay (line 10) | class LaravelRay
    method register (line 12) | public function register()
    method handleWatcherCallable (line 22) | protected function handleWatcherCallable(Watcher $watcher, Closure $ca...

FILE: src/Ray/SoapClientWatcher.php
  class SoapClientWatcher (line 14) | class SoapClientWatcher extends Watcher
    method register (line 16) | public function register(): void
    method handleRequest (line 41) | protected function handleRequest(Request $request)
    method handleResponse (line 54) | protected function handleResponse(Request $request, Response $response)

FILE: src/SoapClient.php
  class SoapClient (line 48) | class SoapClient
    method __construct (line 109) | public function __construct(SoapFactory $factory = null)
    method refreshWsdlProvider (line 123) | public function refreshWsdlProvider()
    method refreshPluginClient (line 130) | public function refreshPluginClient(): static
    method getPluginClient (line 137) | public function getPluginClient(): PluginClient
    method setTransport (line 142) | protected function setTransport(Transport $handler = null): static
    method withHeaders (line 160) | public function withHeaders(array $headers): static
    method getTransport (line 167) | public function getTransport(): TraceableTransport|Transport
    method getClient (line 172) | public function getClient(): Client|ClientInterface
    method withGuzzleClientOptions (line 177) | public function withGuzzleClientOptions(array ...$options): static
    method getEngine (line 185) | public function getEngine(): Engine
    method withRemoveEmptyNodes (line 193) | public function withRemoveEmptyNodes()
    method withBasicAuth (line 207) | public function withBasicAuth($username, ?string $password = null)
    method withCisDHLAuth (line 227) | public function withCisDHLAuth($user, ?string $signature = null)
    method withWsa (line 243) | public function withWsa()
    method withWsa2005 (line 255) | public function withWsa2005()
    method withWsse (line 264) | public function withWsse(array $options): static
    method withOptions (line 279) | public function withOptions(array $options)
    method mergeOptions (line 292) | public function mergeOptions(...$options)
    method debugLastSoapRequest (line 300) | public function debugLastSoapRequest(): array
    method failedValidation (line 326) | protected function failedValidation(Validator $validator)
    method __call (line 335) | public function __call($method, $parameters)
    method call (line 344) | public function call(string $method, Validator|array $arguments = []):...
    method buildResponse (line 382) | protected function buildResponse($response)
    method buildClient (line 398) | public function buildClient(string $setup = '')
    method byConfig (line 418) | public function byConfig(string $setup)
    method arrayKeysToCamel (line 443) | protected function arrayKeysToCamel(array $items)
    method buildHandlerStack (line 458) | public function buildHandlerStack()
    method buildBeforeSendingHandler (line 470) | public function buildBeforeSendingHandler(): Closure
    method runBeforeSendingCallbacks (line 482) | public function runBeforeSendingCallbacks(RequestInterface $request, a...
    method populateResponse (line 499) | protected function populateResponse(Response $response)
    method dispatchRequestSendingEvent (line 512) | protected function dispatchRequestSendingEvent()
    method dispatchResponseReceivedEvent (line 523) | protected function dispatchResponseReceivedEvent(Response $response)
    method dispatchConnectionFailedEvent (line 537) | protected function dispatchConnectionFailedEvent()
    method buildRecorderHandler (line 547) | public function buildRecorderHandler()
    method buildStubHandler (line 570) | public function buildStubHandler()
    method refreshEngine (line 593) | protected function refreshEngine()
    method refreshExtSoapOptions (line 608) | protected function refreshExtSoapOptions()
    method baseWsdl (line 621) | public function baseWsdl(string $wsdl)
    method stub (line 634) | public function stub($callback)

FILE: src/SoapFactory.php
  class SoapFactory (line 12) | class SoapFactory
    method __construct (line 60) | public function __construct()
    method __call (line 73) | public function __call($method, $parameters)
    method isRecording (line 88) | public function isRecording()
    method getResponseSequences (line 93) | public function getResponseSequences()
    method getRecorded (line 98) | public function getRecorded()
    method recordRequestResponsePair (line 110) | public function recordRequestResponsePair($request, $response)
    method fakeWsdl (line 117) | public function fakeWsdl(string $wsdl)
    method getFakeWsdl (line 124) | public function getFakeWsdl()
    method fakeSequence (line 135) | public function fakeSequence($url = '*')
    method sequence (line 148) | public function sequence(array $responses = [])
    method fake (line 159) | public function fake($callback = null)
    method record (line 193) | protected function record()
    method response (line 208) | public static function response($body = null, $status = 200, $headers ...
    method stubMethod (line 228) | public function stubMethod($method, $callback)
    method recorded (line 247) | public function recorded($callback)
    method client (line 267) | public function client()
    method useClientClass (line 277) | public static function useClientClass(string $clientClass): void

FILE: src/SoapServiceProvider.php
  class SoapServiceProvider (line 11) | class SoapServiceProvider extends PackageServiceProvider
    method configurePackage (line 13) | public function configurePackage(Package $package): void
    method packageRegistered (line 25) | public function packageRegistered()
    method packageBooted (line 31) | public function packageBooted()
    method registerService (line 41) | protected function registerService()
    method registerRay (line 48) | protected function registerRay()
    method bootRay (line 61) | protected function bootRay()

FILE: src/SoapTesting.php
  class SoapTesting (line 8) | class SoapTesting
    method __construct (line 21) | public function __construct(SoapFactory $factory = null)
    method assertNotSent (line 32) | public function assertNotSent($callback)
    method assertNothingSent (line 45) | public function assertNothingSent()
    method assertSequencesAreEmpty (line 58) | public function assertSequencesAreEmpty()
    method assertActionCalled (line 74) | public function assertActionCalled(string $action)
    method assertSent (line 87) | public function assertSent($callback)
    method assertSentCount (line 98) | public function assertSentCount(int $count): void
    method assertSentInOrder (line 109) | public function assertSentInOrder($callbacks)

FILE: src/Xml/XMLSerializer.php
  class XMLSerializer (line 13) | class XMLSerializer
    method domNodeToArray (line 21) | public static function domNodeToArray($node)
    method arrayToSoapXml (line 74) | public static function arrayToSoapXml(array $array)
    method addArrayToXml (line 85) | public static function addArrayToXml(array $array, &$xml)

FILE: tests/Fixtures/CustomSoapClient.php
  class CustomSoapClient (line 7) | class CustomSoapClient extends SoapClient
    method buildClient (line 9) | public function buildClient(string $setup = '')

FILE: tests/TestCase.php
  class TestCase (line 10) | abstract class TestCase extends OrchestraTestCase
    method getPackageProviders (line 20) | protected function getPackageProviders($app)
    method getPackageAliases (line 34) | protected function getPackageAliases($app)
    method getEnvironmentSetUp (line 47) | protected function getEnvironmentSetUp($app)

FILE: tests/Unit/Client/ResponseTest.php
  class ResponseTest (line 9) | class ResponseTest extends TestCase
    method testBodyFromSoapError (line 11) | public function testBodyFromSoapError()

FILE: tests/Unit/Commands/MakeClientCommandTest.php
  class MakeClientCommandTest (line 7) | class MakeClientCommandTest extends TestCase
    method testConsoleCommand (line 9) | public function testConsoleCommand()

FILE: tests/Unit/Commands/MakeValidationCommandTest.php
  class MakeValidationCommandTest (line 7) | class MakeValidationCommandTest extends TestCase
    method testConsoleCommand (line 9) | public function testConsoleCommand()

FILE: tests/Unit/Middleware/CisDhlMiddlewareTest.php
  class CisDhlMiddlewareTest (line 10) | class CisDhlMiddlewareTest extends TestCase
    method testCisDHLMiddleware (line 12) | public function testCisDHLMiddleware()

FILE: tests/Unit/SoapClientTest.php
  class SoapClientTest (line 20) | class SoapClientTest extends TestCase
    method setUp (line 22) | protected function setUp(): void
    method testSimpleCall (line 32) | public function testSimpleCall()
    method testMagicCallByConfig (line 49) | public function testMagicCallByConfig()
    method testWsseWithWsaCall (line 57) | public function testWsseWithWsaCall()
    method testWsseWithWsa2005Call (line 73) | public function testWsseWithWsa2005Call()
    method testArrayAccessResponse (line 89) | public function testArrayAccessResponse()
    method testRequestWithArguments (line 97) | public function testRequestWithArguments()
    method testSequenceFake (line 119) | public function testSequenceFake()
    method testSoapFake (line 146) | public function testSoapFake($action, $fake, $exspected)
    method soapActionProvider (line 164) | #[ArrayShape([
    method testSoapOptions (line 194) | public function testSoapOptions(): void
    method testRealSoapCall (line 211) | public function testRealSoapCall(): void
    method testRealSoapCallBank (line 232) | public function testRealSoapCallBank(): void
    method testSoapWithDifferentHeaders (line 260) | public function testSoapWithDifferentHeaders($header, $exspected): void
    method testArgumentsCanBeCalledTwice (line 273) | public function testArgumentsCanBeCalledTwice(): void
    method soapHeaderProvider (line 286) | public static function soapHeaderProvider(): array
    method testSoapClientClassMayBeCustomized (line 298) | public function testSoapClientClassMayBeCustomized(): void
    method testHandlerOptions (line 309) | public function testHandlerOptions(): void

FILE: tests/Unit/Xml/XmlSerializerTest.php
  class XmlSerializerTest (line 8) | class XmlSerializerTest extends TestCase
    method testArrayToSoapXml (line 25) | public function testArrayToSoapXml()
Condensed preview — 60 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (157K chars).
[
  {
    "path": ".editorconfig",
    "chars": 219,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ntrim_"
  },
  {
    "path": ".gitattributes",
    "chars": 281,
    "preview": "* text=auto\n\n/.github export-ignore\n/tests export-ignore\n.editorconfig export-ignore\n.gitattributes export-ignore\n.gitig"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 1423,
    "preview": "# Contributor Code of Conduct\n\nAs contributors and maintainers of this project, we pledge to respect all people who cont"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 192,
    "preview": "# These are supported funding model platforms\n\n# github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 1122,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n---\n\n### Reproduction\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 612,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Ask a question\n    url: https://github.com/CodeDredd/laravel-soap/d"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 514,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: feature request\nassignees: ''\n\n---\n\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 344,
    "preview": "Issue:\n\n## What I did\n\n## How to test\n\n- Does this need an update to the documentation?\n\nIf your answer is yes to any of"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 322,
    "preview": "# Please see the documentation for all configuration options:\n# https://help.github.com/github/administering-a-repositor"
  },
  {
    "path": ".github/pr-labeler.yml",
    "chars": 58,
    "preview": "feature: ['feature/*', 'feat/*']\nfix: fix/*\nchore: chore/*"
  },
  {
    "path": ".github/release-drafter.yml",
    "chars": 497,
    "preview": "name-template: 'v$NEXT_PATCH_VERSION'\ntag-template: 'v$NEXT_PATCH_VERSION'\ncategories:\n  - title: '🚀 Features'\n    label"
  },
  {
    "path": ".github/stale.yml",
    "chars": 1167,
    "preview": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 21\n# Number of days of inactivity before a "
  },
  {
    "path": ".github/workflows/dependabot-auto-merge.yml",
    "chars": 1031,
    "preview": "\nname: dependabot-auto-merge\non: pull_request_target\n\npermissions:\n  pull-requests: write\n  contents: write\n\njobs:\n  dep"
  },
  {
    "path": ".github/workflows/mkdocs.yml",
    "chars": 353,
    "preview": "name: documentation\non:\n  push:\n    branches:\n      - master\n\njobs:\n  build:\n    name: Deploy docs\n    runs-on: ubuntu-l"
  },
  {
    "path": ".github/workflows/php-cs-fixer.yml",
    "chars": 510,
    "preview": "name: Check & fix styling\n\non: [push]\n\njobs:\n  php-cs-fixer:\n    runs-on: ubuntu-latest\n\n    steps:\n      - name: Checko"
  },
  {
    "path": ".github/workflows/phpstan.yml",
    "chars": 488,
    "preview": "name: PHPStan\n\non:\n  push:\n    paths:\n      - '**.php'\n      - 'phpstan.neon'\n\njobs:\n  phpstan:\n    name: phpstan\n    ru"
  },
  {
    "path": ".github/workflows/pr-labeler.yml",
    "chars": 346,
    "preview": "name: PR Labeler\non:\n  pull_request:\n    types: [opened]\n\njobs:\n  pr-labeler:\n    runs-on: ubuntu-latest\n    steps:\n    "
  },
  {
    "path": ".github/workflows/releaseDrafter.yml",
    "chars": 414,
    "preview": "name: Release Drafter\n\non:\n  push:\n    # branches to consider in the event; optional, defaults to all\n    branches:\n    "
  },
  {
    "path": ".github/workflows/tests.yml",
    "chars": 2231,
    "preview": "name: Unit Tests\n\non: push\n\nenv:\n  # see https://github.com/composer/composer/issues/9368#issuecomment-718112361\n  COMPO"
  },
  {
    "path": ".gitignore",
    "chars": 203,
    "preview": ".idea\n.env\n.php_cs\n.php_cs.cache\n.phpunit.result.cache\nbuild\ncomposer.lock\ncoverage\ndocs\nphpunit.xml\nphpstan.neon\ntestbe"
  },
  {
    "path": ".mailmap",
    "chars": 150,
    "preview": "CodeDredd <gregor@codedredd.de> Gregor Becker <g.becker@careerpartner.eu>\nCodeDredd <gregor@codedredd.de> Gregor Becker "
  },
  {
    "path": ".php_cs.dist.php",
    "chars": 1282,
    "preview": "<?php\n\n$finder = Symfony\\Component\\Finder\\Finder::create()\n    ->in([\n        __DIR__ . '/src',\n        __DIR__ . '/test"
  },
  {
    "path": ".styleci.yml",
    "chars": 16,
    "preview": "preset: laravel\n"
  },
  {
    "path": "LICENSE",
    "chars": 1086,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2014 Michael van de Rijt\n\nPermission is hereby granted, free of charge, to any pers"
  },
  {
    "path": "README.md",
    "chars": 2498,
    "preview": "<p align=\"center\">\n  <a href=\"https://codedredd.github.io/laravel-soap/\" target=\"_blank\" rel=\"noopener noreferrer\">\n    "
  },
  {
    "path": "composer.json",
    "chars": 2155,
    "preview": "{\n    \"name\": \"codedredd/laravel-soap\",\n    \"description\": \"A SoapClient wrapper integration for Laravel\",\n    \"keywords"
  },
  {
    "path": "config/soap.php",
    "chars": 2358,
    "preview": "<?php\n\nuse RobRichards\\XMLSecLibs\\XMLSecurityKey;\n\nreturn [\n    /*\n    |------------------------------------------------"
  },
  {
    "path": "mkdocs.yml",
    "chars": 1183,
    "preview": "site_name: Laravel SOAP Docs\nsite_description: 'Laravel SOAP Documentation'\nsite_author: 'Gregor Becker'\ndocs_dir: docs/"
  },
  {
    "path": "src/Client/Events/ConnectionFailed.php",
    "chars": 460,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Client\\Events;\n\nuse CodeDredd\\Soap\\Client\\Request;\n\nclass ConnectionFailed\n{\n    /**\n   "
  },
  {
    "path": "src/Client/Events/RequestSending.php",
    "chars": 393,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Client\\Events;\n\nuse CodeDredd\\Soap\\Client\\Request;\n\nclass RequestSending\n{\n    /**\n     "
  },
  {
    "path": "src/Client/Events/ResponseReceived.php",
    "chars": 748,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Client\\Events;\n\nuse CodeDredd\\Soap\\Client\\Request;\nuse CodeDredd\\Soap\\Client\\Response;\n\n"
  },
  {
    "path": "src/Client/Request.php",
    "chars": 5353,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Client;\n\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Str;\nuse Illuminate\\Support\\"
  },
  {
    "path": "src/Client/Response.php",
    "chars": 8148,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Client;\n\nuse ArrayAccess;\nuse CodeDredd\\Soap\\Exceptions\\RequestException;\nuse GuzzleHttp"
  },
  {
    "path": "src/Client/ResponseSequence.php",
    "chars": 3560,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Client;\n\nuse CodeDredd\\Soap\\SoapFactory;\nuse OutOfBoundsException;\n\nclass ResponseSequen"
  },
  {
    "path": "src/Driver/ExtSoap/ExtSoapEngineFactory.php",
    "chars": 681,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace CodeDredd\\Soap\\Driver\\ExtSoap;\n\nuse CodeDredd\\Soap\\Faker\\EngineFaker;\nuse Soa"
  },
  {
    "path": "src/Exceptions/NotFoundConfigurationException.php",
    "chars": 408,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Exceptions;\n\nuse RuntimeException;\n\nclass NotFoundConfigurationException extends Runtime"
  },
  {
    "path": "src/Exceptions/RequestException.php",
    "chars": 673,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Exceptions;\n\nuse CodeDredd\\Soap\\Client\\Response;\nuse Exception;\n\nclass RequestException "
  },
  {
    "path": "src/Exceptions/SoapException.php",
    "chars": 384,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Exceptions;\n\n/**\n * Class SoapException.\n */\nclass SoapException extends \\RuntimeExcepti"
  },
  {
    "path": "src/Facades/Soap.php",
    "chars": 2009,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Facades;\n\nuse CodeDredd\\Soap\\SoapFactory;\nuse Illuminate\\Support\\Facades\\Facade;\n\n/**\n *"
  },
  {
    "path": "src/Faker/EngineFaker.php",
    "chars": 1166,
    "preview": "<?php\n\ndeclare(strict_types=1);\n\nnamespace CodeDredd\\Soap\\Faker;\n\nuse CodeDredd\\Soap\\Xml\\XMLSerializer;\nuse Soap\\Engine\\"
  },
  {
    "path": "src/Faker/fake.wsdl",
    "chars": 19410,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<!--\r\nCached version of: http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl\r\n-->\r\n"
  },
  {
    "path": "src/Middleware/CisDhlMiddleware.php",
    "chars": 2167,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Middleware;\n\nuse Http\\Client\\Common\\Plugin;\nuse Http\\Promise\\Promise;\nuse Psr\\Http\\Messa"
  },
  {
    "path": "src/Middleware/WsseMiddleware.php",
    "chars": 5545,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Middleware;\n\nuse Http\\Client\\Common\\Plugin;\nuse Http\\Promise\\Promise;\nuse Psr\\Http\\Messa"
  },
  {
    "path": "src/Ray/LaravelRay.php",
    "chars": 996,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Ray;\n\nuse Closure;\nuse Spatie\\LaravelRay\\RayProxy;\nuse Spatie\\LaravelRay\\Watchers\\Watche"
  },
  {
    "path": "src/Ray/SoapClientWatcher.php",
    "chars": 2436,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Ray;\n\nuse CodeDredd\\Soap\\Client\\Events\\RequestSending;\nuse CodeDredd\\Soap\\Client\\Events\\"
  },
  {
    "path": "src/SoapClient.php",
    "chars": 18015,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap;\n\nuse Closure;\nuse CodeDredd\\Soap\\Client\\Events\\ConnectionFailed;\nuse CodeDredd\\Soap\\Cli"
  },
  {
    "path": "src/SoapFactory.php",
    "chars": 6795,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap;\n\nuse Closure;\nuse CodeDredd\\Soap\\Client\\ResponseSequence;\nuse GuzzleHttp\\Promise\\Create"
  },
  {
    "path": "src/SoapServiceProvider.php",
    "chars": 1612,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap;\n\nuse CodeDredd\\Soap\\Ray\\LaravelRay;\nuse CodeDredd\\Soap\\Ray\\SoapClientWatcher;\nuse Spati"
  },
  {
    "path": "src/SoapTesting.php",
    "chars": 3139,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap;\n\nuse CodeDredd\\Soap\\Client\\Request;\nuse PHPUnit\\Framework\\Assert as PHPUnit;\n\nclass Soa"
  },
  {
    "path": "src/Xml/XMLSerializer.php",
    "chars": 3094,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Xml;\n\nuse DOMDocument;\nuse DOMNode;\nuse Illuminate\\Support\\Str;\nuse SimpleXMLElement;\n\n/"
  },
  {
    "path": "tests/Fixtures/CustomSoapClient.php",
    "chars": 446,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Tests\\Fixtures;\n\nuse CodeDredd\\Soap\\SoapClient;\n\nclass CustomSoapClient extends SoapClie"
  },
  {
    "path": "tests/Fixtures/Responses/SoapFault.xml",
    "chars": 482,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope'>\n    <soap:B"
  },
  {
    "path": "tests/Fixtures/Wsdl/weather.wsdl",
    "chars": 19058,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nCached version of: http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl\n-->\n<wsd"
  },
  {
    "path": "tests/TestCase.php",
    "chars": 1508,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Tests;\n\nuse CodeDredd\\Soap\\Facades\\Soap;\nuse CodeDredd\\Soap\\SoapServiceProvider;\nuse Orc"
  },
  {
    "path": "tests/Unit/Client/ResponseTest.php",
    "chars": 517,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Tests\\Unit\\Client;\n\nuse CodeDredd\\Soap\\Client\\Response;\nuse CodeDredd\\Soap\\Tests\\TestCas"
  },
  {
    "path": "tests/Unit/Commands/MakeClientCommandTest.php",
    "chars": 492,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Tests\\Unit\\Commands;\n\nuse CodeDredd\\Soap\\Tests\\TestCase;\n\nclass MakeClientCommandTest ex"
  },
  {
    "path": "tests/Unit/Commands/MakeValidationCommandTest.php",
    "chars": 701,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Tests\\Unit\\Commands;\n\nuse CodeDredd\\Soap\\Tests\\TestCase;\n\nclass MakeValidationCommandTes"
  },
  {
    "path": "tests/Unit/Middleware/CisDhlMiddlewareTest.php",
    "chars": 683,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Tests\\Unit\\Middleware;\n\nuse CodeDredd\\Soap\\Client\\Request;\nuse CodeDredd\\Soap\\Facades\\So"
  },
  {
    "path": "tests/Unit/SoapClientTest.php",
    "chars": 11670,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Tests\\Unit;\n\nuse CodeDredd\\Soap\\Client\\Events\\ConnectionFailed;\nuse CodeDredd\\Soap\\Clien"
  },
  {
    "path": "tests/Unit/Xml/XmlSerializerTest.php",
    "chars": 749,
    "preview": "<?php\n\nnamespace CodeDredd\\Soap\\Tests\\Unit\\Xml;\n\nuse CodeDredd\\Soap\\Tests\\TestCase;\nuse CodeDredd\\Soap\\Xml\\XMLSerializer"
  }
]

About this extraction

This page contains the full source code of the CodeDredd/laravel-soap GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 60 files (143.1 KB), approximately 37.2k tokens, and a symbol index with 215 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!