wsdltophp/packagebase
Base library for PHP packages generated from WSDL files by wsdltophp/packagegenerator. It supplies shared runtime utilities, helpers, and common behavior so generated SOAP client code stays lean, consistent, and easier to maintain.
You almost never install wsdltophp/packagebase by hand — it arrives as a transitive dependency of a client generated by wsdltophp/packagegenerator. Treat this repo as the runtime contract your generated SOAP client depends on, and read it when you need to know exactly what a generated class can do.
Minimal path to first use:
# 1. Generate a client package from a WSDL (usually via the generator, not this repo)
composer require --dev wsdltophp/packagegenerator
vendor/bin/generate.phar generate:package --urlorpath=/path/to/service.wsdl --composer-name=vendor/api-client
# 2. In the generated package, packagebase is a normal require
composer require wsdltophp/packagebase
First things to read, in order:
Settings.php — it wires AbstractSoapClientBase options (WSDL, location, uri, classmap, trace, cache).WsdlToPhp\PackageBase\AbstractSoapClientBase — the object your ServiceType\* classes extend.StructType\* classes extend (AbstractStructBase on v3, AbstractStruct/AbstractStructBase on v4 depending on generator version) — it defines __toArray, jsonSerialize, and property access.AbstractArrayType, AbstractEnumType, AbstractSimpleType — the collection/enum/scalar plumbing generated code delegates to.First real use case: build a struct, call an operation, inspect the result.
use Vendor\ApiClient\ServiceType\Search;
use Vendor\ApiClient\StructType\SearchRequestType;
$svc = new Search();
$req = new SearchRequestType();
$req->setQuery('widgets');
$result = $svc->Search($req); // returns a generated struct or throws SoapFault
if ($result === false) {
throw new \RuntimeException($svc->getLastError());
}
print_r($result->__toArray());
Wrap generated services, don't call them from controllers. AbstractSoapClientBase caches a single \SoapClient instance (getSoapClient()), so long-lived processes share the connection. Inject the generated ServiceType\* class into a repository/adapter and expose domain methods — this isolates the noisy generated API (setFoo, getFoo, __toArray) from application code.
Hydrate structs with arrays for readability. All generated structs accept associative arrays through their setters, and nested structs are auto-constructed where the WSDL declares them:
$req = new SearchRequestType([
'Query' => 'widgets',
'Filters' => ['Category' => 'tools'], // nested StructType
]);
Reuse the base struct for JSON/serialisation boundaries. __toArray() and jsonSerialize() come from the base class and are the only stable way to reach property values without knowing every getter. For anything beyond the default shape, subclass the generated struct and override jsonSerialize() — never edit generated files.
final class SearchRequestType extends \Vendor\ApiClient\StructType\SearchRequestType
{
public function jsonSerialize(): array
{
$data = parent::jsonSerialize();
unset($data['InternalFlag']);
return $data;
}
}
Centralise SOAP client options. Rather than mutating generated Settings, extend AbstractSoapClientBase in a project-level base, or set options once after construction:
$svc = new Search();
$svc->setSoapClientOption(SOAP_1_2, 'soap_version');
$svc->setSoapClientOption('https://proxy.internal/soap', 'proxy_host');
$svc->setSoapClientOption(120, 'connection_timeout');
Attach custom SOAP headers at the service level. Generated structs for headers exist, but you can also push raw \SoapHeader objects when a WSDL doesn't define them:
$svc->getSoapClient()->__setSoapHeaders(new \SoapHeader('urn:auth', 'ApiKey', $key));
Enums and simple types. Generated enum classes expose getValidValues() and validate via setValue(); use them as guards at the edge of your integration rather than scattering strings.
if (!SearchMode::valueIsValid($mode)) {
throw new \InvalidArgumentException("Unknown search mode: {$mode}");
}
$req->setMode(new SearchMode($mode));
Debugging pattern for every integration. Wrap calls so getLastRequest/getLastResponse/getLastRequestHeaders can be logged on failure — this is the single highest-value feature of AbstractSoapClientBase and the main reason to keep trace => true in staging.
try {
return $svc->Search($req);
} catch (\SoapFault $e) {
$logger->error('SOAP fault', [
'fault' => $e->getMessage(),
'request' => $svc->getLastRequest(),
'response' => $svc->getLastResponse(),
]);
throw $e;
}
StructType/, ServiceType/, EnumType/, etc. Put all custom behaviour in subclasses, or configure PackageGenerator's "extends" option so it generates classes extending your base classes from the start.AbstractStructBase, AbstractStructArrayBase); PackageGenerator v4+ targets packagebase v4 with renamed/consolidated bases. Match them or generated classes will fail with "class not found"/signature errors. Pin the generator version in composer.json ("wsdltophp/packagegenerator": "^4") and regenerate deliberately.getLastRequest() returns null unless trace => true. Confirm Settings::getSoapClientOption('trace') is truthy before relying on it — otherwise you get silent nulls during an incident. Keep trace on in staging, off (or sampled) in production.SoapFault from transport errors. getLastError() covers connection/DNS/HTTP failures where the client returns false instead of throwing; SoapFault covers server-side faults, which often arrive with HTTP 500 too. Check both paths.SoapClient caching writes to wsdl-* files under sys_get_temp_dir(); stale caches produce "failed to load external entity" or outdated classmap errors. Use AbstractSoapClientBase::WSDL_CACHE_NONE (or WITHOUT_WSDL_CACHE) in tests/CI, and warm the cache in a deploy step if cold-start latency matters.$struct->Foo = 'x'; use setFoo(). The base structs expose properties via interfaces/magic access in older versions, and relying on that will emit deprecation warnings or break on newer runners.location and uri on the service before calling, or every call fails with "SOAP-ERROR: Parsing WSDL".AbstractArrayType expects values of the declared item type. Passing plain arrays into a property typed as FooArrayType won't auto-wrap in every generated version — construct the generated array type (new FooArrayType($items)) or the class map will silently mis-serialise.AbstractSoapClientBase::getSoapClient() memoises the \SoapClient. Under long-running workers (Octane/Horizon) a single cached client will hold a stale connection and any per-request setSoapClientOption changes won't re-init. Instantiate service classes per request/job, or reset between messages.initSoapClient() (older bases) or getSoapClient() (newer bases) in a project-level abstract service to inject middleware, custom classmap, or logging around __doRequest. This is the sanctioned hook; avoid monkey-patching \SoapClient.How can I help you explore Laravel packages today?