R e i - D r e a m
for Laravel
Guest
login

最終投稿日:2026年09月20日

SOAP API

SOAP APIとは?

WSDL(ウィズドル / Web Services Description Language)定義書を使用した非常に厳格な通信方式です。
XML形式で書かれており、以下のような情報が記述されています。
・提供されるメソッド(関数)の一覧
・リクエスト・レスポンスのデータ型や構造
・アクセス先のURL(エンドポイント)や通信プロトコル
これまで説明してきたAPIの名称は『REST API』となり、今回は『SOAP API』となります。
歴史的には『SOAP API』が古く『SOAP』は【Simple Object Access Protocol】の略で、当時はとっても簡単なAPIの位置づけでした。
ただし現在主流の『REST API』に慣れてる人間からすると『超~面倒臭いんですけどっ!!』って感覚です。
『SOAP』に比べれば『REST』は名前の通り【寝ながらでも作れるよ!】って感じですよ。
    ※ 本来は[Representational State Transfer]の略です
新規プロジェクトで『よし、SOAPサーバを立てよう!』なんて物好きな人はいません。
ただし銀行やレガシーシステムでは『SOAP』が現役で稼働してたりします。
そんなシステムにAPI連携をする要件がきたらどうなりますか?
当然先方から『あ、うちはSOAP通信だから、ヨロシク!』ですよね。
なのでSOAPに関しては『APIクライアント』に限定した内容を解説します。
○ pom.xml
お約束の依存関係を設定します。
pom.xml

<!-- Jakarta Web Services API (jakarta.jws や jakarta.xml.ws が含まれます) -->
<dependency>
    <groupId>jakarta.xml.ws</groupId>
    <artifactId>jakarta.xml.ws-api</artifactId>
    <version>4.0.0</version>
    <scope>provided</scope>
</dependency>

WSDLファイル

以下は単純に、リクエスト『userId』に対し、レスポンス『userName』『email』を返却する定義書となります。
どうですか?
何やら非常に香ばしい匂いが漂い始めたと思いませんか。
src/main/resources/META-INF/wsdl/UserService.wsdl

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="UserService"
    targetNamespace="http://example.com/userservice"
    xmlns:tns="http://example.com/userservice"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns="http://schemas.xmlsoap.org/wsdl/">
    <!-- データ型の定義 -->
    <types>
        <xsd:schema targetNamespace="http://example.com/userservice">
            <xsd:element name="GetUserRequest">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="userId" type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
            <xsd:element name="GetUserResponse">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="userName" type="xsd:string"/>
                        <xsd:element name="email" type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:schema>
    </types>
    <!-- 電文(メッセージ)構造の定義 -->
    <message name="GetUserRequestMessage">
        <part name="parameters" element="tns:GetUserRequest"/>
    </message>
    <message name="GetUserResponseMessage">
        <part name="parameters" element="tns:GetUserResponse"/>
    </message>
    <!-- インターフェース(操作)の定義 -->
    <portType name="UserPortType">
        <operation name="GetUser">
            <input message="tns:GetUserRequestMessage"/>
            <output message="tns:GetUserResponseMessage"/>
        </operation>
    </portType>
    <!-- 通信プロトコルとデータフォーマットのバインディング -->
    <binding name="UserBinding" type="tns:UserPortType">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="GetUser">
            <soap:operation soapAction="http://example.com/userservice/GetUser"/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>
    <!-- 接続先サービスのエンドポイント定義 -->
    <service name="UserService">
        <port name="UserPort" binding="tns:UserBinding">
            <soap:address location="https://api.example.com/UserService"/>
        </port>
    </service>
</definitions>

● データ定義の構造
○『definitions』タグ
WSDLドキュメント全体の「コンテナ(器)」であり、このファイルのルート要素です。
ファイル全体の名前空間(Namespace)を定義し、内部で利用される要素をひとまとめにする役割を持ちます。
① name="UserService"
このWSDLサービス全体につける任意の名前です。
コード自動生成ツールなどでクラス名やサービス名の基礎として使われることがあります。
② targetNamespace="http://example.com/userservice"
このWSDL内で定義されている要素が属する一意の識別URI(名前空間)を指定します。
③ xmlns:tns="http://example.com/userservice"
targetNamespaceで指定した名前空間を、このファイル内で参照するためのエイリアスとして『tns』を割り当てています。
④ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
⑤ xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
WSDL内で利用する外部規格の名前空間と接頭辞の宣言です。
xsd :XML Schema (データ型の定義に使用)
soap:SOAPプロトコルの定義バインディングに使用
⑥ xmlns="http://schemas.xmlsoap.org/wsdl/"
デフォルト名前空間です。
ポイント

