webman vs Hyperf: How They Work Under the Hood

Alright, let’s take a deep dive into how webman and Hyperf work under the hood, and how annotations are implemented in PHP.

1. How webman works under the hood

webman‘s core design philosophy is lightweight, high-performance, easy to use. It’s built on top of the powerful asynchronous, event-driven network library Workerman, making full use of PHP’s CLI (command-line interface) mode and event loop mechanism, and doing away with the traditional PHP-FPM model to achieve high performance and high concurrency.

Core principles

  1. Built on Workerman:

    • Event-driven: Workerman uses the event loop library provided by the libevent (default), event, or swoole extension. It creates and manages one or more event loops in one or more master processes.
    • Non-blocking I/O: All network operations (TCP/UDP/Unix Socket listening and connections) are non-blocking. When data becomes readable or writable on a connection, or an error occurs, the event loop triggers the registered callback to handle it. This avoids the huge overhead of creating a thread or process per connection.
    • Multi-process model:
      • Master process: Parses the configuration, creates listening sockets, manages child processes (Worker processes), monitors child process state (e.g. restart on crash), and handles signals (e.g. reload, stop, status).
      • Worker process: The process that actually handles business logic. The master process forks multiple Worker child processes. By default, these Worker processes are resident in memory. Each Worker process runs its own event loop independently and handles the connection requests assigned to it.
      • Inter-process communication (IPC): Master and Worker usually communicate over a Unix Socket pipe (for example, to send a reload signal or query status).
    • Protocol support: Workerman has built-in support for HTTP, WebSocket, TCP, UDP and other protocols, and protocols can be customized.
  2. What webman adds on top of Workerman:

    • PSR compatibility: It provides implementations that comply with PSR-7 (HTTP message interfaces), PSR-15 (HTTP middleware), PSR-11 (container) and other standards, so developers can use the familiar middleware pattern and dependency injection.
    • Routing: It offers flexible route configuration (such as the file-based routing in route.php), mapping HTTP requests to the corresponding controller method or closure.
    • Middleware: It implements an onion-model middleware mechanism, making it easy to handle logic before and after a request (such as authentication, logging, CORS).
    • Controllers: They organize the business logic code.
    • Views: It supports simple template rendering.
    • Database/Redis: It offers convenient wrappers, but note that under the default Worker process model, database connections cannot be automatically released/reused the way they are with Swoole coroutines — the developer has to manage the connection lifecycle themselves (typically creating and closing a connection within a single request-handling cycle, or using a connection pool). webman officially provides plugins such as webman/redis-queue to help with this.
    • File monitoring and hot reload:
      • php webman start enables monitoring with the -w flag by default.
      • Workerman uses the Linux kernel’s inotify mechanism (or macOS’s kqueue) to monitor changes to project files (.php, .env, etc.).
      • When a file change is detected, the master process sends the SIGUSR1 signal to all Worker processes.
      • On receiving the signal, a Worker process safely finishes the request it is currently handling (if any), then gracefully restarts itself (reloading the modified code), while the master process keeps running and forks new Worker processes. This process is extremely fast — users barely notice any service interruption, which is how “hot reload” is achieved.
  3. Key points for high performance:

    • Resident in memory: The biggest advantage. The framework core, business code, and configuration are loaded once when the Worker process starts and stay resident in memory. Subsequent requests don’t need to re-initialize the framework, reload core files, or reconnect to the database (if the connection pool is managed properly), which greatly reduces overhead.
    • Event-driven & non-blocking: It handles high-concurrency connections efficiently, and is especially suited to I/O-intensive scenarios.
    • Lightweight: The framework itself has little code, thin abstraction layers, and low overhead.

Summary of how webman works: It uses Workerman’s event-driven, non-blocking I/O and multi-process model to provide a high-performance network foundation. On top of that, webman builds a lightweight web framework that conforms to modern PHP development standards (PSR), and uses file monitoring to provide convenient hot reload. Its core strengths are the performance gain from being resident in memory and Workerman’s stability.

2. How Hyperf works under the hood

Hyperf is a high-performance, highly flexible, enterprise-grade coroutine framework. Its core is built on the Swoole extension, making deep use of the coroutine capability Swoole provides to achieve high performance and high concurrency, and it borrows heavily from the design philosophy of frameworks like Java’s Spring Cloud (such as dependency injection, AOP, annotation-driven development).

