Pular para o conteúdo principal

Complex Types

Learn how to work with custom classes, objects, and complex data structures in your SOAP services.

Overview

SOAP services often need to work with complex data types beyond simple strings and integers. The byjg/soap-server library fully supports custom PHP classes as SOAP complex types.

Defining Complex Types

Complex types are simple PHP classes with public properties:

class User
{
public string $username;
public string $email;
public int $age;
public bool $active;
}
informação

Type Hints Required

All public properties must have type hints. The library uses these to generate the WSDL schema.

Using Complex Types with Attributes

As Parameters

use ByJG\SoapServer\Attributes\{SoapService, SoapOperation};

#[SoapService(serviceName: 'UserService')]
class UserService
{
#[SoapOperation(description: 'Creates a new user')]
public function createUser(User $user): bool
{
// Save user to database
return true;
}
}

As Return Types

#[SoapOperation(description: 'Gets user by ID')]
public function getUser(int $id): ?User
{
// Fetch from database
$user = new User();
$user->username = 'john_doe';
$user->email = '[email protected]';
$user->age = 30;
$user->active = true;
return $user;
}

Arrays of Complex Types

#[SoapOperation(description: 'Gets all users')]
public function getAllUsers(): array
{
return [
$user1,
$user2,
$user3
];
}

Use PHPDoc for better documentation:

/**
* @return User[] Array of users
*/
#[SoapOperation(description: 'Gets all active users')]
public function getActiveUsers(): array
{
// Returns array of User objects
}

Using Complex Types Programmatically

When using programmatic configuration, specify the class name as a string:

use ByJG\SoapServer\{SoapHandler, SoapOperationConfig, SoapParameter};

$operation = new SoapOperationConfig();
$operation->description = 'Creates a user';
$operation->args = [
new SoapParameter('user', User::class) // Use class name
];
$operation->returnType = SoapType::Boolean;
$operation->executor = function(array $params) {
$user = $params['user']; // stdClass with user properties
// Process user
return true;
};

Nested Complex Types

Complex types can contain other complex types:

class Address
{
public string $street;
public string $city;
public string $zipCode;
public string $country;
}

class User
{
public string $username;
public string $email;
public Address $address; // Nested complex type
}

#[SoapOperation(description: 'Creates user with address')]
public function createUser(User $user): bool
{
echo $user->address->city; // Access nested properties
return true;
}

Arrays in Complex Types

Complex types can have array properties:

class User
{
public string $username;
public string $email;

/**
* @var string[] Array of roles
*/
public array $roles;
}

#[SoapOperation(description: 'Creates user with roles')]
public function createUser(User $user): bool
{
foreach ($user->roles as $role) {
echo "Role: $role\n";
}
return true;
}
aviso

Array Types

Use PHPDoc @var annotations to specify array element types, as PHP doesn't support typed arrays natively.

Nullable Complex Types

Use nullable types to make complex types optional:

#[SoapOperation(description: 'Updates user optionally with new address')]
public function updateUser(
int $userId,
?Address $newAddress = null
): bool {
if ($newAddress !== null) {
// Update address
}
return true;
}

Complete Example

<?php

use ByJG\SoapServer\Attributes\{SoapService, SoapOperation, SoapParameter};
use ByJG\SoapServer\SoapAttributeParser;

// Define complex types
class Address
{
public string $street;
public string $city;
public string $zipCode;
public string $country;
}

class User
{
public int $id;
public string $username;
public string $email;
public Address $address;

/**
* @var string[]
*/
public array $roles;

public bool $active;
}

class CreateUserRequest
{
public string $username;
public string $email;
public Address $address;

/**
* @var string[]
*/
public array $roles;
}

// Define service
#[SoapService(
serviceName: 'UserManagementService',
namespace: 'http://example.com/users',
description: 'User management web service'
)]
class UserManagementService
{
#[SoapOperation(description: 'Creates a new user')]
public function createUser(CreateUserRequest $request): User
{
$user = new User();
$user->id = rand(1, 1000);
$user->username = $request->username;
$user->email = $request->email;
$user->address = $request->address;
$user->roles = $request->roles;
$user->active = true;

return $user;
}

#[SoapOperation(description: 'Gets user by ID')]
public function getUser(
#[SoapParameter(description: 'User ID')]
int $id
): ?User {
// Fetch from database
if ($id <= 0) {
return null;
}

$user = new User();
$user->id = $id;
$user->username = 'john_doe';
$user->email = '[email protected]';

$address = new Address();
$address->street = '123 Main St';
$address->city = 'Springfield';
$address->zipCode = '12345';
$address->country = 'USA';

$user->address = $address;
$user->roles = ['user', 'admin'];
$user->active = true;

return $user;
}

/**
* @return User[]
*/
#[SoapOperation(description: 'Gets all users')]
public function getAllUsers(): array
{
return [
$this->getUser(1),
$this->getUser(2),
$this->getUser(3)
];
}

#[SoapOperation(description: 'Updates user address')]
public function updateUserAddress(
#[SoapParameter(description: 'User ID')]
int $userId,

#[SoapParameter(description: 'New address')]
Address $address
): bool {
// Update address in database
return true;
}
}

// Start the service
use ByJG\SoapServer\ResponseWriter;

$handler = SoapAttributeParser::parse(UserManagementService::class);
$response = $handler->handle();
ResponseWriter::output($response);

SOAP Request Example

Here's how a client would send a complex type:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<createUser xmlns="http://example.com/users">
<request>
<username>john_doe</username>
<email>[email protected]</email>
<address>
<street>123 Main St</street>
<city>Springfield</city>
<zipCode>12345</zipCode>
<country>USA</country>
</address>
<roles>user</roles>
<roles>admin</roles>
</request>
</createUser>
</soap:Body>
</soap:Envelope>

PHP Client Example

<?php

$client = new SoapClient('http://localhost:8080?WSDL');

// Create request object
$request = new stdClass();
$request->username = 'john_doe';
$request->email = '[email protected]';

$address = new stdClass();
$address->street = '123 Main St';
$address->city = 'Springfield';
$address->zipCode = '12345';
$address->country = 'USA';

$request->address = $address;
$request->roles = ['user', 'admin'];

// Call service
$user = $client->createUser($request);

echo "Created user: {$user->username} with ID: {$user->id}\n";
echo "Lives in: {$user->address->city}, {$user->address->country}\n";

Best Practices

dica

Best Practices

  1. Use type hints: Always type hint all public properties
  2. Document arrays: Use PHPDoc for array types
  3. Create DTOs: Use separate request/response classes for clarity
  4. Keep it simple: Avoid deeply nested structures when possible
  5. Validate data: Validate complex types in your executors
  6. Use nullable types: For optional complex parameters

Next Steps