名前空間は特に『URL』形式にする必要はありません。
何故全ての定義にURL形式が使われてるかは、バッティングを防ぐためです。
全世界中にある『WSDL』ファイルを仮に読込んだ時に、同じ名前空間名があると都合が悪いからです。
URL形式であれば、固有のドメイン名があるためバッティングする危険性がありませんよね。

よって、このURLは特に何処かに接続確認されている訳ではありません。
『definitions』タグをまとめると、以下の様な書式となります。
<definitions name="【任意のWSDLサービス名】"
targetNamespace="【URL①】"
xmlns:tns="【URL①】"
xmlns:xsd="【URL②】"
xmlns:soap="【URL③】"
xmlns="【URL④】">

○『types』タグ
XML Schema (XSD) を利用して、通信で扱うリクエスト/レスポンスのデータ構造や型(文字列、数値など)を定義します。
サンプルでは、以下の構造となっています。
① <xsd:schema...
スキーマの設定
② <xsd:element...
エレメントの設定は『リクエスト|レスポンス』を定義する外枠と『各種項目』を定義する内枠があります。
③ <xsd:complexType>
複数データがありますと宣言する(基本単数でも宣言する)
お約束と思ってください。
④ <xsd:sequence>
ここから各項目定義が始まる宣言 お約束と思ってください。
この定義は、ざっくり『リクエストに[userId]』が必要で『レスポンスに[userName][email]』を返却する。と言っています。
○『message』タグ
送受信される1つのメッセージ単位を定義します。
① <part ...
『types』タグ②外枠で指定した『name属性名』とマッピングします。
リクエスト・レスポンスがあるので、合計2つ作成します。
○『portType』タグ
実行可能なアクション(関数名)を定義します。
① <operation...
リクエストを受けた際の内部処理でコールされる関数名
② <input ...
『message』タグとマッピングします。
リクエスト・レスポンスがあるので、合計2つ作成します。
○『binding』タグ
<portType> で定義した抽象的な操作を、具体的にどのプロトコルで送信するかを指定します。
① <soap:binding...
XMLの形式を定義します。
また記述されたURLは通信プロトコルの定義なので【固定文字】となります。
② <operation ...
『portType』タグとのマッピングです。
③ <soap:operation
URL形式で記述する必要はありません。
ただし慣習としてURL形式とし、アプリ内のどの関数を処理するかの目安となるアドレスにします。
④ <input><soap:body...、<output><soap:body...
WSDLの定義通り、そのままXML要素として組み立てて送信することを意味します。
○『service』タグ
① <port ...
『binding』タグとマッピングします。
② <soap:address...
接続先サービスのエンドポイントを定義します

SOAP APIクライアント作成

先ほど紹介したサンプル『WSDL』を以下のパスで作成します。
    src/main/resources/META-INF/wsdl/UserService.wsdl
○ DTO作成(リクエスト側)
SoapRequestUserDto.java

package dto;

import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Data;

@Data
@XmlRootElement(name = "GetUserRequest", namespace = "http://example.com/userservice")
@XmlAccessorType(XmlAccessType.FIELD)
public class SoapRequestUserDto {
    @XmlElement(required = true, namespace = "http://example.com/userservice")
    private String userId;
}

上記DTOは『WSDL』の以下リクエスト部分の定義となります。
UserService.wsdl

<types>
    <xsd:schema targetNamespace="http://example.com/userservice">
        <xsd:element name="GetUserRequest">
            <xsd:complexType>
                <xsd:sequence>
                    <xsd:element name="userId" type="xsd:string"/>
                </xsd:sequence>
            </xsd:complexType>
        </xsd:element>
        ~ 省略 ~
</types>

○ DTO作成(レスポンス側)
SoapResponseUserDto.java

package dto;

import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Data;

@Data
@XmlRootElement(name = "GetUserResponse", namespace = "http://example.com/userservice")
@XmlAccessorType(XmlAccessType.FIELD)
public class SoapResponseUserDto {
    private String userName;
    private String email;
}

