Web3 और पारंपरिक वेब फ्रेमवर्क का प्रतिच्छेदन वह जगह है जहाँ वास्तविक दुनिया की उपयोगिता शुरू होती है। जबकि हाइप साइकल आते-जाते रहते हैं, स्वामित्व सत्यापित करने के लिए नॉन-फंजिबल टोकन (NFTs) की उपयोगिता — विशेष रूप से इवेंट टिकटिंग में — एक ठोस उपयोग केस बनी हुई है।
इस लेख में, हम Symfony 7.4 और PHP 8.3 का उपयोग करके एक विकेंद्रीकृत इवेंट टिकटिंग सिस्टम की रीढ़ बनाएंगे। हम बुनियादी ट्यूटोरियल से आगे बढ़ेंगे और एक प्रोडक्शन-ग्रेड आर्किटेक्चर लागू करेंगे जो Symfony Messenger घटक का उपयोग करके ब्लॉकचेन लेनदेन की असिंक्रोनस प्रकृति को संभालता है।
एक "सीनियर" दृष्टिकोण यह स्वीकार करता है कि PHP, Node.js की तरह लंबे समय तक चलने वाली प्रक्रिया नहीं है। इसलिए, हम कंट्रोलर के भीतर रियल-टाइम में ब्लॉकचेन इवेंट को नहीं सुनते हैं। इसके बजाय, हम एक हाइब्रिड दृष्टिकोण का उपयोग करते हैं:
कई PHP Web3 लाइब्रेरी परित्यक्त या खराब टाइप की गई हैं। जबकि web3p/web3.php सबसे प्रसिद्ध है, रखरखाव अंतराल के कारण इस पर सख्ती से निर्भर रहना जोखिम भरा हो सकता है।
इस गाइड के लिए, हम ABI एन्कोडिंग के लिए web3p/web3.php (संस्करण ^0.3) का उपयोग करेंगे लेकिन वास्तविक JSON-RPC ट्रांसपोर्ट के लिए Symfony के नेटिव HttpClient का लाभ उठाएंगे। यह हमें टाइमआउट, रिट्राई और लॉगिंग पर पूर्ण नियंत्रण देता है — प्रोडक्शन ऐप्स के लिए महत्वपूर्ण।
सबसे पहले, आइए डिपेंडेंसी इंस्टॉल करें। हमें Symfony रनटाइम, HTTP क्लाइंट और 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
सुनिश्चित करें कि आपका composer.json स्थिरता को दर्शाता है:
{ "require": { "php": ">=8.3", "symfony/http-client": "7.4.*", "symfony/messenger": "7.4.*", "symfony/uid": "7.4.*", "web3p/web3.php": "^0.3.0" } }
हमें ब्लॉकचेन से बात करने के लिए एक मजबूत सेवा की आवश्यकता है। हम एक EthereumService बनाएंगे जो 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; } }
ज्ञात मिंटेड ID के साथ getTicketOwner एक्सेस करते हुए एक स्थानीय परीक्षण चलाएं। यदि आपको 0x पता मिलता है, तो आपका RPC कनेक्शन काम कर रहा है।
ब्लॉकचेन लेनदेन धीमे होते हैं (15s से मिनट तक)। कभी भी किसी उपयोगकर्ता को ब्राउज़र अनुरोध में ब्लॉक पुष्टि के लिए प्रतीक्षा न कराएं। हम इसे बैकग्राउंड में संभालने के लिए Symfony Messenger का उपयोग करेंगे।
//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 ) {} }
यहीं पर जादू होता है। हम स्थानीय रूप से लेनदेन पर हस्ताक्षर करने के लिए web3p/web3.php लाइब्रेरी हेल्पर का उपयोग करेंगे।
नोट: उच्च-सुरक्षा वातावरण में, आप एक Key Management Service (KMS) या एक अलग साइनिंग एन्क्लेव का उपयोग करेंगे। इस लेख के लिए, हम स्थानीय रूप से हस्ताक्षर करते हैं।
//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; } } }
कंट्रोलर पतला रहता है। यह अनुरोध को स्वीकार करता है, इनपुट को मान्य करता है, आपके डेटाबेस में एक "पेंडिंग" टिकट एंटिटी बनाता है (संक्षिप्तता के लिए छोड़ा गया) और संदेश को डिस्पैच करता है।
//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); } }
Symfony 7.4 स्टाइल का पालन करते हुए, हम स्ट्रिक्ट टाइपिंग और एट्रिब्यूट का उपयोग करते हैं। सुनिश्चित करें कि आपका messenger.yaml async ट्रांसपोर्ट के लिए कॉन्फ़िगर किया गया है।
#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
Mainnet पर तैनात किए बिना इस कार्यान्वयन को सत्यापित करने के लिए:
लोकल नोड: Hardhat या Anvil (Foundry) का उपयोग करके एक स्थानीय ब्लॉकचेन चलाएं।
npx hardhat node
एनवायरनमेंट: अपने .env.local को 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"
कंज़्यूम: वर्कर शुरू करें।
php bin/console messenger:consume async -vv
अनुरोध:
curl -X POST https://localhost:8000/api/v1/tickets/mint \ -H "Content-Type: application/json" \ -d '{"wallet_address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"}'
आपको वर्कर को संदेश को प्रोसेस करते हुए देखना चाहिए और, यदि आपने कच्चे लेनदेन हस्ताक्षर तर्क को पूरी तरह से लागू किया है, तो आपके Hardhat कंसोल में एक लेनदेन हैश दिखाई देगा।
PHP में Web3 एप्लिकेशन बनाने के लिए मानसिकता में बदलाव की आवश्यकता होती है। आप केवल एक CRUD ऐप नहीं बना रहे हैं; आप विकेंद्रीकृत स्थिति के लिए एक ऑर्केस्ट्रेटर बना रहे हैं।
Symfony 7.4 का उपयोग करके, हमने लाभ उठाया:
यह आर्किटेक्चर स्केल करता है। चाहे आप 10 टिकट बेच रहे हों या 10,000, संदेश कतार एक बफर के रूप में कार्य करती है, यह सुनिश्चित करती है कि आपके लेनदेन nonces टकराएं नहीं और आपका सर्वर हैंग न हो।
ब्लॉकचेन को एकीकृत करने के लिए सटीकता की आवश्यकता होती है। यदि आपको अपने स्मार्ट कॉन्ट्रैक्ट इंटरैक्शन का ऑडिट करने या अपने Symfony मैसेज कंज़्यूमर को स्केल करने में सहायता की आवश्यकता है, तो चलिए संपर्क में रहते हैं।
\


