What if PHP developers didn’t have to leave their ecosystem to build in Web3?
I have been writing PHP for years. Laravel, Filament, APIs, admin systems, etc. I have written applications in JavaScript and some in .NET too. But PHP has been my home. But like a lot of PHP developers, I noticed that whenever blockchain came up in a conversation, it felt like a conversation happening in another room. The Web3 space has its own languages, its own tooling, its own ecosystem. Solidity for Ethereum. Rust for Solana. Move for Aptos. If you wanted to build smart contracts, the unspoken assumption was: learn a new language first (most likely Rust or Solidity 😂). But I kept wondering… does it have to be that way?
The Web3 PHP Packages Already Exist
Before I go further, I want to be clear: PHP is not absent from Web3. There are already some excellent packages that let you interact with blockchains from PHP:
web3p/web3.php : connect to Ethereum nodes, sign transactions, call contracts
php-solana-sdk : interact with the Solana blockchain
kornrunner/keccak : Keccak-256 hashing in pure PHP
These packages are great. Using them, a PHP developer can read from a smart contract, send transactions, listen to events, and build blockchain-powered applications entirely in PHP. In fact, I tried building a web3 giveaway app at some point to test how I can implement transactions with Solana, Ethereum, and Bitcoin (Solana was successful though 😂).
But there is a ceiling. You can talk to smart contracts. You cannot write them in PHP.
There is a big barrier…
Virtual Machine Compatibility: Popular blockchains like Ethereum run on the EVM, which only executes bytecode compiled from languages like Solidity or Vyper. PHP is a server-side scripting language meant to be interpreted by the PHP engine (Zend VM), not a blockchain VM.
Lack of Determinism: Smart contracts must be deterministic, meaning they must produce the exact same result on every computer in the network. PHP has many features (like random number generation and time-based functions) that could cause different nodes to reach different results, breaking blockchain consensus. You can have some more information written by Ramchandhar Rapolu here.
The moment you need to deploy a new contract, you leave the PHP world and open a .sol file. You need to learn Solidity. You need to understand the EVM type system. You need to configure Hardhat or Foundry.
This gap is what I am trying to close (or shorten, at least)
The Inspiration: NativePHP!!
If you are in the PHP community, you have probably heard of NativePHP.
The idea behind it is elegant: you already know PHP and Laravel. What if you could use those exact skills to build native mobile and desktop applications without learning Swift, Kotlin, or React Native?
NativePHP does not run PHP inside your iPhone. It uses PHP as a syntax and logic layer, compiling or bridging that code into something the native runtime understands. That mental model stuck with me. And one day it just clicked…
“What if we did the same thing for smart contracts?”
PHP does not need to run on the Ethereum virtual machine. It just needs to be the layer where you write the contract. We parse the PHP, understand its intent, and output valid Solidity.
That is the idea behind PHP-Solidity.
What PHP-Solidity Is (and Is Not)
Let me be direct about what this project does, because the name might suggest something it is not 😂.
PHP-Solidity is a transpiler. You write a PHP class using special EVM types and PHP 8 attributes. The transpiler reads that file, parses it into an abstract syntax tree, and outputs a .sol file with valid Solidity source code, which you then compile and deploy with standard tooling like Hardhat or Foundry.
PHP never runs on the blockchain. The EVM never executes PHP. What PHP-Solidity gives you is the ability to express a smart contract using syntax you already know, in a language your IDE already understands.
Here is what it looks like in practice. This is a simple ERC-20 token written entirely in PHP:
<?php
use PhpSolidity\Attributes\Contract;
use PhpSolidity\Attributes\SolidityEvent;
use PhpSolidity\Attributes\Storage;
use PhpSolidity\Attributes\External;
use PhpSolidity\Attributes\View;
use PhpSolidity\Attributes\Constructor;
use PhpSolidity\Attributes\Modifier;
use PhpSolidity\Attributes\Guarded;
#[Contract(license: 'MIT', version: '^0.8.20')]
#[SolidityEvent('Transfer', ['address indexed from', 'address indexed to', 'uint256 value'])]
class MyToken
{
#[Storage(public: true)]
private uint256 $totalSupply;
#[Storage]
private mapping $balances;
#[Storage]
private address $owner;
#[Constructor]
public function __construct(uint256 $initialSupply)
{
$this->owner = msg::sender();
$this->totalSupply = $initialSupply;
$this->balances[msg::sender()] = $initialSupply;
Event::emit('Transfer', address::zero(), msg::sender(), $initialSupply);
}
#[Modifier]
private function onlyOwner(): void
{
EVM::require(msg::sender() == $this->owner, "Not the owner");
}
#[External, View]
public function balanceOf(address $account): uint256
{
return $this->balances[$account];
}
#[External]
public function transfer(address $to, uint256 $amount): bool
{
EVM::require($this->balances[msg::sender()] >= $amount, "Insufficient balance");
$this->balances[msg::sender()] -= $amount;
$this->balances[$to] += $amount;
Event::emit('Transfer', msg::sender(), $to, $amount);
return true;
}
#[External, Guarded('onlyOwner')]
public function mint(address $to, uint256 $amount): void
{
$this->totalSupply += $amount;
$this->balances[$to] += $amount;
Event::emit('Transfer', address::zero(), $to, $amount);
}
}
You run one command:
php bin/phpsolidity compile contracts/MyToken.php
And you get this valid, deployable, (beautiful😏) Solidity:
How It Works Under the Hood
The pipeline has four stages:
1. Validation
Before any parsing happens, the source file is scanned for things that make no sense on a blockchain: float literals (the EVM has no floating point), rand() (unsafe on-chain. Miners can influence block randomness), file_get_contents(), superglobals, and so on. If you write something invalid, you get a clear error immediately:
2. Parsing
The PHP source is parsed using nikic/php-parser, one of the most mature PHP AST libraries available. This gives us a complete abstract syntax tree of the PHP class. We walk to that tree looking for a class decorated with #[Contract], then extract everything from it: state variables (from PHP 8 attributes on properties), functions (from methods and their attributes), events (from class-level attributes), and modifiers.
The function bodies are translated line by line
$this->balances[msg::sender()] becomes balances[msg.sender], EVM::require(…) becomes require(…). Event::emit(‘Transfer’, …) becomes emit Transfer(…).
3. AST to ContractNode
The PHP AST is converted into our own internal representation, a ContractNode containing FunctionNode, StateVariableNode, EventNode, and ModifierNode objects. This intermediate layer keeps the parser and emitter completely decoupled. In the future, this same AST could target Vyper or Yul instead of Solidity.
4. Emitting Solidity
The SolidityEmitter walks the ContractNode and builds the Solidity source string—license header, pragma, state variables, events, modifiers, constructor, and functions. The output is formatted, readable Solidity that you would not be embarrassed to share.
The EVM Type System in PHP
One of the trickiest parts of this project is the type system. Solidity has types like uint256, address, bytes32, and mapping that simply do not exist in PHP.
The solution is a set of PHP stub classes in stubs/evm.php. These classes: uint256, address, mapping, msg, and block… exist purely for the IDE and the transpiler. They give your editor autocompletion and type checking, and they give the transpiler something to map.
// In your contract file — valid PHP that your IDE understands
private uint256 $totalSupply;
$this->balances[msg::sender()] = $initialSupply;
// What the transpiler emits
uint256 private totalSupply;
balances[msg.sender] = initialSupply;
There is one notable constraint: PHP treats require as a language construct, not a function. You cannot shadow it. So instead of require(condition, “message”), you write EVM::require(condition, “message”).which the transpiler converts to Solidity’s require().
The Limits
Am I happy it works on my machine? Yes. 😂🔥
Are there real limitations? Also yesss
PHP-Solidity is a v0.1 project. It works, but it is not magic. Here are the real limitations I think you should know:
No floating point. The EVM has no float type. All arithmetic must be integer-based, scaled by a factor (usually 1e18 for ETH values). This is a Solidity limitation, not ours though (but you should know😉).
Supported PHP subset only. You cannot use closures, dynamic arrays, reflection, or most of PHP’s standard library inside a contract. The transpiler enforces this with clear errors.
Body translation is pattern-based. The function body translator works by recognising common patterns ($this->, msg::sender(), etc.) and replacing them. Complex PHP expressions that have no Solidity equivalent will not transpile correctly.
No struct support yet. Solidity structs, which map to complex value types are not yet supported. This is on the roadmap.
No interface or abstract contract support yet. I hope to add this in future versions or leave it open for others who may want to contribute in this way too.
You still need Solidity tooling to deploy. PHP-Solidity produces .sol files. You still use Hardhat, Foundry, or Remix to compile and deploy them. PHP-Solidity does not replace that step; it just means you write in PHP first.
The Potential
Despite those limits, the potential here is real.
You have heard this many times but here I go again 😂… “PHP is one of the most widely used languages in the world." Laravel alone powers hundreds of thousands of applications. I personally think that the Web3 ecosystem desperately needs more developers. And right now, the barrier to entry is high because developers have to learn an entirely new language and toolchain before they can contribute anything.
PHP-Solidity lowers that barrier. A Laravel developer can write their first smart contract on day one, using concepts they already understand: classes, typed properties, methods, and attributes. They can contribute to DeFi projects, NFT platforms, and DAO tooling without spending months learning Solidity from scratch.
And the design is extensible. The internal AST is language-agnostic. A Vyper emitter, a Yul emitter, or even a Cairo emitter (for StarkNet) could be built on top of the same parser. The PHP syntax layer stays the same; only the output target changes.
It Is Open Source (Come Build With Me)🎉
PHP-Solidity is published on Packagist and completely open source:
composer require danielmabadeje/php-solidity
The GitHub repository is at https://github.com/DanielMabadeje/php-solidity
There is meaningful work to do at every level:
Beginners: add new EVM types (bytes16, bytes4), improve error messages, and write example contracts.
Intermediate: improve the body translator to handle more PHP expression patterns and add struct support.
Advanced: build the interface/abstract contract system, explore a Vyper emitter, write a formal test suite.
The CONTRIBUTING.md in the repo walks through exactly how the transpiler works, how to add a new type, how to add a validator rule, and the code standards we follow.
We have watched NativePHP prove that PHP developers do not have to abandon their ecosystem to work on mobile applications. The same principle applies here.
You do not have to learn Solidity to contribute to Web3. You do not have to leave Laravel to build DeFi tooling. PHP-Solidity is an early, honest attempt to build that bridge.
It is not perfect. It is not complete. But it works (on my laptop and hopefully in yours 😁) and every open source project starts somewhere. If this resonates with you, install the package, write a contract, break something, and open a PR. That is how this gets better.
You can connect with me on twitter : https://twitter.com/mabadejedanphp