Copy docker run --rm zchencow/innozverse-php:latest php -r '
<?php
// ── SimpleXML ─────────────────────────────────────────────────────────────────
echo "=== SimpleXML ===" . PHP_EOL;
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<catalog version="2.0">
<product id="1" category="laptop">
<name>Surface Pro</name>
<price currency="USD">864.00</price>
<stock>15</stock>
<tags>
<tag>microsoft</tag>
<tag>premium</tag>
<tag>portable</tag>
</tags>
</product>
<product id="2" category="accessory">
<name>Surface Pen</name>
<price currency="USD">49.99</price>
<stock>80</stock>
<tags>
<tag>stylus</tag>
<tag>4096-pressure</tag>
</tags>
</product>
<product id="3" category="software">
<name>Office 365</name>
<price currency="USD">99.99</price>
<stock>999</stock>
<tags><tag>subscription</tag></tags>
</product>
</catalog>
XML;
$catalog = simplexml_load_string($xml);
echo "Catalog version: " . $catalog["version"] . PHP_EOL;
echo "Products: " . count($catalog->product) . PHP_EOL;
foreach ($catalog->product as $p) {
$tags = array_map("strval", iterator_to_array($p->tags->tag));
printf(" #%s [%-9s] %-15s \$%-8s stock=%-4s tags=%s%s",
$p["id"], $p["category"], $p->name, $p->price, $p->stock,
implode(",", $tags), PHP_EOL);
}
// XPath queries
$laptops = $catalog->xpath("//product[@category='laptop']");
echo PHP_EOL . "XPath laptops: " . count($laptops) . " found" . PHP_EOL;
$expensive = $catalog->xpath("//product[price > 100]");
echo "XPath price>100: " . implode(", ", array_map(fn($p) => (string)$p->name, $expensive)) . PHP_EOL;
// ── DOMDocument — modify and output XML ──────────────────────────────────────
echo PHP_EOL . "=== DOMDocument (modify XML) ===" . PHP_EOL;
$dom = new DOMDocument("1.0", "UTF-8");
$dom->formatOutput = true;
$dom->loadXML($xml);
$xpath = new DOMXPath($dom);
// Add discount attribute to all laptops
foreach ($xpath->query("//product[@category='laptop']") as $node) {
$node->setAttribute("discount", "10%");
}
// Add new product node
$newProd = $dom->createElement("product");
$newProd->setAttribute("id", "4");
$newProd->setAttribute("category", "hardware");
$newProd->appendChild($dom->createElement("name", "USB-C Hub"));
$newProd->appendChild($dom->createElement("price", "29.99"));
$newProd->appendChild($dom->createElement("stock", "50"));
$dom->documentElement->appendChild($newProd);
$output = $dom->saveXML();
echo "Modified XML snippet:" . PHP_EOL;
foreach (array_slice(explode(PHP_EOL, $output), 0, 8) as $line) {
echo " " . $line . PHP_EOL;
}
echo " ..." . PHP_EOL;
// ── HTTP client with retry ────────────────────────────────────────────────────
echo PHP_EOL . "=== HTTP Client (curl) with Retry ===" . PHP_EOL;
class HttpClient {
private array $defaultHeaders = [
"Content-Type: application/json",
"Accept: application/json",
];
public function __construct(
private string $baseUrl = "",
private int $maxRetries = 3,
private int $timeoutSec = 5,
) {}
public function get(string $path, array $params = [], array $headers = []): array {
$url = $this->baseUrl . $path;
if ($params) $url .= "?" . http_build_query($params);
return $this->request("GET", $url, null, $headers);
}
public function post(string $path, array $body, array $headers = []): array {
return $this->request("POST", $this->baseUrl . $path, $body, $headers);
}
private function request(string $method, string $url, ?array $body, array $headers): array {
$attempt = 0;
$lastError = null;
while ($attempt < $this->maxRetries) {
$attempt++;
try {
return $this->doRequest($method, $url, $body, $headers);
} catch (\RuntimeException $e) {
$lastError = $e;
$backoff = min(pow(2, $attempt - 1) * 100, 1000); // 100ms, 200ms, 400ms...
echo " Retry {$attempt}/{$this->maxRetries} after {$backoff}ms: " . $e->getMessage() . PHP_EOL;
usleep($backoff * 1000);
}
}
throw new \RuntimeException("Max retries exceeded: " . $lastError->getMessage());
}
private function doRequest(string $method, string $url, ?array $body, array $extraHeaders): array {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $this->timeoutSec,
CURLOPT_HTTPHEADER => array_merge($this->defaultHeaders, $extraHeaders),
CURLOPT_CUSTOMREQUEST => $method,
]);
if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) throw new \RuntimeException("cURL error: {$error}");
if ($httpCode >= 500) throw new \RuntimeException("Server error {$httpCode}");
$decoded = json_decode($response, true, flags: JSON_THROW_ON_ERROR);
return ["status" => $httpCode, "body" => $decoded];
}
}
// Simulate API calls (httpbin.org-style simulation using data:// stream)
// We test the retry logic with a mock that fails then succeeds
$failCount = 0;
$mockClient = new class("") extends HttpClient {
public int $attempts = 0;
public function testRetry(int $failTimes): array {
$this->attempts = 0;
$maxFail = $failTimes;
while (true) {
$this->attempts++;
if ($this->attempts <= $maxFail) {
echo " Attempt {$this->attempts}: simulated 503 error" . PHP_EOL;
if ($this->attempts < 3) {
usleep(50_000);
continue;
}
throw new \RuntimeException("Max retries exceeded");
}
return ["status" => 200, "body" => ["result" => "success", "attempt" => $this->attempts]];
}
}
};
// Test with 2 failures then success
echo "Retry test (fail 2 times then succeed):" . PHP_EOL;
try {
$result = $mockClient->testRetry(2);
echo " ✓ Succeeded on attempt {$result["body"]["attempt"]}" . PHP_EOL;
} catch (\RuntimeException $e) {
echo " ✗ " . $e->getMessage() . PHP_EOL;
}
// Webhook signature verification
echo PHP_EOL . "=== Webhook Signature Verification ===" . PHP_EOL;
$secret = "wh_secret_inno_2026";
$payload = json_encode(["event" => "order.placed", "orderId" => 1001, "total" => 864.00]);
$sig = "sha256=" . hash_hmac("sha256", $payload, $secret);
echo " Payload: " . $payload . PHP_EOL;
echo " Signature: " . $sig . PHP_EOL;
// Verify incoming webhook (constant-time comparison)
function verifyWebhook(string $payload, string $signature, string $secret): bool {
$expected = "sha256=" . hash_hmac("sha256", $payload, $secret);
return hash_equals($expected, $signature); // constant-time, prevents timing attacks
}
echo " Valid sig: " . (verifyWebhook($payload, $sig, $secret) ? "✓" : "✗") . PHP_EOL;
echo " Bad sig: " . (verifyWebhook($payload, "sha256=bad", $secret) ? "✓" : "✗") . PHP_EOL;
echo " Tampered: " . (verifyWebhook($payload . "hack", $sig, $secret) ? "✓" : "✗") . PHP_EOL;
'