-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexamples.php
More file actions
254 lines (209 loc) · 8.79 KB
/
Copy pathexamples.php
File metadata and controls
254 lines (209 loc) · 8.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
<?php
/**
* PHP Typed Arrays & Array Shapes - Examples
*
* Two complementary features:
* 1. Typed Collections: array<ClassName> for lists of objects
* 2. Array Shapes: array{key: type} for structured data from external sources
*
* Run with: ./php-src/sapi/cli/php examples.php
*/
echo "=== PHP Typed Arrays & Array Shapes ===\n\n";
// =============================================================================
// PART 1: TYPED COLLECTIONS — array<ClassName>
// =============================================================================
echo "--- PART 1: Typed Collections (array<ClassName>) ---\n\n";
// DTOs for internal domain logic
class User {
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly string $email
) {}
public function getDisplayName(): string {
return strtoupper($this->name);
}
}
class Product {
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly float $price
) {}
}
// -----------------------------------------------------------------------------
// 1. Typed Collection: array<User>
// -----------------------------------------------------------------------------
function getUsers(): array<User> {
return [
new User(1, 'Alice', 'alice@example.com'),
new User(2, 'Bob', 'bob@example.com'),
new User(3, 'Charlie', 'charlie@example.com'),
];
}
echo "1. array<User> - Collection of DTOs:\n";
foreach (getUsers() as $user) {
echo " - {$user->getDisplayName()} ({$user->email})\n";
}
echo "\n";
// -----------------------------------------------------------------------------
// 2. Repository returning typed collection
// -----------------------------------------------------------------------------
class UserRepository {
private array $users;
public function __construct() {
$this->users = [
1 => new User(1, 'Alice', 'alice@example.com'),
2 => new User(2, 'Bob', 'bob@example.com'),
];
}
public function findAll(): array<User> {
return array_values($this->users);
}
public function findByIds(array<int> $ids): array<User> {
return array_filter(
$this->users,
fn($u) => in_array($u->id, $ids)
);
}
}
$repo = new UserRepository();
echo "2. Repository with array<User>:\n";
echo " findAll(): " . count($repo->findAll()) . " users\n";
echo " findByIds([1]): " . count($repo->findByIds([1])) . " user\n\n";
// =============================================================================
// PART 2: ARRAY SHAPES — For External Data (APIs, Databases)
// =============================================================================
echo "--- PART 2: Array Shapes (External Data Boundaries) ---\n\n";
// -----------------------------------------------------------------------------
// 3. Database query result - PDO returns arrays, not objects
// -----------------------------------------------------------------------------
function fetchUserFromDb(int $id): array{id: int, name: string, email: string, created_at: string} {
// Simulating: $stmt->fetch(PDO::FETCH_ASSOC)
return [
'id' => $id,
'name' => 'Alice',
'email' => 'alice@example.com',
'created_at' => '2024-01-15 10:30:00'
];
}
echo "3. Database result (array shape):\n";
$dbUser = fetchUserFromDb(1);
echo " Fetched: {$dbUser['name']} (ID: {$dbUser['id']})\n\n";
// -----------------------------------------------------------------------------
// 4. API response - json_decode returns arrays
// -----------------------------------------------------------------------------
function fetchGitHubUser(string $username): array{login: string, id: int, name: ?string, public_repos: int} {
// Simulating: json_decode($response, true)
return [
'login' => $username,
'id' => 12345,
'name' => 'Alice Developer',
'public_repos' => 42
];
}
echo "4. API response (array shape):\n";
$ghUser = fetchGitHubUser('alice');
echo " GitHub user: {$ghUser['login']} has {$ghUser['public_repos']} repos\n\n";
// -----------------------------------------------------------------------------
// 5. Webhook payload - external data you don't control
// -----------------------------------------------------------------------------
function handleWebhook(array{event: string, data: array{id: string, amount: int}} $payload): string {
return "Received {$payload['event']} for {$payload['data']['id']}: \${$payload['data']['amount']}";
}
$webhookPayload = [
'event' => 'payment.completed',
'data' => ['id' => 'pay_123', 'amount' => 5000]
];
echo "5. Webhook payload (array shape):\n";
echo " " . handleWebhook($webhookPayload) . "\n\n";
// =============================================================================
// PART 3: THE BOUNDARY PATTERN — Arrays In, DTOs Inside
// =============================================================================
echo "--- PART 3: The Boundary Pattern ---\n\n";
// -----------------------------------------------------------------------------
// 6. Convert array shape to DTO at the boundary
// -----------------------------------------------------------------------------
class UserService {
// External boundary: array shape from API/database
private function fetchFromApi(int $id): array{id: int, name: string, email: string} {
// Simulating external API call
return ['id' => $id, 'name' => 'Alice', 'email' => 'alice@example.com'];
}
// Internal: returns DTO with behavior
public function getUser(int $id): User {
$data = $this->fetchFromApi($id); // Array shape at boundary
return new User( // Convert to DTO
$data['id'],
$data['name'],
$data['email']
);
}
}
$service = new UserService();
$user = $service->getUser(1);
echo "6. Boundary pattern - Array -> DTO:\n";
echo " External data fetched as array shape\n";
echo " Converted to User DTO: {$user->getDisplayName()}\n\n";
// -----------------------------------------------------------------------------
// 7. Collection of shapes from database, converted to DTOs
// -----------------------------------------------------------------------------
function fetchAllUsersFromDb(): array<array{id: int, name: string, email: string}> {
// Simulating: fetchAll(PDO::FETCH_ASSOC)
return [
['id' => 1, 'name' => 'Alice', 'email' => 'alice@example.com'],
['id' => 2, 'name' => 'Bob', 'email' => 'bob@example.com'],
];
}
function convertToUsers(array<array{id: int, name: string, email: string}> $rows): array<User> {
return array_map(
fn($row) => new User($row['id'], $row['name'], $row['email']),
$rows
);
}
echo "7. Database rows -> DTO collection:\n";
$rows = fetchAllUsersFromDb();
echo " Fetched " . count($rows) . " rows as array<shape>\n";
$users = convertToUsers($rows);
echo " Converted to " . count($users) . " User DTOs\n\n";
// =============================================================================
// PART 4: COMBINING BOTH — Real-World Patterns
// =============================================================================
echo "--- PART 4: Combined Real-World Patterns ---\n\n";
// -----------------------------------------------------------------------------
// 8. API response containing typed collection
// -----------------------------------------------------------------------------
function getApiResponse(): array{
success: bool,
data: array<User>,
meta: array{total: int, page: int}
} {
return [
'success' => true,
'data' => getUsers(),
'meta' => ['total' => 3, 'page' => 1]
];
}
echo "8. API response with array<User>:\n";
$response = getApiResponse();
echo " Success: " . ($response['success'] ? 'yes' : 'no') . "\n";
echo " Users: " . count($response['data']) . "\n";
echo " Page: {$response['meta']['page']} of {$response['meta']['total']}\n\n";
// =============================================================================
// Summary
// =============================================================================
echo "=== Summary ===\n\n";
echo "Array Shapes vs DTOs - When to use what:\n\n";
echo " USE ARRAY SHAPES for:\n";
echo " - Database query results (PDO returns arrays)\n";
echo " - API responses (json_decode returns arrays)\n";
echo " - Webhook payloads (external data)\n";
echo " - Configuration files\n\n";
echo " USE DTOs/CLASSES for:\n";
echo " - Domain entities with behavior (methods)\n";
echo " - Internal application state\n";
echo " - Complex business logic\n\n";
echo " THE BOUNDARY PATTERN:\n";
echo " - Array shapes at edges (external data in)\n";
echo " - Convert to DTOs for internal logic\n";
echo " - Best of both worlds!\n";