* * { * /** * * @param int $x * * @param int $y * * @return int * * * public function add($x, $y) * { * } * } * * * The document/literal wrapper pattern would lead php ext/soap to generate a * single "request" object that contains $x and $y properties. To solve this a * wrapper service is needed that extracts the properties and delegates a * proper call to the underlying service. * * The input variable from a document/literal SOAP-call to the client * MyCalculatorServiceClient#add(10, 20) would lead PHP ext/soap to create * the following request object: * * * $addRequest = new \stdClass; * $addRequest->x = 10; * $addRequest->y = 20; * * * This object does not match the signature of the server-side * MyCalculatorService and lead to failure. * * Also the response object in this case is supposed to be an array * or object with a property "addResult": * * * $addResponse = new \stdClass; * $addResponse->addResult = 30; * * * To keep your service object code free from this implementation detail * of SOAP this wrapper service handles the parsing between the formats. * * @example * * $service = new MyCalculatorService(); * $soap = new \Laminas\Soap\Server($wsdlFile); * $soap->setObject(new \Laminas\Soap\Server\DocumentLiteralWrapper($service)); * $soap->handle(); * */ class DocumentLiteralWrapper { /** @var object */ protected $object; /** @var ReflectionObject */ protected $reflection; /** * Pass Service object to the constructor * * @param object $object */ public function __construct($object) { $this->object = $object; $this->reflection = new ReflectionObject($this->object); } /** * Proxy method that does the heavy document/literal decomposing. * * @param string $method * @param array $args * @return mixed */ public function __call($method, $args) { $this->assertOnlyOneArgument($args); $this->assertServiceDelegateHasMethod($method); $delegateArgs = $this->parseArguments($method, $args[0]); $ret = call_user_func_array([$this->object, $method], $delegateArgs); return $this->getResultMessage($method, $ret); } /** * Parse the document/literal wrapper into arguments to call the real * service. * * @param string $method * @param object $document * @return array * @throws Exception\UnexpectedValueException */ protected function parseArguments($method, $document) { $reflMethod = $this->reflection->getMethod($method); $params = []; foreach ($reflMethod->getParameters() as $param) { $params[$param->getName()] = $param; } $delegateArgs = []; foreach (get_object_vars($document) as $argName => $argValue) { if (! isset($params[$argName])) { throw new Exception\UnexpectedValueException(sprintf( "Received unknown argument %s which is not an argument to %s::%s", $argName, get_class($this->object), $method )); } $delegateArgs[$params[$argName]->getPosition()] = $argValue; } return $delegateArgs; } /** * Returns result message content * * @param string $method * @param mixed $ret * @return array */ protected function getResultMessage($method, $ret) { return [$method . 'Result' => $ret]; } /** * @param string $method * @throws Exception\BadMethodCallException */ protected function assertServiceDelegateHasMethod($method) { if (! $this->reflection->hasMethod($method)) { throw new Exception\BadMethodCallException(sprintf( "Method %s does not exist on delegate object %s", $method, get_class($this->object) )); } } /** * @param array $args * @throws Exception\UnexpectedValueException */ protected function assertOnlyOneArgument(array $args) { if (count($args) !== 1) { throw new Exception\UnexpectedValueException(sprintf( "Expecting exactly one argument that is the document/literal wrapper, got %d", count($args) )); } } }