Ten przewodnik pokazuje, jak zbudować skalowalny backend do sprzedaży biletów na wydarzenia oparty na NFT w PHP, wykorzystując Symfony Messenger do bezpiecznej i niezawodnej obsługi opóźnień blockchain.Ten przewodnik pokazuje, jak zbudować skalowalny backend do sprzedaży biletów na wydarzenia oparty na NFT w PHP, wykorzystując Symfony Messenger do bezpiecznej i niezawodnej obsługi opóźnień blockchain.

Budowa Zdecentralizowanego Systemu Biletowania Wydarzeń Web3 z Symfony 7.4

2025/12/22 01:43

Przecięcie Web3 i tradycyjnych frameworków webowych to miejsce, w którym zaczyna się rzeczywista użyteczność. Choć cykle szumu przychodzą i odchodzą, użyteczność tokenów niezamiennych (NFT) do weryfikacji własności — szczególnie w przypadku biletowania wydarzeń — pozostaje solidnym przypadkiem użycia.

W tym artykule zbudujemy podstawy zdecentralizowanego systemu biletowania wydarzeń przy użyciu Symfony 7.4 i PHP 8.3. Wyjdziemy poza podstawowe tutoriale i zaimplementujemy architekturę produkcyjną, która obsługuje asynchroniczny charakter transakcji blockchain przy użyciu komponentu Symfony Messenger.

Architektura

Podejście „Senior" uznaje, że PHP nie jest długo działającym procesem jak Node.js. Dlatego nie nasłuchujemy zdarzeń blockchain w czasie rzeczywistym w kontrolerze. Zamiast tego używamy podejścia hybrydowego:

  1. Bezpośrednia interakcja (zapis): Używamy Symfony Messenger do przeniesienia transakcji „Mintowania" do workera, zapobiegając timeoutom HTTP.
  2. Odpytywanie RPC (odczyt): Używamy zaplanowanych poleceń do weryfikacji statusu on-chain.
  3. Inteligentny kontrakt: Zakładamy standardowy kontrakt ERC-721 wdrożony w łańcuchu kompatybilnym z EVM (Ethereum, Polygon, Base).

Wymagania wstępne i stos technologiczny

  • PHP: 8.3+
  • Symfony: 7.4 (LTS)
  • Węzeł Blockchain: Infura, Alchemy lub lokalny węzeł Hardhat.

Wiele bibliotek PHP Web3 jest porzuconych lub słabo typowanych. Chociaż web3p/web3.php jest najbardziej znana, ścisłe poleganie na niej może być ryzykowne ze względu na luki w utrzymaniu.

W tym przewodniku użyjemy web3p/web3.php (wersja ^0.3) do kodowania ABI, ale wykorzystamy natywny HttpClient Symfony do rzeczywistego transportu JSON-RPC. Daje nam to pełną kontrolę nad timeoutami, ponownymi próbami i logowaniem — kluczowymi dla aplikacji produkcyjnych.

Konfiguracja projektu

Najpierw zainstalujmy zależności. Potrzebujemy środowiska uruchomieniowego Symfony, klienta HTTP i biblioteki Web3.

composer create-project symfony/skeleton:"7.4.*" decentralized-ticketing cd decentralized-ticketing composer require symfony/http-client symfony/messenger symfony/uid web3p/web3.php

Upewnij się, że Twój composer.json odzwierciedla stabilność:

{ "require": { "php": ">=8.3", "symfony/http-client": "7.4.*", "symfony/messenger": "7.4.*", "symfony/uid": "7.4.*", "web3p/web3.php": "^0.3.0" } }

Usługa Blockchain

Potrzebujemy solidnej usługi do komunikacji z blockchain. Utworzymy EthereumService, który opakuje wywołania JSON-RPC.

