Ce guide montre comment construire un backend évolutif de billetterie d'événements basé sur les NFT en PHP en utilisant Symfony Messenger pour gérer la latence de la blockchain de manière sûre et fiable.Ce guide montre comment construire un backend évolutif de billetterie d'événements basé sur les NFT en PHP en utilisant Symfony Messenger pour gérer la latence de la blockchain de manière sûre et fiable.

Construire un Système de Billetterie d'Événements Décentralisé Web3 avec Symfony 7.4

L'intersection du Web3 et des frameworks web traditionnels est là où commence l'utilité dans le monde réel. Alors que les cycles de battage médiatique vont et viennent, l'utilité des Non-Fungible Token (NFT) / jeton non fongible pour vérifier la propriété — spécifiquement dans la billetterie d'événements — reste un cas d'usage solide.

Dans cet article, nous allons construire l'épine dorsale d'un Système de billetterie d'événements décentralisé en utilisant Symfony 7.4 et PHP 8.3. Nous irons au-delà des tutoriels de base et implémenterons une architecture de qualité production qui gère la nature asynchrone des transactions blockchain en utilisant le composant Symfony Messenger.

L'Architecture

Une approche "Senior" reconnaît que PHP n'est pas un processus de longue durée comme Node.js. Par conséquent, nous n'écoutons pas les événements blockchain en temps réel dans un contrôleur. Au lieu de cela, nous utilisons une approche hybride :

  1. Interaction directe (Écriture) : Nous utilisons Symfony Messenger pour décharger les transactions "Minting" vers un worker, évitant les timeouts HTTP.
  2. Polling RPC (Lecture) : Nous utilisons des commandes planifiées pour vérifier le statut on-chain.
  3. Smart Contract (Contrat Intelligent) : Nous supposons un contrat ERC-721 standard déployé sur une chaîne compatible EVM (Ethereum, Polygon, Base).

Prérequis & Stack

  • PHP : 8.3+
  • Symfony : 7.4 (LTS)
  • Nœud Blockchain : Infura, Alchemy, ou un nœud Hardhat local.

De nombreuses bibliothèques PHP Web3 sont abandonnées ou mal typées. Bien que web3p/web3.php soit la plus célèbre, s'appuyer strictement sur elle peut être risqué en raison des lacunes de maintenance.

Pour ce guide, nous utiliserons web3p/web3.php (version ^0.3) pour l'encodage ABI mais exploiterons le HttpClient natif de Symfony pour le transport JSON-RPC réel. Cela nous donne un contrôle total sur les timeouts, les nouvelles tentatives et la journalisation — critique pour les applications de production.

Configuration du projet

D'abord, installons les dépendances. Nous avons besoin du runtime Symfony, du client HTTP et de la bibliothèque 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

Assurez-vous que votre composer.json reflète la stabilité :

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

Le Service Blockchain

Nous avons besoin d'un service robuste pour communiquer avec la blockchain. Nous allons créer un EthereumService qui encapsule les appels 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; } }

Exécutez un test local en accédant à getTicketOwner avec un ID minté connu. Si vous obtenez une adresse 0x, votre connexion RPC fonctionne.

Minting asynchrone avec Messenger

Les transactions blockchain sont lentes (15 s à plusieurs minutes). Ne faites jamais attendre un utilisateur pour une confirmation de bloc dans une requête de navigateur. Nous utiliserons Symfony Messenger pour gérer cela en arrière-plan.

Le Message

//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 ) {} }

Le Handler

C'est ici que la magie opère. Nous utiliserons l'assistant de bibliothèque web3p/web3.php pour signer une transaction localement.

Note : Dans un environnement de haute sécurité, vous utiliseriez un Service de gestion de clés (KMS) ou une enclave de signature séparée. Pour cet article, nous signons localement.

//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; } } }

Le Contrôleur

Le contrôleur reste mince. Il accepte la requête, valide l'entrée, crée une entité de billet "En attente" dans votre base de données (omise par souci de brièveté) et distribue le message.

//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); } }

Configuration & Guide de style

En suivant le style Symfony 7.4, nous utilisons le typage strict et les attributs. Assurez-vous que votre messenger.yaml est configuré pour le transport asynchrone.

#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

Vérificaiton

Pour vérifier que cette implémentation fonctionne sans déployer sur le Mainnet :

Nœud local : Exécutez une blockchain locale en utilisant Hardhat ou Anvil (Foundry).

npx hardhat node

Environnement : Configurez votre .env.local pour pointer vers 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"

Consommer : Démarrez le worker.

php bin/console messenger:consume async -vv

Requête :

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

Vous devriez voir le worker traiter le message et, si vous avez entièrement implémenté la logique de signature de transaction brute, un hash de transaction apparaître dans votre console Hardhat.

Conclusion

Construire des applications Web3 en PHP nécessite un changement de mentalité. Vous ne construisez pas simplement une application CRUD ; vous construisez un orchestrateur pour un état décentralisé.

En utilisant Symfony 7.4, nous avons exploité :

  • HttpClient pour une communication RPC fiable et contrôlable.
  • Messenger pour gérer la réalité asynchrone des blockchains.
  • Attributs PHP 8.3 pour un code propre et lisible.

Cette architecture évolue. Que vous vendiez 10 billets ou 10 000, la file d'attente de messages agit comme un tampon, garantissant que vos nonces de transaction n'entrent pas en collision et que votre serveur ne se bloque pas.

Prêt à faire évoluer votre infrastructure Web3 ?

L'intégration de la blockchain nécessite de la précision. Si vous avez besoin d'aide pour auditer vos interactions de smart contract ou pour faire évoluer vos consommateurs de messages Symfony, restons en contact.

\

Opportunité de marché
Logo de 4
Cours 4(4)
$0.02031
$0.02031$0.02031
+3.25%
USD
Graphique du prix de 4 (4) en temps réel
Clause de non-responsabilité : les articles republiés sur ce site proviennent de plateformes publiques et sont fournis à titre informatif uniquement. Ils ne reflètent pas nécessairement les opinions de MEXC. Tous les droits restent la propriété des auteurs d'origine. Si vous estimez qu'un contenu porte atteinte aux droits d'un tiers, veuillez contacter service@support.mexc.com pour demander sa suppression. MEXC ne garantit ni l'exactitude, ni l'exhaustivité, ni l'actualité des contenus, et décline toute responsabilité quant aux actions entreprises sur la base des informations fournies. Ces contenus ne constituent pas des conseils financiers, juridiques ou professionnels, et ne doivent pas être interprétés comme une recommandation ou une approbation de la part de MEXC.