phpro/soap-client

repository·v6.x·Indexed 21 days ago

https://github.com/phpro/soap-client

A general-purpose PHP SOAP client that replaces arrays and stdClasses with strongly-typed Value Objects for Requests and Responses. It features a scaffolding wizard and a CLI tool for generating PHP classes from WSDL XSD types, class maps, and client factories. The library supports PSR-6 caching, PSR-7/17 HTTP messages, and PSR-18 HTTP transports, allowing for deep customization of type conversion, event listeners, and WSDL loading.

Tokens
24.6K
Snippets
88
Records
96
Agent score
69%

What's inside phpro-soap-client

  1. The Caller pattern in V2 clients

    v6.x

    In V2, generated clients no longer inherit from a base client. Instead, they use a Caller object to transport requests. This makes the client easier to own and manage.

    use Calculator\Type\Add;
    use Calculator\Type\AddResponse;
    use Phpro\SoapClient\Caller\Caller;
    
    class CalculatorClient
    {
        /**
         * @var Caller
         */
        private $caller;
    
        public function __construct(Caller $caller)
        {
            $this->caller = $caller;
        }
    
        public function add(Add $parameters): AddResponse
        {
            return ($this->caller)('Add', $parameters);
        }
    }
  2. How code generation rules work

    v6.x

    Code generation rules allow you to customize how the SOAP client's code is automatically generated. The primary purpose of a rule is to trigger a Code Assembler (a component that performs the actual code modification) when specific conditions are met.

    Rules are evaluated during the code generation process, and you can use built-in rules or implement your own to apply specific assemblers to classes, properties, or client methods based on metadata, names, or types.

  3. Understand Assembler Contexts

    v6.x

    Assemblers are triggered based on the ContextInterface provided during code generation. Understanding these contexts allows you to target specific parts of the generation process:

    • ClassMapContext: Triggered during the generate:classmap command.
    • TypeContext: Triggered during generate:types for every SOAP type.
    • PropertyContext: Triggered during generate:types for every property in a SOAP type.
    • ClientMethodContext: Triggered during generate:client for every method.
    • FileContext: Triggered during every generate:* command.

    Specific contexts (TypeContext, PropertyContext, ClientMethodContext, and ClassMapContext) allow you to access the CodeGeneratorContext via $context->getCodeGeneratorContext(). This provides access to:

    • $context->getCodeGeneratorContext()->typeNamespaceMap: The namespace map for types.
    • $context->getCodeGeneratorContext()->codingStandards: The coding standards strategy.
  4. Configure Type Namespace Mapping and Strategies

    v6.x

    The TypeNamespaceMap manages how XML namespaces (xmlns) map to PHP namespaces and directories. This is essential for organizing types from multiple XML namespaces into separate PHP directories.

    Explicit Mappings

    You can manually map specific XML namespaces to specific Destination objects using withMapping().

    TypeNamespaceMap::create(new Destination('src/Type', 'App\\Type'))
        ->withMapping('http://www.opengis.net/gml/3.2', new Destination('src/Type/Gml', 'App\\Type\\Gml'))
        ->withMapping('http://xoev.de/schemata/xzufi/2_2_0', new Destination('src/Type/Xzufi', 'App\\Type\\Xzufi'))
  5. How coding standards work in the code generator

    v6.x

    The CodingStandardsStrategyInterface allows you to customize how SOAP names (types, operations, namespaces, etc.) are normalized into PHP-compliant names during code generation. This is essential if your project requires specific naming conventions that differ from the default DefaultCodingStandardsStrategy.

    By implementing this interface, you can control:

    • Type Names: How SOAP types become PHP classes or enums.
    • Operation Names: How SOAP methods become PHP methods.
    • Namespace Segments: How XML namespace parts are converted to PHP namespace segments.
    • Enum Cases: How enum values are converted to PHP enum case names.
    • Property Accessors: How accessor methods (getters/setters) are named.
    • Parameter Names: How property names are converted into PHP parameter names (useful for supporting named arguments).
    use Phpro\SoapClient\CodeGenerator\CodingStandards\CodingStandardsStrategyInterface;
    
    interface CodingStandardsStrategyInterface
    {
        public function normalizeTypeName(string $name): string;
        public function normalizeOperationName(string $method): string;
        public function normalizeNamespaceSegment(string $segment): ?string;
        public function normalizeEnumCaseName(string $value): string;
        public function generatePropertyAccessorMethodName(string $prefix, string $property): string;
        public function normalizeParameterName(string $propertyName): string;
    }
  6. Use Strategy-based Type Namespace Resolution

    v6.x

    Instead of manual mappings, you can use a strategy to automatically resolve destinations. The PrefixBasedTypeNamespaceStrategy derives a sub-namespace from the XML namespace prefix (e.g., a gml prefix results in an App\Type\Gml namespace).

    Explicit withMapping() entries always take precedence over the strategy. You can combine both for hybrid management.

    use Phpro\SoapClient\CodeGenerator\TypeNamespaceMap\Strategy\PrefixBasedTypeNamespaceStrategy;
    
    TypeNamespaceMap::create(new Destination('src/Type', 'App\\Type'))
        ->withStrategy(new PrefixBasedTypeNamespaceStrategy($config->getCodingStandards()))
  7. What are code assemblers?

    v6.x
    Code assemblers are a thin layer built on top of laminas-code used by the SOAP client to generate PHP code for SOAP types. They allow you to customize the structure of generated classes, such as adding constructors, getters, setters, or implementing specific interfaces. While many built-in assemblers are available, you can also create custom assemblers to inject specific code patterns into your generated SOAP types.
  8. Hooking in with SOAP events

    v6.x

    The generated client factory provides an EventDispatchingCaller by default. This allows you to listen to SOAP lifecycle events by subscribing listeners to the configured EventDispatcher.

    Available event types include:

    • \Phpro\SoapClient\Event\RequestEvent: Triggered when a SOAP request is made.
    • \Phpro\SoapClient\Event\ResponseEvent: Triggered when a SOAP response is received.
    • \Phpro\SoapClient\Event\FaultEvent: Triggered when a SOAP fault occurs.
    // Example of subscribing a custom listener to the dispatcher
    class ResponseFailedSubscriber implements SubscriberInterface
    {
        // implement interface
    }
    
    $dispatcher->addSubscriber(new ResponseFailedSubscriber());
  9. Configure php-vcr for SOAP testing

    v6.x

    When setting up php-vcr for your test suite, you must configure the cassette path and enable the appropriate library hooks. For SOAP clients, you typically need to enable the soap and/or curl hooks. Ensure you only select the library hooks relevant to your environment.

    \VCR\VCR::configure()
        ->setCassettePath('test/fixtures/vcr')
        ->enableLibraryHooks(['soap', 'curl'])
    ;
    \VCR\VCR::turnOn();
  10. Generate a base client factory

    v6.x

    Use the generate:clientfactory command to create a boilerplate client factory class. This factory serves as a starting point for initializing your SOAP client and can be customized to include specific engines, transports, or middleware.

    The generated factory is placed in the same namespace and directory as the client, using the client's name appended with Factory (e.g., CalculatorClient results in CalculatorClientFactory).

    A configuration file is required to build the classmap during generation.

    vendor/bin/soap-client generate:clientfactory --config=path/to/your/config.yaml
  11. Upgrade from V2 to V3

    v6.x

    To upgrade to V3, update the package via composer and modify your code generation configuration to use CodeGeneratorEngineFactory::create() instead of the V2 engine factory.

    composer require 'phpro/soap-client:^3.0.0' --update-with-dependencies
    use Phpro\SoapClient\Soap\CodeGeneratorEngineFactory;
    use Soap\Wsdl\Loader\FlatteningLoader;
    use Soap\Wsdl\Loader\StreamWrapperLoader;
    
    return Config::create()
        ->setEngine($engine = CodeGeneratorEngineFactory::create(
            'your.wsdl',
            new FlatteningLoader(new StreamWrapperLoader())
        ));
  12. Create a custom SOAP client

    v6.x

    To use the library, you must create a custom client class that wraps a Phpro\SoapClient\Caller\Caller instance. This client acts as the high-level API for your application.

    Key requirements for your custom client:

    • Constructor: Inject Phpro\SoapClient\Caller\Caller.
    • Methods: Explicitly define methods for each SOAP operation. Each method should accept a request object and return a response object.
    • Request Objects: All request value-objects passed to the caller MUST implement RequestInterface.
    • Response Types: SOAP responses typically implement either ResultInterface or ResultProviderInterface (the latter is used if the response wraps a ResultInterface).
    • Exception Handling: The client is designed to normalize exceptions by throwing Phpro\SoapClient\Exception\SoapException, allowing for consistent error handling even when multiple underlying handlers are used.

    The Caller service is responsible for initializing the SOAP call and triggering any subscribed event listeners.

    class YourClient
    {
        private \Phpro\SoapClient\Caller\Caller $caller;
    
        public function __construct(\Phpro\SoapClient\Caller\Caller $caller)
        {
            $this->caller = $caller;
        }
    
        /**
         * @param RequestInterface $request
         *
         * @return ResultInterface & HelloWorldResponse
         * @throws \Phpro\SoapClient\Exception\SoapException
         */
        public function helloWorld(RequestInterface $request): HelloWorldResponse
        {
            $response = ($this->caller)('HelloWorld', $request);
    
            \Psl\Type\instance_of(\Your\HelloWorldResponse::class)->assert($response);
            \Psl\Type\instance_of(\Phpro\SoapClient\Type\ResultInterface::class)->assert($response);
    
            return $response;    
        }
    }