PHP isn’t obsolete—it’s a quiet workhorse of Web3 infrastructure. This guide shows how to build a production-ready Ethereum integration using Symfony 7, PHP 8, PHP isn’t obsolete—it’s a quiet workhorse of Web3 infrastructure. This guide shows how to build a production-ready Ethereum integration using Symfony 7, PHP 8,

How to Build Real-World Web3 Infrastructure Using Symfony 7.4

2025/12/16 18:52
8 min read

The narrative that “PHP is dead” has been wrong for a decade. The narrative that “PHP can’t do Web3” is just as incorrect.

While Node.js dominates the frontend dApp ecosystem, PHP and Symfony are quietly powering the heavy lifting of the decentralized web: indexing off-chain data, managing private key orchestration for enterprise wallets, and bridging the gap between Web2 business logic and Web3 protocols.

In this guide, we will build a production-ready Web3 integration using Symfony 7.4 and PHP 8.3+. We won’t use obscure, unmaintained wrappers. We will use the industry-standard libraries to read the blockchain, interact with smart contracts, and implement a Sign-In with Ethereum (SIWE) authentication system using Symfony’s security core.

The Stack & Prerequisites

We are simulating a real-world environment. We will assume you are running Symfony 7.4 (the LTS release as of late 2025).

Requirements

  • PHP 8.3 or higher (with gmp and bcmath extensions enabled).
  • Symfony 7.4 CLI.
  • Composer.
  • An Ethereum Node URL (Infura, Alchemy, or a local Hardhat/Anvil node).

Library Selection

We will use the following strictly typed, verified libraries:

  1. web3p/web3.php: The foundational library for JSON-RPC communication.
  2. kornrunner/keccak: For Keccak-256 hashing (standard in Ethereum).
  3. simplito/elliptic-php: For cryptographic signature verification (essential for SIWE).

Installation

Create your project and install dependencies. Note that we explicitly allow web3p/web3.php to interface with modern Guzzle versions if needed.

\

composer create-project symfony/website-skeleton my_web3_app cd my_web3_app # Install the Web3 standard library composer require web3p/web3.php:^0.3 # Install crypto utilities for signature verification composer require kornrunner/keccak:^1.1 simplito/elliptic-php:^1.0 # Install the Maker bundle for rapid prototyping composer require --dev symfony/maker-bundle

\

Infrastructure: The Ethereum Client Service

Directly instantiating libraries in controllers is an anti-pattern. We will wrap the Web3 connection in a robust Symfony Service using Dependency Injection.

First, configure your node URL in .env:

# .env ETHEREUM_NODE_URL="https://mainnet.infura.io/v3/YOUR_INFURA_ID"

Now, create the service. We use PHP 8.2 Readonly Classes and Constructor Promotion for clean architecture.

\

// src/Service/Web3Client.php namespace App\Service; use Web3\Web3; use Web3\Eth; use Web3\Contract; use Web3\Providers\HttpProvider; use Web3\RequestManagers\HttpRequestManager; use Symfony\Component\DependencyInjection\Attribute\Autowire; readonly class Web3Client { private Web3 $web3; public function __construct( #[Autowire(env: 'ETHEREUM_NODE_URL')] private string $nodeUrl ) { // We utilize a timeout of 10 seconds for RPC calls $provider = new HttpProvider(new HttpRequestManager($this->nodeUrl, 10)); $this->web3 = new Web3($provider); } public function getEth(): Eth { return $this->web3->eth; } public function getContract(string $abi, string $address): Contract { return new Contract($this->web3->provider, $abi); } }

Reading State: Balance Checker

Let’s verify our connection by reading the native ETH balance of an address.

Note on Asynchrony: web3p/web3.php uses callbacks by default. To make this compatible with Symfony’s synchronous request/response lifecycle, we wrap the callback in a simple latch or use the returned promise if available. For simplicity and reliability in this version, we will use a referenced variable capture method which is the standard pattern for this library in PHP 8.

\

