DEV Community

Cover image for I built a lightweight AJAX-first PHP framework for shared hosting
Vercy Dev
Vercy Dev

Posted on

I built a lightweight AJAX-first PHP framework for shared hosting

If you've ever tried to use Laravel or Symfony on a shared hosting
account, you know the pain. No terminal access, no Composer on the
server, no Docker — just FTP and cPanel.

That's exactly why I built Zieex.

What is Zieex?

Zieex is a lightweight PHP 8.2+ framework with zero runtime
dependencies. You can literally FTP the files into your public_html
folder and it works.

No terminal. No SSH. No Docker. Just PHP.

Key Features

  • AJAX-first by default — links, forms and buttons never reload the page unless you want them to
  • Zero runtime dependencies — no third-party packages required
  • Web-based installer — like WordPress, runs in the browser
  • Blade-like templating — familiar .ze.php syntax
  • Built-in Auth — session-based with BCRYPT hashing
  • JWT support — for REST APIs and mobile apps
  • CSRF protection — automatic on all POST requests
  • Rate limiting — per route, out of the box
  • Raw SMTP socket — sends emails without any library
  • MVC + REST API — separate web and API route files
  • Query Builder — clean database API without an ORM
  • PHP 8.2+ only — modern PHP, no legacy baggage

Install It

composer create-project vercy/zieex my-app
Enter fullscreen mode Exit fullscreen mode

Then open your browser — the installer runs automatically.

How It Works

Routes

// routes/web/index.php
Router::get('/', [HomeController::class, 'index']);

Router::post('/login', [AuthController::class, 'login'])
    ->rateLimit(5, 60);

Router::group(['prefix' => '/admin', 'middleware' => ['auth', 'role:admin']], function () {
    Router::get('/dashboard', [AdminController::class, 'index']);
});

// Registers GET, POST, PUT, DELETE automatically
Router::resource('/api/posts', PostController::class);
Enter fullscreen mode Exit fullscreen mode

Controllers

class PostController extends Controller
{
    public function store(Request $request): Response
    {
        $data = $this->validate($request, [
            'title'   => 'required|max:255',
            'content' => 'required',
        ]);

        $post = Post::create($data);

        return $this->success($post, 'Post created.', 201);
    }
}
Enter fullscreen mode Exit fullscreen mode

Templates (.ze.php)

@extends('layouts.app')

@section('content')
  <h1>{{ $title }}</h1>

  @foreach($posts as $post)
    <p>{{ $post['title'] }}</p>
  @endforeach

  @auth
    <a href="/dashboard">Dashboard</a>
  @endauth
@endsection
Enter fullscreen mode Exit fullscreen mode

Auth

Auth::attempt($email, $password);
Auth::check();
Auth::user();
Auth::hash($password);
Auth::verify($password, $hash);
Enter fullscreen mode Exit fullscreen mode

Query Builder

DB::table('posts')
    ->where('status', 'published')
    ->orderBy('created_at', 'DESC')
    ->paginate(15, $page);

DB::transaction(function () {
    // multiple queries here
});
Enter fullscreen mode Exit fullscreen mode

Mail — multiple drivers

// Use Resend for auth emails
Mail::driver('resend')
    ->to($user['email'])
    ->subject('Verify your email')
    ->view('emails.verify', ['code' => $code])
    ->send();

// Use SMTP for bulk emails
Mail::driver('smtp')
    ->to($email)
    ->subject('Newsletter')
    ->html($content)
    ->send();
Enter fullscreen mode Exit fullscreen mode

Logging

Log::info('User logged in', ['user_id' => $id]);
Log::error('Payment failed', ['order' => $orderId]);
Log::warning('Rate limit approaching', ['ip' => $ip]);
Enter fullscreen mode Exit fullscreen mode

The AJAX Layer — interaction.js

This is one of my favourite parts of Zieex.

Every link, form and button is AJAX-enabled by default. No page
reloads. No extra code. It just works.

<!-- Normal link — no reload -->
<a href="/dashboard">Dashboard</a>

<!-- Normal form — submitted via AJAX -->
<form method="POST" action="/posts">
  <input name="title">
  <button type="submit">Save</button>
</form>

<!-- Opt out if you need a hard reload -->
<a href="/download" data-no-ajax>Download</a>
Enter fullscreen mode Exit fullscreen mode

It also handles JSON responses, flash messages, form errors and
browser back/forward navigation automatically.

There's also a LiveComponent system for reactive UI without
any JavaScript framework:

LiveComponent.register('counter', {
  data: () => ({ count: 0 }),
  methods: {
    increment() { this.count++; }
  },
  render() {
    return `
      <p>${this.count}</p>
      <button data-on="click:increment">+</button>
    `;
  }
});
Enter fullscreen mode Exit fullscreen mode
<div data-live="counter"></div>
Enter fullscreen mode Exit fullscreen mode

Shared Hosting Deployment

  1. Run composer create-project vercy/zieex my-app locally
  2. FTP all files to your public_html
  3. Visit your domain
  4. Fill in the installer form
  5. Done

No terminal access needed on the server at all.

Why Not Just Use Laravel?

Laravel is amazing. But it is also:

  • 100MB+ with dependencies
  • Requires terminal access to install
  • Requires terminal access to deploy
  • Overkill for small to medium projects

Zieex is under 3MB, has zero runtime dependencies, and installs
through a browser form just like WordPress.

Links


I would love to hear your feedback! What features would you like
to see next? Drop a comment below.

Top comments (1)

Collapse
 
jaholland profile image
Jonathan A Holland

Whilst I haven't tried it, I'm impressed by how complete and well structured the code looks. This is a very good achievement. Well done!