name: soap-client-setup description: Scaffolds SOAP client initialization and credential handling as used in src/Fantastico.php. Use when user says 'new soap client', 'connect to API', or creates a new API wrapper class in src/. Key capabilities: ext-soap wiring with nusoap fallback, connected flag, constructor credential injection, md5 hash auth, response caching skeleton. Do NOT use for REST or non-SOAP integrations.
soap-client-setup
Critical
- Use tabs for indentation — never spaces (
.scrutinizer.ymluse_tabs: true) - No short open tags (
<?phponly, never<?) - All public methods require PHPDoc blocks with
@paramand@returntags - Constants must be
UPPERCASE; properties and methods must becamelCase $this->connect()must be called lazily inside each public API method — never in the constructor$connectedflag must bepublic; credentials must beprivate
Instructions
Create the source class file in
src/with namespaceDetain\ClassName\matching the PSR-4 entry incomposer.json. Add the optional StatisticClient require block at the top:<?php namespace Detain\YourClass; if (is_file(__DIR__.'/../../../workerman/statistics/Applications/Statistics/Clients/StatisticClient.php')) { require_once __DIR__.'/../../../workerman/statistics/Applications/Statistics/Clients/StatisticClient.php'; }Verify the namespace matches
composer.jsonautoload.psr-4before proceeding.Declare class properties in this order: public constants → public
$wsdlstring →public $connected = false→private $apiUsername→private $apiPassword→private $soapClient→private $cache. Each needs a PHPDoc block.Write the constructor — accepts
$usernameand$password, initializes$cache = [],$soapClient = null, and assigns credentials to private properties:public function __construct($username, $password) { $this->cache = []; $this->soapClient = null; $this->apiUsername = $username; $this->apiPassword = $password; }Write
connect()— guard withnull === $this->soapClient, disable WSDL cache, set timeouts, try\SoapClientwithSOAP_1_1first, fall back tonusoap_clienton exception, set$this->connected = truein the nusoap branch:public function connect() { $nusoap = false; if (null === $this->soapClient) { ini_set('soap.wsdl_cache_enabled', '0'); ini_set('max_execution_time', 1000); ini_set('default_socket_timeout', 1000); try { $this->soapClient = new \SoapClient($this->wsdl, ['soap_version' => SOAP_1_1, 'connection_timeout' => 1000, 'trace' => 1, 'exception' => 1]); } catch (\Exception $e) { $nusoap = true; } } if (true === $nusoap) { require_once INCLUDE_ROOT.'/../vendor/detain/nusoap/lib/nusoap.php'; $this->soapClient = new \nusoap_client($this->wsdl); $this->connected = true; } }Write a private
getHash()that returnsmd5($this->apiUsername.$this->apiPassword). Pass this as the first argument to every SOAP call.In each public API method, follow this pattern: check cache → call
$this->connect()→ optional\StatisticClient::tick()→ call$this->soapClient->methodName($this->getHash(), ...)→json_decode(..., true)the response → optional\StatisticClient::report()→myadmin_log('module', 'debug', json_encode($result), __LINE__, __FILE__)→ store in$this->cache→ return.Add
tests/YourClassTest.phpextendingPHPUnit\Framework\TestCase.setUp()must instantiate via env vars:$this->object = new YourClass(getenv('YOURAPI_USERNAME'), getenv('YOURAPI_PASSWORD'));Add
testConnect()asserting$this->object->connected === trueafter callingconnect(). Mark all unimplemented tests with$this->markTestIncomplete('...').Verify tests run:
vendor/bin/phpunit tests/ -v
Examples
User says: "Create a new SOAP API wrapper for Netenberg in src/"
Actions taken:
- Created
src/Fantastico.phpundernamespace Detain\Fantastico; - Added
public $wsdl,public $connected = false, private$apiUsername,$apiPassword,$soapClient,$cache - Constructor stores credentials, inits
$cache = []and$soapClient = null connect()tries\SoapClientwithSOAP_1_1, falls back tonusoap_client, sets$connected = truegetHash()returnsmd5($username.$password)for auth- Each public method calls
$this->connect()lazily, passes$this->getHash()as first SOAP arg - Created
tests/FantasticoTest.phpwithsetUp()readinggetenv('FANTASTICO_USERNAME')/getenv('FANTASTICO_PASSWORD')
Result: vendor/bin/phpunit tests/ -v passes testConnect().
Common Issues
Fatal error: Class 'SoapClient' not found—ext-soapis not enabled. Runphp -m | grep soap; enableextension=soapinphp.inior installphp-soappackage.testConnect()fails with$connected === false— The\SoapClientconstructor threw but the nusoap fallback path was not reached. Addvar_dump($e->getMessage())inside the catch block to check the exception, then verifyINCLUDE_ROOTis defined beforeconnect()is called.- WSDL fetch timeout — Increase
ini_set('default_socket_timeout', 1000)or check network access to the WSDL URL. Never cache WSDL during development (soap.wsdl_cache_enabled = 0). json_decodereturnsnull— The SOAP endpoint returned non-JSON (e.g. raw XML or a SOAP fault). Use$this->soapClient->__getLastResponse()to inspect the raw response.faultcode 1302: invalid hash—getHash()is computingmd5of wrong values. Confirm$apiUsernameand$apiPasswordare the raw credential strings, not empty (check env vars are set).- Scrutinizer fails on indentation — File uses spaces. Run
:retabin vim orunexpand --first-only -t 4to convert to tabs before committing.