// src/Controller/WalletController.php namespace App\Controller; use App\Service\Web3Client; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; use Web3\Utils; #[Route('/api/wallet')] class WalletController extends AbstractController { public function __construct(private Web3Client $web3Client) {} #[Route('/balance/{address}', name: 'app_wallet_balance', methods: ['GET'])] public function balance(string $address): JsonResponse { $balance = null; $error = null; // Fetch balance via JSON-RPC $this->web3Client->getEth()->getBalance($address, function ($err, $data) use (&$balance, &$error) { if ($err !== null) { $error = $err; return; } $balance = $data; }); if ($error) { return $this->json(['error' => $error->getMessage()], 500); } // Convert BigInteger to Ether string // web3p returns PHP GMP/BigInteger objects $ethBalance = Utils::fromWei($balance, 'ether'); [$whole, $decimals] = $ethBalance; return $this->json([ 'address' => $address, 'balance_wei' => (string) $balance, 'balance_eth' => $whole . '.' . $decimals, ]); } }

Start your server (symfony server:start) and visit https://localhost:8000/api/wallet/balance/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 (Vitalik’s address). You should see a JSON response with his current balance.

Smart Contract Interaction (ERC-20)

Reading ETH is easy. Reading a token balance (like USDC) requires the ABI (Application Binary Interface).

We will create a Service method to read any ERC-20 balance.

\

// src/Service/TokenService.php namespace App\Service; use Web3\Contract; use Web3\Utils; class TokenService { // Minimal ERC-20 ABI for 'balanceOf' private const ERC20_ABI = '[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"type":"function"}]'; public function __construct(private Web3Client $web3Client) {} public function getBalance(string $tokenAddress, string $walletAddress): string { $contract = $this->web3Client->getContract(self::ERC20_ABI, $tokenAddress); $resultBalance = null; // The "at" method sets the contract address for the call $contract->at($tokenAddress)->call('balanceOf', $walletAddress, function ($err, $result) use (&$resultBalance) { if ($err !== null) { throw new \RuntimeException($err->getMessage()); } // Result is an array based on outputs in ABI $resultBalance = $result['balance']; }); // Assuming 18 decimals for standard ERC-20 // In production, you should fetch the 'decimals' function from the contract first $formatted = Utils::fromWei($resultBalance, 'ether'); return $formatted[0] . '.' . $formatted[1]; } }

Security: Sign-In with Ethereum (SIWE)

This is the most critical part of Web3 UX. We do not want users to create passwords. We want them to sign a message with their wallet (Metamask, Rabby, etc.) to prove ownership.

The Logic:

  1. Frontend requests a “nonce” (a random string) from Symfony.
  2. Frontend signs a message: “I am signing into MyApp with nonce: X”.
  3. Frontend sends the addresssignature and message to Symfony.
  4. Symfony cryptographically recovers the public key from the signature.
  5. If the recovered address matches the claimed address, the user is authenticated.

The Cryptographic Verifier

We need a helper to perform ecrecover. PHP does not have this built-in easily, so we use simplito/elliptic-php and kornrunner/keccak.

\

// src/Security/Web3/SignatureVerifier.php namespace App\Security\Web3; use Elliptic\EC; use kornrunner\Keccak; class SignatureVerifier { public function verifySignature(string $message, string $signature, string $address): bool { // 1. Hash the message according to Ethereum standard (EIP-191) $prefix = sprintf("\x19Ethereum Signed Message:\n%d", strlen($message)); $hash = Keccak::hash($prefix . $message, 256); // 2. Parse Signature (Remove 0x, split into r, s, v) $signature = substr($signature, 2); $r = substr($signature, 0, 64); $s = substr($signature, 64, 64); $v = hexdec(substr($signature, 128, 2)); // Adjust v for recovery (Ethereum uses 27/28, library expects 0/1) $recId = $v - 27; if ($recId < 0 || $recId > 1) { return false; } // 3. Recover Public Key $ec = new EC('secp256k1'); try { $pubKey = $ec->recoverPubKey($hash, ['r' => $r, 's' => $s], $recId); } catch (\Exception $e) { return false; } // 4. Derive Address from Public Key // Drop first byte (04 prefix), hash the rest, take last 20 bytes $pubKeyHex = $pubKey->encode('hex'); $pubKeyHex = substr($pubKeyHex, 2); $addressHash = Keccak::hash(hex2bin($pubKeyHex), 256); $recoveredAddress = '0x' . substr($addressHash, -40); // 5. Compare (Case insensitive) return strtolower($address) === strtolower($recoveredAddress); } }

The Symfony Authenticator