上記DTOは『WSDL』の以下リクエスト部分の定義となります。
UserService.wsdl

<types>
    ~ 省略 ~
        <xsd:element name="GetUserResponse">
            <xsd:complexType>
                <xsd:sequence>
                    <xsd:element name="userName" type="xsd:string"/>
                    <xsd:element name="email" type="xsd:string"/>
                </xsd:sequence>
            </xsd:complexType>
        </xsd:element>
    </xsd:schema>
</types>

○ SOAP通信インターフェース作成
『api.soap』に作成しましょう。
src/main/java/api/soap/UserPortType.java

package api.soap;

import dto.SoapRequestUserDto;
import dto.SoapResponseUserDto;
import jakarta.jws.WebMethod;
import jakarta.jws.WebParam;
import jakarta.jws.WebResult;
import jakarta.jws.WebService;
import jakarta.jws.soap.SOAPBinding;

@WebService(targetNamespace = "http://example.com/userservice", name = "UserPortType")
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT
    , use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface UserPortType {
    @WebMethod(operationName = "GetUser", action = "http://example.com/userservice/GetUser")
    @WebResult(name = "GetUserResponse", targetNamespace = "http://example.com/userservice", partName = "parameters")
    public SoapResponseUserDto getUser(
        @WebParam(name = "GetUserRequest", targetNamespace = "http://example.com/userservice", partName = "parameters")
        SoapRequestUserDto request);
}

上記インターフェースは『WSDL』の以下リクエスト部分の定義となります。
UserService.wsdl

<?xml version="1.0" encoding="UTF-8"?>
<definitions name="UserService"

    targetNamespace="http://example.com/userservice"
    xmlns:tns="http://example.com/userservice"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns="http://schemas.xmlsoap.org/wsdl/">
    <!-- データ型の定義 -->
    <types>

        <xsd:schema targetNamespace="http://example.com/userservice">
            <xsd:element name="GetUserRequest">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="userId" type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>

            <xsd:element name="GetUserResponse">
                <xsd:complexType>
                    <xsd:sequence>
                        <xsd:element name="userName" type="xsd:string"/>
                        <xsd:element name="email" type="xsd:string"/>
                    </xsd:sequence>
                </xsd:complexType>
            </xsd:element>
        </xsd:schema>
    </types>
    <!-- 電文(メッセージ)構造の定義 -->
    <message name="GetUserRequestMessage">

        <part name="parameters" element="tns:GetUserRequest"/>
    </message>
    <message name="GetUserResponseMessage">

        <part name="parameters" element="tns:GetUserResponse"/>
    </message>
    <!-- インターフェース(操作)の定義 -->

    <portType name="UserPortType">
        <operation name="GetUser">
            <input message="tns:GetUserRequestMessage"/>
            <output message="tns:GetUserResponseMessage"/>
        </operation>
    </portType>
    <!-- 通信プロトコルとデータフォーマットのバインディング -->

    <binding name="UserBinding" type="tns:UserPortType">">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="GetUser">
            <soap:operation soapAction="http://example.com/userservice/GetUser"/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>
    <!-- 接続先サービスのエンドポイント定義 -->
    <service name="UserService">
        <port name="UserPort" binding="tns:UserBinding">
            <soap:address location="https://api.example.com/UserService"/>
        </port>
    </service>
</definitions>

○ SOAP APIクライアント作成
それでは、いよいよクライアントの作成です。
src/main/java/api/soap/SoapUserApiClient.java

package api.soap;

import java.net.URL;
import javax.xml.namespace.QName;
import dto.SoapRequestUserDto;
import dto.SoapResponseUserDto;
import jakarta.enterprise.context.RequestScoped;
import jakarta.xml.ws.Service;