Core principles

  1. Built on Swoole:

    • Coroutines: Swoole’s core capability. Coroutines are user-space lightweight threads, scheduled by the framework or runtime itself, with extremely low switching cost (usually just the overhead of a function call). Hyperf embraces coroutines deeply — almost every component (HTTP Server, Database Client, Redis Client, RPC Client/Server, AMQP, etc.) is designed to be coroutine-safe or coroutine-friendly.
    • Coroutine scheduling: Swoole provides a coroutine scheduler. When a coroutine hits an I/O operation (such as a database query, network request, or file read/write), it automatically suspends (yield) and gives the CPU to other ready coroutines. When the I/O operation completes, the scheduler resumes that coroutine to continue execution. This means a single process can concurrently handle thousands of connections/tasks, while the code logic is still written sequentially (the callback hell problem of async callbacks is greatly alleviated).
    • Event-driven: The underlying layer is still event-driven (based on epoll/kqueue, etc.); Swoole’s event loop drives coroutine scheduling.
    • Server: It provides high-performance HTTP Server, WebSocket Server, TCP/UDP Server, etc. Hyperf mainly uses the HTTP Server.
  2. Hyperf’s higher-level encapsulation and architecture on top of Swoole:

    • Powerful dependency injection container (DI Container):
      • It’s the cornerstone of the whole framework (based on the hyperf/di component).
      • It implements PSR-11.
      • It supports autowiring, binding interfaces to implementations, constructor injection, property injection, and method injection.
      • It manages the lifecycle of almost every object in the application (singleton, prototype, etc.).
      • It’s the foundation for AOP and annotation-driven implementation.
    • Annotation-Driven Development:
      • Hyperf relies heavily on annotations to configure routes, define middleware, declare AOP aspects, mark scheduled tasks, configure dependency injection, define RPC services, and so on.
      • At framework startup (or at Worker process startup, depending on the annotation scope), it scans the code via reflection, parses the annotations on classes, methods, and properties, collects metadata, and dynamically generates proxy classes or performs the corresponding configuration registration (such as registering route information into the router).
      • This greatly improves development efficiency and the readability and maintainability of the code (configuration sits right next to the code).
    • Aspect-Oriented Programming (AOP):
      • Implemented on top of the DI container and dynamic proxies.
      • It lets developers define “aspect” (Aspect) classes containing “advice” (Advice - such as @Before, @After, @Around) and “pointcuts” (Pointcut - specifying via annotation or expression which methods of which classes should be intercepted).
      • At runtime, the framework generates a proxy class for the target classes matching the Pointcut. When the target method is called, the proxy class’s method is actually called; the proxy executes the relevant Advice logic in order (such as logging, performance monitoring, transaction management, caching, permission checks), and then calls the original target method, either directly or around it.
      • This decouples cross-cutting concerns from the core business logic.
    • Coroutine context management:
      • Because coroutines are lightweight and execute concurrently, traditional global variables and singleton patterns can be unsafe in a coroutine environment (a change made by one coroutine affects the others).
      • Hyperf provides the hyperf/context component, which uses Swoole’s coroutine API (Swoole\Coroutine::getContext()) to achieve coroutine-level context isolation. The Context class lets you safely store and retrieve data within the same coroutine.
    • Connection pools:
      • They provide pooled management for expensive resources (such as database connections, Redis connections, HTTP client connections).
      • When a coroutine needs a resource, it takes one from the pool; when it’s done, it returns it to the pool.
      • This avoids the overhead of frequently creating and destroying connections, greatly improving performance, and it naturally fits the coroutine model (each coroutine uses its own connection, avoiding concurrency issues).
    • Componentization and async non-blocking clients:
      • Hyperf provides a large number of out-of-the-box high-performance coroutine components: database (hyperf/database - based on Eloquent/Doctrine, with connection pooling), Redis (hyperf/redis - with connection pooling), cache, queue (hyperf/async-queue), RPC (hyperf/json-rpc), service registration and discovery (hyperf/service-governance), config center (hyperf/config), distributed tracing (hyperf/tracer), rate limiting and circuit breaking (hyperf/rate-limit, hyperf/circuit-breaker), GraphQL, gRPC, AMQP, WebSocket, and more.
      • Under the hood, these clients all use Swoole’s coroutine Client or a self-implemented coroutine-based Client, ensuring every I/O operation is async and non-blocking and can be suspended and resumed by the coroutine scheduler.
    • Process model:
      • Master process: Manages the service lifecycle.
      • Manager process: Manages Worker/TaskWorker processes (creation, recycling).
      • Worker process: Handles network requests (HTTP, WebSocket, TCP, etc.). Each Worker process is an independent coroutine scheduling unit, and can internally run a large number of concurrent coroutines to handle requests.
      • TaskWorker process (optional): Specifically handles long-running synchronous blocking tasks (such as certain complex computations, or calling libraries that don’t support coroutines). Worker processes dispatch tasks to the TaskWorker via task(). Hyperf also offers an asynchronous task processing solution based on message queues (async-queue).
    • Hot reload:
      • The principle is similar to webman/Workerman, using inotify/kqueue to monitor file changes.
      • It sends a signal to Worker processes (SIGUSR1 or SIGTERM) to notify them to restart gracefully (exit after finishing the current request, and the master process starts new Workers to load the new code). Hyperf’s DI container and proxy class generation mechanism make hot reload relatively reliable.

Summary of how Hyperf works: It integrates Swoole coroutines deeply to build a high-performance, enterprise-grade microservice framework. Its core lies in a powerful dependency injection container, annotation-based metaprogramming and configuration, AOP decoupling of cross-cutting concerns, well-developed coroutine context and connection pool management, and a large number of out-of-the-box high-performance coroutine components. It’s better suited to building complex, distributed, high-concurrency systems (such as microservice architectures).

3. How PHP annotations work

