Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Besimple Soap Laravel Package

smartbox/besimple-soap

A Laravel package that brings SOAP web services into your app with a simple, expressive client. Build and send SOAP requests, parse responses, and integrate legacy APIs without boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

smartbox/besimple-soap is a maintained-in-name-only fork of the BeSimpleSoap toolchain: a set of libraries that wrap PHP's ext-soap to add WS-Security, MTOM/SwA attachments, WSDL tooling and a friendlier builder API. There is no Laravel service provider, no config file, no facade — you are wiring a low-level SOAP library. Treat it as vendored infrastructure, not as a package you composer require and forget.

Before you install anything, verify you actually need it:

php -m | grep -i soap   # ext-soap is mandatory
php -m | grep -i openssl # required only for WS-Security signing/encryption

If your integration is a plain WSDL call with no WS-Security and no attachments, use ext-soap directly — it is maintained by php-src and needs zero extra dependency. Reach for this package when you need at least one of:

  • WS-Security UsernameToken / X.509 signing / encryption
  • MTOM or SwA binary attachments
  • Generated PHP classes for large, imported, multi-namespace WSDLs
  • Type converters for dateTime, duration, custom XSD types

Install and pin exactly. v1.3.5 is the release to start from — it fixes a real WSDL-resolution caching bug (see Gotchas) and cleans up test-suite deprecations:

composer require smartbox/besimple-soap:^1.3.5

Do not float on dev-master. The package still ships no Laravel integration and the release cadence is glacial, so immediately fork it into your VCS ("repositories": [{"type": "vcs", ...}]) as soon as you need your first patch. Add it to your CI matrix on the exact PHP version you deploy — note that the 1.3.5 test matrix additions are Symfony 2.8 & 3.4, i.e. the project's CI is still anchored to a pre-PHP-8 ecosystem; do not read the changelog as evidence of modern PHP support.

First use case — a config-bound client. Create a service provider that builds and caches one client per endpoint:

// app/Providers/SoapServiceProvider.php
public function register(): void
{
    $this->app->singleton(BillingSoapClient::class, fn () => SoapClientBuilder::create()
        ->withWsdl(config('soap.billing.wsdl'))
        ->withOptions([
            'trace'              => true,          // needed for debugging
            'exceptions'         => true,
            'cache_wsdl'         => WSDL_CACHE_NONE, // see gotchas
            'connection_timeout' => 10,
            'classmap'           => require app_path('Soap/billing-classmap.php'),
        ])
        ->build());
}

Look first at the generated classmap and the WSDL's targetNamespace — almost every integration bug later traces back to one of those two. If the WSDL pulls in <xsd:import>s, look at where the resolved copies land before you go further; that path is exactly what 1.3.5 changed.

Implementation Patterns

Wrap every remote in a contract. Do not inject SoapClient into controllers, jobs or models. Define a plain interface and an adapter:

interface BillingSoapClient
{
    public function getInvoice(string $id): InvoiceResponse;
}

This is the single highest-leverage decision with this package, because it makes the SOAP layer swappable and lets you fake it in tests (you cannot Http::fake() a raw socket-based SoapClient).

Config-driven endpoint registry. Put WSDL URLs, credentials and per-environment overrides in config/soap.php, never in code. Enterprise partners hand you different WSDLs per environment, and WS-Security passwords rotate.

DTO hydration at the boundary. ext-soap (and therefore this package) returns stdClass graphs, not your models. Hydrate immediately in the adapter:

$raw = $this->client->getInvoice(['invoiceId' => $id]);
return InvoiceResponse::fromSoap($raw);   // one place knows the SOAP shape

On the way out, build arrays deliberately. Repeated elements need a list with sequential integer keys; a single-element result must still be [0 => ...] or it serializes as an object instead of an array.

WS-Security for enterprise partners. Attach it at build time:

$builder = SoapClientBuilder::create()->withWsdl($wsdl)->withWsSecurity();
// username token
$builder->withWsSecurityUsernameToken($user, $pass, /* digest */ true);
// or X.509 signing
$builder->withWsSecurityCertificate($certPath, $privateKeyPath, $passphrase);

Keep the credentials in config/soap.php, and make sure the server's clock is NTP-synced — UsernameToken timestamps default to a short TTL and drift produces opaque faults.

Attachments via MTOM/SwA. For document uploads, stream from Laravel's Storage into a SoapAttachment rather than file_get_contents — payloads in these integrations are routinely tens of megabytes and base64 inflates them by a third.

Queue the calls, but understand idempotency. SOAP operations are usually not idempotent. If you dispatch SendOrderJob, set tries = 1 unless the partner API explicitly supports a dedupe key; a retried createInvoice silently double-bills.