@RequestScoped
public class SoapUserApiClient {
    public void getUser() {
        try {
            // WSDLのURLとQName(名前空間URI + サービス名)を指定
            URL wsdlUrl = SoapUserApiClient.class.getClassLoader()
                     .getResource("META-INF/wsdl/UserService.wsdl");
            if (wsdlUrl == null) {
                throw new IllegalArgumentException("WSDLファイルがリソース配下に見つかりません。");
            }
            QName serviceName = new QName("http://example.com/userservice", "UserService");
            // サービスを動的生成
            Service service = Service.create(wsdlUrl, serviceName);
            // 定義したインターフェースを指定してポートを取得
            UserPortType port = service.getPort(UserPortType.class);
            // 通信実行
            SoapRequestUserDto request = new SoapRequestUserDto();
            request.setUserId("12345");
            SoapResponseUserDto response = port.getUser(request);
            System.out.println("User Name : " + response.getUserName());
            System.out.println("Email : " + response.getEmail());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

● 処理の流れ
先ずは『WSDL』ファイルを読込みます。
次に『QName』を設定します。
第1引数:最終的な「QName」を一意とするため『targetNamespace』の値とします。
第2引数:service name="UserService">の『name属性名』の値とします。
次に『service』インスタンスを作成します。
第1引数:WSDLファイルの場所
第2引数:QName
次に『port』インスタンスを『service』から作成します。
リクエストDTOに値を設定し『port』インスタンスから、対象のメソッド名を指定してAPIリクエストします。
    ※実際は『WSDL』に定義しているURL「https://api.example.com/UserService」は存在しないのでエラーになります

PHP側にSOAPサーバを立てる

○ SOAPがPHPで使用できるか確認
『phpinfo();』で確認します。
まずは『php.ini』の保存パスを探して下さい。
対象モジュールは以下となりますので『;』が付いている場合は外します。
    ;extension=soap
※当然apacheは再起動します
○ SOAPサーバ作成
まず常設のサーバにはしないので、サッと作成してサッと削除できる様に『webroot/soap』に作成します。
○ UserService.wsdl
当たり前ですが、JAVA側と同じ内容となります。
ただし今回はPHP側のサービスに本当に接続するため、両『WSDL』ファイルの以下の部分を修正します。
【修正前】<soap:address location="https://api.example.com/UserService"/>
【修正後】<soap:address location="http://localhost/soap/SoapService.php"/>
○ UserService.php
『WSDL』ファイルの以下の部分の関数と、それに必要なクラスを定義します。
      <operation name="GetUser">
UserService.php

<?php
namespace App;

// リクエスト用 DTO
class GetUserRequest {
    public string $userId;
}
// レスポンス用 DTO
class GetUserResponse {
    public string $userName;
    public string $email;
}
// SOAP サービス本体
class UserService {
    // GetUser 操作(メソッド)
    public function GetUser(GetUserRequest $request): GetUserResponse
    {
        $response = new GetUserResponse();
        if ($request->userId === '12345') {
            $response->userName = '山田 太郎';
            $response->email = '[email protected]';
        } else {
            $response->userName = 'Unknown User';
            $response->email = '[email protected]';
        }
        return $response;
    }
}

リクエスト、レスポンス用のクラス定義を作成します。
関数名は『WSDL』ファイルの定義通り『GetUser』とします。
処理内容は単純で、リクエストされた『userId = '12345'』か、それ以外かで返却内容を制御しています。
○ SoapService.php
PHP側のエンドポイント本体を作成します。
SoapService.php

<?php
// ロジックファイルを読み込む
require_once __DIR__ . '/UserService.php';

use App\UserService;
use App\GetUserRequest;
use App\GetUserResponse;

// 開発時はWSDLのキャッシュをオフにする
ini_set('soap.wsdl_cache_enabled', '0');

// WSDLファイルのパスとオプション(同じ soap フォルダ内にある想定)
$wsdl = __DIR__ . '/UserService.wsdl';

$options = [
    'soap_version' => 1,
    'encoding'     => 'UTF-8',
    'classmap'     => [
        'GetUserRequest' => GetUserRequest::class,
        'GetUserResponse' => GetUserResponse::class,
        ]
];
try {
    // SoapServer インスタンス作成
    $server = new \SoapServer($wsdl, $options);
    // 処理クラスをセット
    $server->setClass(UserService::class);
    // リクエストの処理を実行(JavaからのPOSTを受けてXMLを返す)
    $server->handle();
} catch (\Exception $e) {
    http_response_code(500);
    echo $e->getMessage();
}

処理の流れは以下です。
①『WSDL』のキャッシュをオフにする
開発時は本番時と違い、WSDLの内容を変更しがちなので設定します。
②『WSDL』の保存パスの設定
③『options』の設定
重要なのは『classmap』で、リクエスト・レスポンスクラスを設定する事で自動的にマッピングします。
④『server』インスタンスを②③で初期化します。
⑤ 更に使用するクラス『UserService』をセットします。
⑥『$server->handle();』を実行する事で『UserService.GetUser』が発火します。
○ SOAPサーバ確認
それでは以下を叩いてSOAPサーバが正常に起動しているか確認します。
    http://localhost/soap/SoapService.php
特にエラーが画面に表示されなければ成功です!
JAVA側からもアクセスしてみて下さい。
今度はエラーにならず以下期待した値が返却されたハズです。
System.out.println("User Name : " + response.getUserName());
System.out.println("Email : " + response.getEmail());

複雑な返却値

○ 複雑な返却値①
これまでのサンプルでは、単純に以下内容を返却するだけの単純な構造でした。
・ユーザーID
・メールアドレス
例えば、メールアドレスは個人で複数保持する事ができますよね?
では返却も複数(List型)で返却する様にカスタマイズしてみましょう。
○ WSDLファイル
現状返却値である『userName』『email』の定義は以下の様になっています。
UserService.wsdl

<xsd:sequence>
    <xsd:element name="userName" type="xsd:string"/>
    <xsd:element name="email" type="xsd:string"/>
</xsd:sequence>

この『email』を複数返却可能にするには『maxOccurs』属性を追加します。
UserService.wsdl

<xsd:sequence>
    <xsd:element name="userName" type="xsd:string"/>
    <xsd:element name="email" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>

『maxOccurs="unbounded"』とする事で複数の定義となります。
また『minOccurs="0"』は、空の配列を許容する場合の属性となります。
○ SoapResponseUserDto.java
DTOの定義も修正する必要があります。
private String email;

private List<String> email;
○ PHP側の修正
・WSDLファイルは当然同じ内容としてください。
・UserService.php
『UserService』クラスのプロパティ『public string $email;』を配列定義にします。
また、返却で以下配列を定義して、それを返却値とします。
    $mails = ['[email protected]', '[email protected]', '[email protected]'];
UserService.php

<?php
namespace App;

// リクエスト用 DTO
class GetUserRequest {
    public string $userId;
}
// レスポンス用 DTO
class GetUserResponse {
    public string $userName;

    public array $email;
}
// SOAP サービス本体
class UserService {
    // GetUser 操作(メソッド)
    public function GetUser(GetUserRequest $request): GetUserResponse
    {

        $emails = ['hoge@hoge.com', 'foo@foo.com', 'bar@bar.com'];
        $response = new GetUserResponse();
        if ($request->userId === '12345') {
            $response->userName = '山田 太郎';

            $response->email = $emails;
        } else {
            $response->userName = 'Unknown User';

            $response->email = $emails;
        }
        return $response;
    }
}

項目『email』が配列で返却されるのが確認できるハズです。
○ 複雑な返却値②
返却値の構造の中に、更に新しい構造がネストされている場合はどうでしょう。
つまり以下の様な構造です。
イメージ

<GetUserResponse>
    <userName>12345</userName>
    <email>[email protected]</email>
    <email>[email protected]</email>
    <email>[email protected]</email>

    <profile>
        <tel>09012345678</tel>
        <sex>man</sex>
        <address>東京都</address>
    </profile>
</GetUserResponse>

この場合は、大外の項目『profile』は配列とほぼ同様の定義となります。
ただし、中身の定義をするための『マッピング名』を指定し、中身を別の定義で記述します。
UserService.wsdl

<types>
    <xsd:schema targetNamespace="http://example.com/userservice"

        xmlns:tns="http://example.com/userservice"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
        <xsd:element name="GetUserRequest">
            <xsd:complexType>
                <xsd:sequence>
                    <xsd:element name="userId" type="xsd:string"/>
                </xsd:sequence>
            </xsd:complexType>
        </xsd:element>
        <xsd:element name="GetUserResponse">
            <xsd:complexType>
                <xsd:sequence>
                    <xsd:element name="userName" type="xsd:string"/>
                    <xsd:element name="email" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>

                    <xsd:element name="profile" type="tns:ProfileItem" minOccurs="0" maxOccurs="unbounded"/>
                </xsd:sequence>
            </xsd:complexType>
        </xsd:element>

        <xsd:complexType name="ProfileItem">
            <xsd:sequence>
                <xsd:element name="key" type="xsd:string"/>
                <xsd:element name="value" type="xsd:string"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:schema>
</types>

○ 『ProfileItem』クラスを修正
以下定義の通り『profile』インスタンス、『ProfileItem』クラスを追加します。
    <xsd:element name="profile" type="tns:ProfileItem" minOccurs="0" maxOccurs="unbounded"/>
SoapResponseUserDto.java

package dto;

import java.util.ArrayList;
import java.util.List;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Data;

@Data
@XmlRootElement(name = "GetUserResponse", namespace = "http://example.com/userservice")
@XmlAccessorType(XmlAccessType.FIELD)
public class SoapResponseUserDto {
    private String userName;
    private List<String> email;

    private List<ProfileItem> profile = new ArrayList<>();
    
    @Data
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class ProfileItem {
        private String key;
        private String value;
    }
}

プロパティ『private List<ProfileItem> profile = new ArrayList<>();』を追加します。
このプロパティは『ProfileItem』型とし、本体はサブクラスとして定義します。
サブクラス内のプロパティは、PHPの返却方法と同様に『key』『value』となります。
○ PHP側の修正
WSDLファイルは当然同じ内容としてください。
WSDLファイルで定義した『ProfileItem』クラスを追加します。
UserService.php

class ProfileItem {
    public string $key;
    public string $value;
    public function __construct(string $key, string $value) {
        $this->key = $key;
        $this->value = $value;
    }
}

次に、既存『GetUserResponse』にプロパティ『public array $profile;』を追加します。
UserService.php

public array $profile;
~ 省略 ~
    $mails = ['hoge@hoge.com', 'foo@foo.com', 'bar@bar.com'];
    $response = new GetUserResponse();
if ($request->userId === '12345') {
    $response->userName = '山田 太郎';
    $response->email = $mails;
    $response->profile = [
        new ProfileItem('tel', '09012345678'),
        new ProfileItem('sex', 'man'),
        new ProfileItem('address', '東京都'),
    ];
} else {

以下が完成した『UserService.php』ファイルです。
UserService.php

<?php
namespace App;

// リクエスト用 DTO
class GetUserRequest {
    public string $userId;
}
// レスポンス用 DTO
class GetUserResponse {
    public string $userName;
    public array $email;

    public array $profile;
}
// SOAP サービス本体
class UserService {
    // GetUser 操作(メソッド)
    public function GetUser(GetUserRequest $request): GetUserResponse
    {

        $emails = ['hoge@hoge.com', 'foo@foo.com', 'bar@bar.com'];
        $response = new GetUserResponse();
        if ($request->userId === '12345') {
            $response->userName = '山田 太郎';
            $response->email = $emails;

            $response->profile = [
                new ProfileItem('tel', '09012345678'),
                new ProfileItem('sex', 'man'),
                new ProfileItem('address', '東京都'),
            ];
        } else {
            $response->userName = 'Unknown User';
            $response->email = $emails;
        }
        return $response;
    }
}
// ProfileItemクラス追加

class ProfileItem {
    public string $key;
    public string $value;
    public function __construct(string $key, string $value) {
        $this->key = $key;
        $this->value = $value;
    }
}

○ SOAPサービスクラス修正
マッピングクラスが増えたので追加します。
SoapService.php

$options = [
    'soap_version' => 1, // または 1
    'encoding'     => 'UTF-8',
    'classmap'     => [
            'GetUserRequest' => GetUserRequest::class,
            'GetUserResponse' => GetUserResponse::class,

            'ProfileItem'     => \App\ProfileItem::class,
        ]
];

○ 確認方法
正常に返却値を受取れるはずですが、少し複雑な内容となってきましたのでDTOからの取出し方を解説します。
SoapUserApiClient.java

request.setUserId("12345");
SoapResponseUserDto response = port.getUser(request);
// そのまま取出せます
System.out.println("User Name : " + response.getUserName());
// 配列として取出す
for (String mail : response.getEmail()) {
    System.out.println("Email     : " + mail);
}
// 配列として取出すが、値が『key』『value』あります
for (ProfileItem prof : response.getProfile()) {
    System.out.println(prof.getKey() + " : " + prof.getValue()); }

『SOAP API』の解説は以上となります。
どうでしたか?
二度と関わりたくない通信方式なのがご理解できたと思いますw
ログインしてコメントを残そう!!


きっぷる