Annotations (often called DocBlock Annotations before PHP 8; PHP 8 introduced native Attributes) are a metadata mechanism. They let you attach structured information (metadata) to code elements (classes, methods, properties, functions, parameters). That information doesn’t directly affect the logical execution of the code, but it can be read and used at runtime or compile time by external tools (such as frameworks, libraries, IDEs, documentation generators).

How they’re implemented (focusing on how frameworks use them)

  1. Before PHP 8 (DocBlock Annotations):

    • Where they’re stored: The metadata is written above the code element as a comment block in a specific format (/** ... */).
    • Format: It follows the PHPDoc standard, but frameworks define their own special tags (such as @Route, @Inject, @Cacheable).
    • Parsing:
      • Reflection: The framework uses PHP’s reflection API (ReflectionClass, ReflectionMethod, ReflectionProperty) to get the code elements (classes, methods, properties).
      • Getting the DocComment: It calls the reflection object’s getDocComment() method to get the doc comment string on that element.
      • Parsing the string: The framework has to write its own parser (or use a library like doctrine/annotations) to parse that string. The parser:
        • Recognizes tags starting with @.
        • Parses the arguments after the tag (which may be simple strings, key-value pairs, arrays, or even nested structures).
        • Converts the parse result into structured data (usually an array or a specific annotation object).
    • Processing:
      • Scan at startup: The framework usually scans all PHP files under a specified directory or namespace during the startup phase (or on the first request).
      • Reflect and parse: For scanned classes, methods, and properties, it uses reflection to get the DocComment and parse it.
      • Collect metadata: It collects and stores the structured annotation information obtained from parsing (for example, registering @Route information into the route table, or using @Inject information for the DI container’s autowiring configuration).
      • Use at runtime: When handling requests or executing specific logic, the framework guides its behavior based on the annotation metadata collected earlier (such as matching controller methods based on route annotations, or deciding whether to read data from cache based on cache annotations).
  2. PHP 8 and later (native Attributes):

    • Language-level support: PHP 8 introduced annotations as a first-class language feature, called Attributes. They are no longer comments but a formal syntax construct.
    • Definition: They’re defined with the #[...] syntax. An Attribute is itself an ordinary PHP class (usually extending \Attribute), and can have its own constructor, properties, and methods, used to define and validate the structure of the metadata.
    • Retrieving via reflection: The reflection API added a getAttributes() method (for example ReflectionClass::getAttributes()). This method returns an array of ReflectionAttribute objects.
    • Instantiation: You can instantiate the Attribute class object via the ReflectionAttribute::newInstance() method (if the class is defined), passing in the arguments provided when the Attribute was defined. This gives you a strongly-typed, structured annotation object.
    • Advantages:
      • Performance: Native syntax parses far faster than parsing DocComment strings.
      • Validation: The Attribute class can define constructor parameter types, and the PHP engine performs parameter type checking at definition time, avoiding runtime parsing errors.
      • Clear structure: Strongly-typed objects are clearer and safer than arrays obtained by parsing strings.
      • IDE support: IDEs can better recognize, autocomplete, and check Attributes.
    • Framework processing flow (similar to DocBlock, but more efficient and safer):
      • Scan directories/namespaces at startup.
      • Use the reflection API to get classes/methods/properties.
      • Call getAttributes() to get the list of attached Attributes.
      • Use newInstance() to instantiate the needed Attribute objects.
      • Collect the metadata contained in these Attribute objects.
      • Configure the framework based on that metadata (registering routes, configuring DI, defining AOP pointcuts, etc.).

Summary of how annotations work: Whether it’s the old DocBlock or the new native Attribute, the essence of an annotation is metadata attached to a code element. The framework uses reflection to scan the code at startup (or on demand), parses that metadata, converts it into structured information, and uses that information to dynamically configure framework behavior, generate proxy code (AOP), or guide runtime logic. Native Attributes bring a significant improvement in performance, type safety, and developer experience. Modern frameworks like Hyperf have fully switched to native Attributes.

webman vs Hyperf: core principles comparison summary

Feature webman Hyperf
Core engine Workerman (event-driven, multi-process) Swoole (event-driven + coroutines, multi-process)
Programming model Event callbacks + traditional synchronous logic (you manage blocking I/O yourself) Coroutines (write synchronously, async non-blocking I/O)
Performance key Resident in memory (less initialization overhead) Resident in memory + coroutines (high concurrency, low resource usage)
Connection management Must be managed manually or via plugins/connection pools Built-in coroutine connection pool (DB, Redis, HTTP Client, etc.)
Core mechanisms Lightweight routing, middleware, PSR wrappers Powerful DI container, annotation-driven, AOP, rich componentization
Concurrency High (multi-process) Extremely high (multi-process + high coroutine concurrency within a single process)
Complexity Lightweight and simple, gentle learning curve Powerful and complex, steeper learning curve (you need to understand DI, AOP, coroutines)
Positioning High-performance HTTP APIs / simple real-time apps Enterprise-grade, microservices, distributed systems, complex high-concurrency applications
Hot reload Supported (based on inotify/kqueue) Supported (based on inotify/kqueue)
Representative tech File watching, Workerman API Attributes, AOP, dependency injection, connection pools, service governance