Now we implement the Symfony 7 AbstractAuthenticator.

\

// src/Security/Web3Authenticator.php namespace App\Security; use App\Repository\UserRepository; use App\Security\Web3\SignatureVerifier; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; class Web3Authenticator extends AbstractAuthenticator { public function __construct( private SignatureVerifier $verifier, private UserRepository $userRepository ) {} public function supports(Request $request): ?bool { return $request->isMethod('POST') && $request->getPathInfo() === '/api/login_web3'; } public function authenticate(Request $request): Passport { $data = json_decode($request->getContent(), true); $address = $data['address'] ?? ''; $message = $data['message'] ?? ''; // Contains the nonce $signature = $data['signature'] ?? ''; if (!$address || !$message || !$signature) { throw new AuthenticationException('Missing Web3 credentials.'); } // Verify the signature matches the address if (!$this->verifier->verifySignature($message, $signature, $address)) { throw new AuthenticationException('Invalid signature.'); } // Check nonce (Optional but recommended: Verify nonce exists in session/cache) // $storedNonce = $request->getSession()->get('login_nonce'); // if (!str_contains($message, $storedNonce)) throw ... return new SelfValidatingPassport( new UserBadge($address, function ($userIdentifier) { // Find user by wallet address or create new one return $this->userRepository->findOrCreateByWallet($userIdentifier); }) ); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { return new JsonResponse(['message' => 'Welcome to Web3', 'user' => $token->getUser()->getUserIdentifier()]); } public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response { return new JsonResponse(['error' => $exception->getMessage()], 401); } }

Advanced: Indexing Events with Symfony Messenger

Web3 is often about reacting to things happening off-chain. You shouldn’t make your user wait while you query the blockchain. Instead, use a worker.

We will create a command that polls for “Transfer” events and dispatches them to the Messenger bus.

\

// src/Command/BlockchainListenerCommand.php namespace App\Command; use App\Service\Web3Client; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; #[AsCommand(name: 'app:blockchain:listen', description: 'Polls for ERC20 Transfer events')] class BlockchainListenerCommand extends Command { public function __construct(private Web3Client $web3Client) { parent::__construct(); } protected function execute(InputInterface $input, OutputInterface $output): int { $contractAddress = '0x...'; // USDC or your token $transferTopic = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'; // Keccak('Transfer(address,address,uint256)') $output->writeln("Listening for events on $contractAddress..."); // In a real app, you would store the 'last_scanned_block' in a DB $currentBlock = 'latest'; // Uses eth_getLogs $this->web3Client->getEth()->getLogs([ 'address' => $contractAddress, 'topics' => [$transferTopic], 'fromBlock' => '0x' . dechex(20000000) // Hex block number ], function ($err, $logs) use ($output) { if ($err) { $output->writeln("Error: " . $err->getMessage()); return; } foreach ($logs as $log) { // Dispatch to Symfony Messenger here $output->writeln("Transfer detected in transaction: " . $log->transactionHash); } }); return Command::SUCCESS; } }

Note: In production, you would run this command inside a supervisord loop or cron, maintaining state of the last scanned block to ensure no events are missed.

Conclusion

We have successfully bridged the gap. You now have a Symfony 7.4 application that can:

  1. Read direct blockchain state via JSON-RPC.
  2. Decode smart contract data (ERC-20).
  3. Authenticate users securely using their Ethereum wallets (SIWE) without passwords.
  4. Listen for on-chain events via CLI commands.

Web3 is not about rewriting your entire stack in Solidity or Rust. It’s about orchestration. Symfony is the perfect orchestrator — stable, secure and typed.

Ready to Tokenize Your Enterprise?

If you are looking to integrate high-value assets onto the blockchain or need a secure audit of your current Web3-PHP architecture, I can help.

Contact me to discuss your Web3 Strategy https://www.linkedin.com/in/matthew-mochalkin/

\

Market Opportunity
RealLink Logo
RealLink Price(REAL)
$0.05332
$0.05332$0.05332
+2.71%
USD
RealLink (REAL) Live Price Chart
Disclaimer: The articles reposted on this site are sourced from public platforms and are provided for informational purposes only. They do not necessarily reflect the views of MEXC. All rights remain with the original authors. If you believe any content infringes on third-party rights, please contact service@support.mexc.com for removal. MEXC makes no guarantees regarding the accuracy, completeness, or timeliness of the content and is not responsible for any actions taken based on the information provided. The content does not constitute financial, legal, or other professional advice, nor should it be considered a recommendation or endorsement by MEXC.