//src/Service/Web3/EthereumService.php namespace App\Service\Web3; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; use Web3\Utils; class EthereumService { private const JSON_RPC_VERSION = '2.0'; public function __construct( private HttpClientInterface $client, #[Autowire(env: 'BLOCKCHAIN_RPC_URL')] private string $rpcUrl, #[Autowire(env: 'SMART_CONTRACT_ADDRESS')] private string $contractAddress, #[Autowire(env: 'WALLET_PRIVATE_KEY')] private string $privateKey ) {} /** * Reads the owner of a specific Ticket ID (ERC-721 ownerOf). */ public function getTicketOwner(int $tokenId): ?string { // Function signature for ownerOf(uint256) is 0x6352211e // We pad the tokenId to 64 chars (32 bytes) $data = '0x6352211e' . str_pad(Utils::toHex($tokenId, true), 64, '0', STR_PAD_LEFT); $response = $this->callRpc('eth_call', [ [ 'to' => $this->contractAddress, 'data' => $data ], 'latest' ]); if (empty($response['result']) || $response['result'] === '0x') { return null; } // Decode the address (last 40 chars of the 64-char result) return '0x' . substr($response['result'], -40); } /** * Sends a raw JSON-RPC request using Symfony HttpClient. * This offers better observability than standard libraries. */ private function callRpc(string $method, array $params): array { $response = $this->client->request('POST', $this->rpcUrl, [ 'json' => [ 'jsonrpc' => self::JSON_RPC_VERSION, 'method' => $method, 'params' => $params, 'id' => random_int(1, 9999) ] ]); $data = $response->toArray(); if (isset($data['error'])) { throw new \RuntimeException('RPC Error: ' . $data['error']['message']); } return $data; } }

Uruchom lokalny test dostępu do getTicketOwner ze znanym wyemitowanym ID. Jeśli otrzymasz adres 0x, Twoje połączenie RPC działa.

Asynchroniczne mintowanie z Messenger

Transakcje blockchain są wolne (od 15 sekund do minut). Nigdy nie zmuszaj użytkownika do czekania na potwierdzenie bloku w żądaniu przeglądarki. Użyjemy Symfony Messenger do obsługi tego w tle.

Wiadomość

//src/Message/MintTicketMessage.php: namespace App\Message; use Symfony\Component\Uid\Uuid; readonly class MintTicketMessage { public function __construct( public Uuid $ticketId, public string $userWalletAddress, public string $metadataUri ) {} }

Handler

To tutaj dzieje się magia. Użyjemy helpera biblioteki web3p/web3.php do lokalnego podpisywania transakcji.

Uwaga: W środowisku o wysokim poziomie bezpieczeństwa użyłbyś usługi zarządzania kluczami (KMS) lub oddzielnej enklawy podpisywania. W tym artykule podpisujemy lokalnie.

//src/MessageHandler/MintTicketHandler.php namespace App\MessageHandler; use App\Message\MintTicketMessage; use App\Service\Web3\EthereumService; use Psr\Log\LoggerInterface; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Web3\Contract; use Web3\Providers\HttpProvider; use Web3\RequestManagers\HttpRequestManager; use Web3p\EthereumTx\Transaction; #[AsMessageHandler] class MintTicketHandler { public function __construct( private EthereumService $ethereumService, // Our custom service private LoggerInterface $logger, #[Autowire(env: 'BLOCKCHAIN_RPC_URL')] private string $rpcUrl, #[Autowire(env: 'WALLET_PRIVATE_KEY')] private string $privateKey, #[Autowire(env: 'SMART_CONTRACT_ADDRESS')] private string $contractAddress ) {} public function __invoke(MintTicketMessage $message): void { $this->logger->info("Starting mint process for Ticket {$message->ticketId}"); // 1. Prepare Transaction Data (mintTo function) // detailed implementation of raw transaction signing usually goes here. // For brevity, we simulate the logic flow: try { // Logic to get current nonce and gas price via EthereumService // $nonce = ... // $gasPrice = ... // Sign transaction offline to prevent key exposure over network // $tx = new Transaction([...]); // $signedTx = '0x' . $tx->sign($this->privateKey); // Broadcast // $txHash = $this->ethereumService->sendRawTransaction($signedTx); // In a real app, you would save $txHash to the database entity here $this->logger->info("Mint transaction broadcast successfully."); } catch (\Throwable $e) { $this->logger->error("Minting failed: " . $e->getMessage()); // Symfony Messenger will automatically retry based on config throw $e; } } }

Kontroler

Kontroler pozostaje szczupły. Akceptuje żądanie, waliduje dane wejściowe, tworzy encję biletu „Oczekujący" w bazie danych (pominięto dla zwięzłości) i wysyła wiadomość.

