Compare

Geblang vs PHP

Keep PHP's class-based, web-first productivity and gain generics that exist at run time, request bodies typed by your own classes, one long-running server that handles requests concurrently, built-in HTTPS, and a single executable to deploy.

Geblang and PHP at a glance

PHPGeblang
TypesParameter, return, and property types are checked at run time (scalars are coerced unless strict_types is on)Types are checked before the program runs and enforced at run time, with no scalar coercion
GenericsOnly in docblocks, for static analysers such as PHPStan or PsalmGeneric classes and functions whose type arguments are enforced at run time
Execution modelTypically a fresh process state per request under PHP-FPMOne long-running process serving requests concurrently on goroutines
ConcurrencyOne request per worker; Fibers (PHP 8.1) and extensions such as Swoole add asyncasync, await, channels, and select built into the language
DeploymentPHP runtime, extensions, PHP-FPM, and a web server such as nginxOne executable that includes the HTTPS server, with automatic certificates available
FrameworkLaravel, Symfony, and the wider Packagist ecosystemGebweb: controllers, DI, typed binding, validation, views, auth, jobs, and OpenAPI

Generics that exist at run time

PHP has no generics in the language. A @template docblock lets a static analyser track the element type, but nothing stops the wrong value at run time.

PHP

/** @template T */
final class Stack {
    /** @var list<T> */
    private array $items = [];

    /** @param T $item */
    public function push(mixed $item): void {
        $this->items[] = $item;
    }
}

$ids = new Stack(); // "Stack<int>" lives only in the docblocks
$ids->push("8");    // accepted at run time

Geblang

import io;

class Stack<T> {
    list<T> items = [];

    func push(T item): void {
        this.items.push(item);
    }
}

let ids = Stack<int>();
ids.push(7);

any input = "8";
try {
    ids.push(input);
} catch (TypeError e) {
    io.println("rejected: " + e.message);
}
io.println(ids.items);

In Geblang, Stack<int> is a real type: the binding is kept at run time, so push rejects a string even when it arrives as an untyped value. See the type system guide.

Request bodies typed by your own classes

In Laravel, the shape of a request lives in a list of validation rule strings, separate from the types the rest of your code uses.

PHP (Laravel)

public function store(Request $request)
{
    $data = $request->validate([
        'email' => 'required|string',
        'age' => 'required|integer',
    ]);

    return response()->json(['welcome' => $data['email']], 201);
}

Geblang (Gebweb)

import gebweb;
import http;
import io;

class Signup {
    string email;
    int age;
}

@Controller("/signup")
class SignupController extends gebweb.Controller {
    @Post("")
    func create(Signup body): http.Response {
        return this.json({"welcome": body.email}, 201);
    }
}

let client = gebweb.TestClient(gebweb.app());
let ok = client.post("/signup", {"email": "[email protected]", "age": 36});
io.println("${ok.status} ${ok.text()}");
let bad = client.post("/signup", {"email": "[email protected]", "age": "old"});
io.println("${bad.status} ${bad.json()["detail"]}");

Gebweb binds the JSON body to the Signup class and enforces its field types, so a wrong type is answered with a 400 problem-details response that names the field. Add @Assert rules for constraints beyond types. See parameter binding and validation.

One server, many concurrent requests

Under PHP-FPM each request starts from a fresh state, so configuration is parsed, containers are built, and connections are opened again and again. A Geblang server is one long-running process: each request runs concurrently on a goroutine, while caches, connection pools, and services built once stay in memory.

Inside a handler, async and await fan out to several services at once, and background work runs as Gebweb jobs or scheduled tasks in the same codebase. See concurrency in Gebweb.

Deploy one binary, not a stack

A PHP site needs the PHP runtime, its extensions, a PHP-FPM pool, and a web server in front. A Geblang application builds into one executable that includes its own HTTPS server, and the standard library can obtain certificates automatically.

geblang build --entry app --out dist/app
./dist/app

Which should you choose?

Choose Geblang when:

  • You want PHP's productivity with generics and types that the runtime enforces.
  • A service should keep state in memory and handle many requests concurrently in one process.
  • You want typed request handling, validation, views, and OpenAPI from one framework.
  • You want to deploy one binary instead of a runtime, extensions, FPM, and a web server.

PHP still makes sense when you are building on WordPress, Laravel, or Symfony, maintaining an existing PHP codebase, or deploying to shared hosting where PHP is the runtime on offer.

Frequently asked questions

Is Geblang a good alternative to PHP for web development?

Yes for APIs and server-rendered sites. The Gebweb framework provides controllers with route attributes, dependency injection, typed request binding, validation, Twig-style views, sessions and JWT authentication, CSRF protection, background jobs, and generated OpenAPI documentation, and the whole application deploys as one executable.

Does Geblang have real generics?

Yes. Generic classes and functions keep their type arguments at run time, so a Stack<int> rejects a string even when the value arrives untyped. In PHP, generics exist only in docblocks for static analysers.

How does a Geblang server handle concurrency compared with PHP-FPM?

A Geblang server is one long-running process. Requests are handled concurrently on goroutines, and state such as caches and connection pools stays in memory between requests instead of being rebuilt for each one.

Do I need nginx or PHP-FPM to run a Geblang site?

No. The HTTP server is part of the standard library and of the built executable, and it serves HTTPS directly, including automatic certificates. You can still put a reverse proxy in front of it if your infrastructure uses one.

Can I use a database the way I use PDO?

Yes. The db module is a PDO-style wrapper with SQLite, PostgreSQL, and MySQL drivers, prepared statements, transactions, and streaming result sets.