You May Also Like

The UA Sprinkler Fitters Local 669 JATC – Notice of Privacy Incident

The UA Sprinkler Fitters Local 669 JATC – Notice of Privacy Incident

Landover, Maryland, February 6, 2026– The UA Sprinkler Fitters Local 669 Joint Apprenticeship and Training Committee (“JATC”) is providing notice of an event that
Share
AI Journal2026/02/07 07:30
CME pushes Solana, XRP into derivatives spotlight with new options

CME pushes Solana, XRP into derivatives spotlight with new options

CME Group is launching options for Solana and XRP futures this October. The move signals a major shift, acknowledging that institutional liquidity is now firmly expanding beyond the established dominance of Bitcoin and Ether. According to a press release dated…
Share
Crypto.news2025/09/18 01:18
Ethereum koers toont zeldzaam dubbel koopsignaal en richt zich op $4.550

Ethereum koers toont zeldzaam dubbel koopsignaal en richt zich op $4.550

Connect met Like-minded Crypto Enthusiasts! Connect op Discord! Check onze Discord   Ethereum laat op de uurgrafiek twee opeenvolgende TD Sequential koopsignalen zien. Deze indicator meet uitputting in een trend en geeft vaak een signaal dat de verkoopdruk kan afnemen. Dit dubbele signaal verschijnt rond het niveau van $4.516, waar de ETH prijs kortstondig steun vindt. Dit type formatie komt zelden voor en wordt daarom extra nauwlettend gevolgd. Wat gaat de Ethereum koers hiermee doen? Ethereum koers test steun rond $4.516 De scherpe daling van de Ethereum koers vanaf de prijszone rond $4.800 bracht de ETH prijs in korte tijd naar ongeveer $4.516. Op dit niveau trad duidelijke koopactiviteit op, waardoor de neerwaartse beweging tijdelijk werd gestopt. Het dubbele signaal dat door de TD Sequential indicator is gegenereerd, viel precies samen met dit prijspunt. De TD Sequential is opgebouwd uit negen candles die een trend meetellen. Wanneer de negende candle verschijnt, kan dit duiden op een trendomslag. In dit geval verschenen zelfs twee signalen kort na elkaar, wat aangeeft dat de verkoopdruk mogelijk uitgeput is. Het feit dat dit gebeurde in een zone waar ETH kopers actief bleven, maakt het patroon extra opvallend. TD Sequential just flashed two buy signals for Ethereum $ETH! pic.twitter.com/JPO8EhiEPi — Ali (@ali_charts) September 16, 2025 Welke crypto nu kopen?Lees onze uitgebreide gids en leer welke crypto nu kopen verstandig kan zijn! Welke crypto nu kopen? Fed-voorzitter Jerome Powell heeft aangekondigd dat de rentes binnenkort zomaar eens omlaag zouden kunnen gaan, en tegelijkertijd blijft BlackRock volop crypto kopen, en dus lijkt de markt klaar om te gaan stijgen. Eén vraag komt telkens terug: welke crypto moet je nu kopen? In dit artikel bespreken we de munten die… Continue reading Ethereum koers toont zeldzaam dubbel koopsignaal en richt zich op $4.550 document.addEventListener('DOMContentLoaded', function() { var screenWidth = window.innerWidth; var excerpts = document.querySelectorAll('.lees-ook-description'); excerpts.forEach(function(description) { var excerpt = description.getAttribute('data-description'); var wordLimit = screenWidth wordLimit) { var trimmedDescription = excerpt.split(' ').slice(0, wordLimit).join(' ') + '...'; description.textContent = trimmedDescription; } }); }); Technische indicatoren schetsen herstelkans voor ETH Naast de dubbele koopsignalen verstrekken ook andere indicatoren belangrijke aanwijzingen. Tijdens de daling van de ETH koers waren grote rode candles zichtbaar, maar na de test van $4.516 stabiliseerde de Ethereum koers. Dit wijst op een mogelijke verschuiving in het evenwicht tussen de bears en bulls. Als deze opwaartse beweging doorzet, liggen de eerste weerstanden rond $4.550. Daarboven wacht een sterkere zone rond $4.650. Deze niveaus zijn in eerdere Ethereum sessies al meerdere keren getest. Een doorbraak zou ruimte openen richting de all-time high van ETH rond $4.953. Wanneer de prijs toch opnieuw onder $4.516 zakt, liggen er zones rond $4.500 en $4.450 waar grotere kooporders worden verwacht. Deze niveaus kunnen als een vangnet fungeren, mocht de druk opnieuw toenemen. Marktdynamiek bevestigt technische indicatoren De huidige situatie volgt op een bredere correctie in de cryptomarkt. Verschillende vooraanstaande crypto tokens zagen scherpe koersdalingen, waarna traders op zoek gingen naar signalen voor een mogelijke ommekeer. Dat juist Ethereum nu een dubbel TD Sequential signaal toont, versterkt de interesse in dit scenario. Fundamenteel blijft Ethereum sterk. Het aantal ETH tokens dat via staking is vastgezet, blijft groeien. Dat verkleint de vrije circulatie en vermindert verkoopdruk. Tegelijk blijft het netwerk intensief gebruikt voor DeFi, NFT’s en stablecoins. Deze activiteiten zorgen voor een stabiele vraag naar ETH, ook wanneer de prijs tijdelijk onder druk staat. Fundamentele drijfveren achter de Ethereum koers De Ethereum koers wordt echter niet alleen bepaald door candles en patronen, maar ook door bredere factoren. Een stijgend percentage van de totale ETH supply staat vast in staking contracten. Hierdoor neemt de liquiditeit op exchanges af. Dit kan prijsschommelingen versterken wanneer er plotseling meer koopdruk ontstaat. Daarnaast is Ethereum nog steeds het grootste smart contract platform. Nieuwe standaarden zoals ERC-8004 en ontwikkelingen rond layer-2 oplossingen houden de activiteit hoog. Deze technologische vooruitgang kan de waardepropositie ondersteunen en zo indirect bijdragen aan een ETH prijsherstel. Het belang van de korte termijn dynamiek De komende handelsdagen zullen duidelijk maken of de bulls genoeg kracht hebben om door de weerstandszone rond $4.550 te breken. Voor de bears ligt de focus juist op het verdedigen van de prijsregio rond $4.516. De whales, die met grote handelsorders opereren, kunnen hierin een beslissende rol spelen. Het dubbele TD Sequential signaal blijft hoe dan ook een zeldzame gebeurtenis. Voor cryptoanalisten vormt het een objectief aanknopingspunt om de kracht van de huidige Ethereum trend te toetsen. Vooruitblik op de ETH koers Ethereum liet twee opeenvolgende TD Sequential signalen zien op de uurgrafiek, iets wat zelden voorkomt. Deze formatie viel samen met steun rond $4.516, waar de bulls actief werden. Als de Ethereum koers boven dit niveau blijft, kan er ruimte ontstaan richting $4.550 en mogelijk $4.650. Zakt de prijs toch opnieuw onder $4.516, dan komen $4.500 en $4.450 in beeld als nieuwe steunzones. De combinatie van zeldzame indicatoren en een sterke fundamentele basis maakt Ethereum interessant voor zowel technische als fundamentele analyses. Of de bulls het momentum echt kunnen overnemen, zal blijken zodra de Ethereum koers de eerstvolgende weerstanden opnieuw test. Koop je crypto via Best Wallet Best wallet is een topklasse crypto wallet waarmee je anoniem crypto kan kopen. Met meer dan 60 chains gesupport kan je al je main crypto coins aanschaffen via Best Wallet. Best wallet - betrouwbare en anonieme wallet Best wallet - betrouwbare en anonieme wallet Meer dan 60 chains beschikbaar voor alle crypto Vroege toegang tot nieuwe projecten Hoge staking belongingen Lage transactiekosten Best wallet review Koop nu via Best Wallet Let op: cryptocurrency is een zeer volatiele en ongereguleerde investering. Doe je eigen onderzoek. Het bericht Ethereum koers toont zeldzaam dubbel koopsignaal en richt zich op $4.550 is geschreven door Dirk van Haaster en verscheen als eerst op Bitcoinmagazine.nl.
Share
Coinstats2025/09/17 23:31