//src/Controller/TicketController.php: namespace App\Controller; use App\Message\MintTicketMessage; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Uid\Uuid; #[Route('/api/v1/tickets')] class TicketController extends AbstractController { #[Route('/mint', methods: ['POST'])] public function mint(Request $request, MessageBusInterface $bus): JsonResponse { $payload = $request->getPayload(); $walletAddress = $payload->get('wallet_address'); // 1. Basic Validation if (!$walletAddress || !str_starts_with($walletAddress, '0x')) { return $this->json(['error' => 'Invalid wallet address'], 400); } // 2. Generate Internal ID $ticketId = Uuid::v7(); // 3. Dispatch Message (Fire and Forget) $bus->dispatch(new MintTicketMessage( $ticketId, $walletAddress, 'https://api.myapp.com/metadata/' . $ticketId->toRfc4122() )); // 4. Respond immediately return $this->json([ 'status' => 'processing', 'ticket_id' => $ticketId->toRfc4122(), 'message' => 'Minting request queued. Check status later.' ], 202); } }

Konfiguracja i przewodnik stylu

Zgodnie ze stylem Symfony 7.4, używamy ścisłego typowania i atrybutów. Upewnij się, że Twój messenger.yaml jest skonfigurowany dla transportu asynchronicznego.

#config/packages/messenger.yaml: framework: messenger: transports: async: dsn: '%env(MESSENGER_TRANSPORT_DSN)%' retry_strategy: max_retries: 3 delay: 1000 multiplier: 2 routing: 'App\Message\MintTicketMessage': async

Weryfikacja

Aby zweryfikować, że ta implementacja działa bez wdrażania na Mainnet:

Węzeł lokalny: Uruchom lokalny blockchain za pomocą Hardhat lub Anvil (Foundry).

npx hardhat node

Środowisko: Ustaw swój .env.local, aby wskazywał na localhost.

BLOCKCHAIN_RPC_URL="http://127.0.0.1:8545" WALLET_PRIVATE_KEY="<one of the test keys provided by hardhat>" SMART_CONTRACT_ADDRESS="<deployed contract address>" MESSENGER_TRANSPORT_DSN="doctrine://default"

Konsumpcja: Uruchom workera.

php bin/console messenger:consume async -vv

Żądanie:

curl -X POST https://localhost:8000/api/v1/tickets/mint \ -H "Content-Type: application/json" \ -d '{"wallet_address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"}'

Powinieneś zobaczyć, jak worker przetwarza wiadomość i, jeśli w pełni zaimplementowałeś logikę podpisywania surowych transakcji, hash transakcji pojawi się w konsoli Hardhat.

Podsumowanie

Budowanie aplikacji Web3 w PHP wymaga zmiany sposobu myślenia. Nie budujesz tylko aplikacji CRUD; budujesz orkiestratora dla zdecentralizowanego stanu.

Używając Symfony 7.4, wykorzystaliśmy:

  • HttpClient do niezawodnej, kontrolowanej komunikacji RPC.
  • Messenger do obsługi asynchronicznej rzeczywistości blockchain.
  • Atrybuty PHP 8.3 dla czystego, czytelnego kodu.

Ta architektura skaluje się. Niezależnie od tego, czy sprzedajesz 10 biletów czy 10 000, kolejka wiadomości działa jako bufor, zapewniając, że nonce transakcji nie kolidują, a serwer nie zawiesi się.

Gotowy do skalowania infrastruktury Web3?

Integracja blockchain wymaga precyzji. Jeśli potrzebujesz pomocy w audycie interakcji z inteligentnymi kontraktami lub skalowaniu konsumentów wiadomości Symfony, skontaktujmy się.

\

Okazja rynkowa
Logo 4
Cena 4(4)
$0.02026
$0.02026$0.02026
+2.99%
USD
4 (4) Wykres Ceny na Żywo
Zastrzeżenie: Artykuły udostępnione na tej stronie pochodzą z platform publicznych i służą wyłącznie celom informacyjnym. Niekoniecznie odzwierciedlają poglądy MEXC. Wszystkie prawa pozostają przy pierwotnych autorach. Jeśli uważasz, że jakakolwiek treść narusza prawa stron trzecich, skontaktuj się z [email protected] w celu jej usunięcia. MEXC nie gwarantuje dokładności, kompletności ani aktualności treści i nie ponosi odpowiedzialności za jakiekolwiek działania podjęte na podstawie dostarczonych informacji. Treść nie stanowi porady finansowej, prawnej ani innej profesjonalnej porady, ani nie powinna być traktowana jako rekomendacja lub poparcie ze strony MEXC.