Pre-resolve imported WSDLs at build time. Even with the 1.3.5 fix, the safest pattern for import-heavy WSDLs remains: download the whole tree into storage/app/wsdl during deploy, rewrite the <xsd:import schemaLocation="..."> entries to relative local paths, and point withWsdl() at the local entry file. That makes the build hermetic, removes intermittent network failures from your test suite, and makes the WSDL-cache behaviour irrelevant.

Server side, if you are the producer. You can expose a Laravel route that delegates to the server builder:

Route::post('/soap/billing', function () {
    $server = SoapServerBuilder::create()
        ->withWsdl(storage_path('wsdl/billing.wsdl'))
        ->withService(new BillingService())
        ->build();
    $server->handle();
})->withoutMiddleware([\App\Http\Middleware\VerifyCsrfToken::class]);

Return the raw response — do not wrap it in a JsonResponse or a view.

Observability decorator. Because calls bypass Guzzle, Laravel Telescope and the HTTP client events see nothing. Add a decorator around your interface that logs elapsed time, operation name and, on \SoapFault, __getLastRequest() / __getLastResponse() (enabled by trace => true). Redact credentials before logging.

Testing. Bind a fake implementation of your interface in TestCase::setUp(). For genuine integration tests, run the generated SoapServer locally against a WSDL fixture and point the client at it — this is the only reliable way to catch namespace and soap_version mismatches before they hit staging.

Gotchas and Tips

  • Thin maintenance, and the changelog is narrow. v1.3.5 is three items: test-suite deprecation fixes, a WSDL-resolution saving bug, and Symfony 2.8/3.4 added to CI. Notably absent: any PHP 8.2/8.3 runtime work. The "fixed deprecation notices" line refers to the tests, not to the library's overridden \SoapClient internals — so if you saw deprecation noise on PHP 8.1+, verify it is actually gone against your PHP version rather than assuming. Budget a fork-and-patch afternoon; evaluate phpro/soap-client or native ext-soap before adopting for greenfield work.
  • Resolved WSDLs were not saved correctly before 1.3.5. If you use any cache_wsdl mode other than WSDL_CACHE_NONE with a WSDL that <xsd:import>s other files, older versions wrote incomplete/incorrect resolved documents into the cache directory. Symptom: the client re-fetches the imports on every request, or fails on a cache entry that looks present but is unusable. When upgrading, clear the WSDL cache directory (sys_get_temp_dir() or your configured cache_dir) — stale artifacts written by the old code will otherwise survive the upgrade and keep failing.
  • cache_wsdl still bites in containers. The default WSDL_CACHE_BOTH writes to sys_get_temp_dir(). Read-only or ephemeral filesystems produce "failed to load external entity" or serve a stale WSDL forever after the partner updates theirs. Set WSDL_CACHE_NONE or an explicit cache_dir and clear it on deploy. 1.3.5 makes the saving correct; it does not make an unwritable temp dir writable.
  • Two different credentials. login/password in withOptions() authenticate the WSDL download; withBasicAuth() / WS-Security authenticates the service call. Mixing these up gives a 401 on one and a 500 on the other.
  • No timeout option. SoapClient honours connection_timeout and PHP's default_socket_timeout, not a timeout key. A missing setting means your queue worker hangs for the process default.
  • "looks like we got no XML document". Almost always a soap_version mismatch (SOAP_1_1 vs SOAP_1_2) or a response wrapped in a proxy/error page. Log the raw response before assuming the WSDL is wrong.
  • exceptions => true is not optional. Without it you get silent false returns. With it you get \SoapFault, which Laravel's handler renders as a 500 unless you catch it in the adapter and translate to a domain exception.
  • __getLastRequest() returns null if trace => true was not set at construction — you cannot enable it later.
  • Single-element arrays. Set ['item' => [0 => $a]] explicitly; associative keys produce a struct rather than a repeated element. This is the most common cause of partner-side schema validation failures.
  • xsi:nil and attributes. Nulls are omitted by default. To send an explicit nil or an XML attribute you need SoapVar with XSD_ANYXML or the leading-underscore attribute array form the package's type converters understand.
  • WS-Security clock skew and cert formats. Signing needs the private key unencrypted or the passphrase passed in the builder; PKCS#12 bundles are not PEM. A five-minute clock drift breaks UsernameToken validation with an unhelpful Security fault.
  • Extension points worth knowing: TypeConverterInterface (register via withTypeConverters()) is the clean hook for custom XSD types and is far safer than post-processing stdClass. Subclassing the client's __doRequest() is the pragmatic hook for request signing not covered by the builder, and for capturing envelopes in tests.
  • Import-heavy WSDLs (<xsd:import> across relative URLs) require the parser to be able to resolve those URLs. Pre-download the whole WSDL tree into storage/app/wsdl and point at the local file; it makes builds hermetic and removes a class of intermittent network failures from your test suite — and it is now the mitigation of choice rather than relying on the cache layer, even post-1.3.5.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core