PHPackages                             php-lrpm/php-lrpm - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. php-lrpm/php-lrpm

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

php-lrpm/php-lrpm
=================

PHP Long Running Process Manager

v1.0.0(2y ago)187.8k↓30.8%3[1 PRs](https://github.com/vrza/php-lrpm/pulls)1BSD-3-ClausePHPPHP &gt;=7.1

Since Feb 26Pushed 1y ago1 watchersCompare

[ Source](https://github.com/vrza/php-lrpm)[ Packagist](https://packagist.org/packages/php-lrpm/php-lrpm)[ RSS](/packages/php-lrpm-php-lrpm/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (6)Versions (9)Used By (1)

PHP-LRPM - PHP Long Running Process Manager
===========================================

[](#php-lrpm---php-long-running-process-manager)

PHP-LRPM is a framework for managing long-running PHP processes. It can be also be thought of as a process-level concurrent (and, optionally, distributed) PHP application engine.

[![lrpmctl screenshot](https://raw.githubusercontent.com/vrza/php-lrpm/main/lrpmctl.png)](https://raw.githubusercontent.com/vrza/php-lrpm/main/lrpmctl.png)

Quick Start
-----------

[](#quick-start)

Included is a bare-bones example application consisting of `test/MockConfigurationSource.php` and `test/MockWorker.php`, it can be started by running `example.sh`.

Motivation
----------

[](#motivation)

Dynamic management and supervision of PHP processes.

In a nutshel, PHP-LRPM can be described as a process farm state management framework — it receives declarations of state of the concurrent application (how many processes of which type need to be running), and updates the actual state of the application by spawning and/or terminating worker processes.

Stable and robust applicatinos utilizing process-level concurrency are not easy to program. PHP-LRPM provides a framework for building all kinds of concurrent applications, including (but not limited to) message queue consumers, web crawlers, parallel database replication services…​

On a surface level, LRPM is similar in function to generic process supervisors like daemontools or supervisord, but is configured through PHP and easily and smoothly integrates with existing PHP codebases.

LRPM provides an easy to use yet versatile framework that can vastly simplify many use cases where dynamically orchestrating PHP CLI SAPI processes through a general-purpose process supervisor (e.g. systemd) or scheduler (e.g. cron) would be cumbersome.

While LRPM could be used to build a "prefork"-style HTTP server, and thus could be used to build web services, this is not the primary motivation behind its design — good specialized frameworks for building HTTP applications in PHP are readily available. The primary motivation behind LRPM is to provide a framework for building PHP CLI SAPI applications using process-level concurrency, in a way that works with many existing PHP libraries and extensions that are not fork safe.

This [talk by Giorgio Sironi](https://youtu.be/MJkFHMOCEkg) given at the Dutch PHP Conference in 2017 covers some problems and challenges when implementing long-running PHP processes.

How it works
------------

[](#how-it-works)

LRPM dynamically gets its configuration from a custom source implemented in PHP (for example, user-provided PHP code might read configuration from a database), checks for changes in the configuration and starts/stops "worker" processes accordingly. Depending on application needs, LRPM configuration can be updated either in a poll (periodic) or push (reactive) fashion.

Features
--------

[](#features)

### Simple

[](#simple)

PHP-LRPM is built on top of POSIX process management facilities. PHP-LRPM starts its subprocesses via `fork(2)` and subprocesses don’t daemonize.

### Efficient

[](#efficient)

The operating system signals PHP-LRPM immediately when a process terminates. PHP-LRPM will in turn attempt to restart the terminated process.

PHP-LRPM is able to take advantage of the OPcache PHP extension, allowing worker processes to store compiled bytecode in shared memory, which reduces the total memory usage of the process farm.

On Linux systems, PHP-LRPM is also able to take advantage of the fact that the `fork(2)` system call is implemented through the use of copy-on-write pages. This reduces the memory usage of Linux deployments even further.

### Flexible

[](#flexible)

PHP-LRPM makes minimal assumptions about its use cases. It assumes no more than:

1. that worker processes will need to be provided with some sort of configuration
2. that they will need to run some code when they are started
3. that after initialization they might want to run in an endless loop.

PHP-LRPM uses exponential backoff when attempting to restart a process; some process managers will try several times in quick succession and fail the process until operator intervention.

### Reactive

[](#reactive)

PHP-LRPM configuration is reactive with regard to both process state changes and configuration changes. The process farm configuration is updated through a user-provided `ConfigurationSource`, that can either be polled in order to pull fresh configuration periodically, or block on an event (e.g. receiving a message from some message bus) in order to push fresh configuration in reaction to an event.

### Horizontally scalable

[](#horizontally-scalable)

It is easy to spread a PHP-LRPM process farm across multiple machines. Add-on package [php-lrpm-cluster](https://github.com/vrza/php-lrpm-cluster) provides clustering support.

### Stable

[](#stable)

PHP-LRPM is a stable platform that works equally well with existing and legacy PHP codebases and with newly developed code. Concurrent services built on top of PHP-LRPM have been reliably running in production for years.

To achieve a high level of stability, PHP-LRPM by design separates trusted framework code from untrusted user code, by running all user code in sandbox processes.

### Extensible

[](#extensible)

LRPM is designed to be easily extended without modifying the framework itself. Examples include, but are not limited to:

- Clustering support ([php-lrpm-cluster](https://github.com/vrza/php-lrpm-cluster)) is implemented as an extension.
- The [php-symplib](https://github.com/vrza/php-symplib) synchronous message passing framework, that PHP-LRPM uses internally, can also be used for communication between user processes, or even for workers communicating with the supervisor process.
- Facilities like detecting memory leaks in user code (LRPM itself is stable in this regard) can easily be implemented by developing specialized worker processes.

Non-goals
---------

[](#non-goals)

- Currently, LRPM is not intended to run as PID 1, nor was it tested in this role.
- Using main entry points other than `bin/lrpm`, i.e. using `ProcessManager` as a library and instantiating it from custom user programs, is not recommended and not supported.

Usage
-----

[](#usage)

1. Implement one or more `Worker` classes. The `PHPLRPM\Worker` interface exposes two public methods, `start()` and `cycle()`. The child process will call `start()`, passing the worker configuration as an argument, and then enter an endless loop, where it calls `cycle()`, and then checks for a shutdown condition (usually, the termination of the LRPM supervisor process). Backoff should be implemented at the end of the `cycle()` method, as we dispatch signal handlers right after `cycle()` returns. Be careful not to run a tight loop at times when cycles are not doing actual work!
2. Implement the `ConfigurationSource` class. The `PHPLRPM\ConfigurationSource` interface exposes a public method `loadConfiguration()`, that returns an associative array containing the configuration for all the workers (see example and full documentation in the `Job configuration` subsection).
3. Run `bin/lrpm` with your configuration class as an argument, e.g. `bin/lrpm '\MyNamespace\MyConfigurationSource'`

### Job configuration

[](#job-configuration)

Configuration is a simple associative array of jobs to run. Here’s an example configuration consisting of a single job, with all the mandatory fields described in comments:

```
$config = [
    42 => [ // Unique job id (string or int)
        'name' => 'Job 42', // Descriptive name (string)
        'workerClass' => '\PHPLRPM\Test\MockWorker', // Class implementing the Worker interface (string)
        'mtime' => 1629121362, // Time when this job config was last modified, as UTC Unix timestamp (int)
        'workerConfig' => [] // Array of additional configuration specific to the Worker implementation (array)
    ]
]
```

Class `ConfigurationValidator` is used for config validation internally, and you can also use it to test your `ConfigurationSource` implementation.

If a job’s `mtime` returned by the `ConfigurationSource` is newer than `mtime` from previous poll, that job will be restarted with the new configuration.

#### Configuration fields

[](#configuration-fields)

##### mtime

[](#mtime)

Time of last modification, must be a UTC Unix timestamp integer

Mandatory: yes

##### name

[](#name)

Descriptive job name, must be a string

Mandatory: yes

##### workerConfig

[](#workerconfig)

Worker-specific configuration, must be an array

Mandatory: yes

##### workerClass

[](#workerclass)

Worker class, must be a string referencing an existing class

Mandatory: yes

##### shortRunTimeSeconds

[](#shortruntimeseconds)

Minimum number of seconds a process is expected to run; if the process terminates earlier then this, it will be restarted with backoff

Mandatory: no
Default value: 5

##### shutdownTimeoutSeconds

[](#shutdowntimeoutseconds)

Time in seconds to wait for SIGCHLD after sending SIGTERM to a child, before killing the child with SIGKILL

Mandatory: no
Default value: 10

### Push configuration

[](#push-configuration)

Instead of relying on periodic polling of the `ConfigurationSource` (default interval between polls is 30 seconds), it is possible to push configuration updates in response to an event. Here’s how:

- Make the `loadConfiguration()` method block waiting for a configuration change event
- Set the configuration poll interval to 0, e.g. `bin/lrpm --interval=0 '\MyNamespace\MyPushConfigurationSource'`

Unless you are sure that your blocking call can get interrupted by a SIGTERM, set a short wait limit, e.g. less than 5 seconds, in order to help the service shut down cleanly.

### Signal handling

[](#signal-handling)

The LRPM supervisor process installs signal handlers for SIGCHLD (child processes termination notifications), SIGUSR1 (configuration process readiness notification), SIGTERM and SIGINT (shut down request).

The configuration process (the process running user-provided `ConfiguratinoSource` class) installs a signal handler for SIGHUP, that will reset the internal configuration poll timer, effectively making LRPM reload configuration immediately.

Worker processes (processes running user-provided `Worker` classes) install default signal handlers for SIGTERM and SIGINT. Signal handlers are dispatched between loop cycles, and these default handlers will terminate the Worker.

You can implement and install your own signal handlers inside your Worker implementation, but make sure that your Worker process shuts down cleanly after receiving SIGTERM, otherwise the LRPM supervisor will consider it unresponsive and follow up with a SIGKILL.

### Implementing a custom entry point

[](#implementing-a-custom-entry-point)

If for some reason you need to implement a custom entry point for LRPM, this is possible, but not recommended and not supported.

The code in the entry point runs in the supervisor (parent) process, while `Worker` classes run in child processes `fork(2)`-ed from the supervisor. Ideally the entry point should do no more than set up the autoloader and run the `ProcessManager`. Any open file descriptors apart from stdin/stdout/stderr should be closed before entering the event loop (`ProcessManager→run()`). Sharing open sockets between parent and children through `fork(2)` is not safe! Worker processes should connect to wherever they need to connect to only after they have been spawned.

For these and other reasons it is recommended to use the provided `bin/lrpm` entry point.

Operating LRPM
--------------

[](#operating-lrpm)

It is recommended to run LRPM as a normal system service. Its main process stays in the foreground and logs to stdout and stderr.

For LRPM to be able to listen for control messages, it needs to create a Unix domain socket in the `/run/php-lrpm` directory — make sure that this directory is writable by the main LRPM process. As a fallback, LRPM will attempt to create a socket in `/run/user//php-lrpm`. If a socket cannot be created, LRPM wil run with control messaging disabled.

Place the `bin/lrpmctl` tool into your PATH (either by adding `vendor/bin` to the PATH, or symlinking `lrpmctl` to e.g. `/usr/local/bin`) and use it to query the running instance for status, or to restart a process on demand. Type `lrpmctl -h` for more detailed usage instructions.

To take advantage of caching precompiled bytecode in shared memory, you need to explicitly enable using the OPcache extension in the CLI SAPI, and make sure that it’s configured to store the cache in shared memory. Minimal recommended config is:

```
opcache.enable=1
opcache.enable_cli=1
opcache.file_cache_only=0
```

Architecture
------------

[](#architecture)

LRPM was designed for stability and robustness. Many existing 3rd party PHP extensions are not fork safe, and can fail ungracefully in a process-level concurrent application. To work around this, LRPM runs user code in sandbox processes, providing a pristine code execution environment that benefits 3rd party libraries and extensions that rely on the "happy path".

In the following architecture diagram, green rectangles represent sandbox processes for running untrusted user code (including any extensions and drivers specific to user code). Yellow rectangles represent processes running trusted framework code. While it is possible to use the trusted code as a library, this is not recommended, and supporting such use cases is a non-goal.

[![LRPM architecture diagram](https://raw.githubusercontent.com/vrza/php-lrpm/main/lrpm-architecture.svg)](https://raw.githubusercontent.com/vrza/php-lrpm/main/lrpm-architecture.svg)

Development roadmap
-------------------

[](#development-roadmap)

### Completed

[](#completed)

#### Improve metadata handling

[](#improve-metadata-handling)

PHP-LRPM keeps metadata in an associative array. For efficient lookups by PID, a separate index is maintained.

This functionality was offloaded to a generic library [Array with Secondary Keys](https://github.com/vrza/array-with-secondary-keys), that wraps a hash map and maintains secondary indexes (similar to how secondary keys in an SQL database work). Implementing this particular collection lead to the creation of [Cardinal Collections](https://github.com/vrza/cardinal-collections), a PHP toolkit for building collections.

#### Implement receiving, handling and responding to control messages

[](#implement-receiving-handling-and-responding-to-control-messages)

Included is the `lrpmctl` tool, which uses the [SyMPLib](https://github.com/vrza/php-symplib) library to exchange messages with a running instance of LRPM over a Unix domain socket connection. Some examples of messages include getting the `status` of all workers (see screenshot above), and requesting a `restart` of a worker process.

#### Make sure unresponsive processes get terminated

[](#make-sure-unresponsive-processes-get-terminated)

Wait for children to terminate after sending SIGTERM, follow up with SIGKILL if child doesn’t respond to SIGTERM after some time.

#### Blocking shutdown

[](#blocking-shutdown)

Implemented blocking shutdown loop that makes sure all children are terminated on shutdown, including processes that may be unresponsive.

#### Configuration process

[](#configuration-process)

Made `ConfigurationSource` run in a process separate from the supervisor. This is to prevent `Worker` processes inheriting sockets opened by `ConfigurationSource` code (e.g. persistent database connections). The supervisor process and the config process are using the SyMPLib library to exchange messages over a Unix domain socket connection.

Some name ideas that were considered
------------------------------------

[](#some-name-ideas-that-were-considered)

- Palermo
- polearm
- poolroom
- pillar-pm
- polar-pm
- plural-pm
- plier-pm

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance30

Infrequent updates — may be unmaintained

Popularity33

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~190 days

Total

5

Last Release

783d ago

Major Versions

v0.9.2 → v1.0.02024-03-26

### Community

Maintainers

![](https://www.gravatar.com/avatar/b842fcccb4fa8eb90b6d6645e9fe1066b59ca01b3c38aa90c3b80a5ce07d02fe?d=identicon)[vrza](/maintainers/vrza)

---

Top Contributors

[![vrza](https://avatars.githubusercontent.com/u/1699472?v=4)](https://github.com/vrza "vrza (228 commits)")

---

Tags

phpposixprocessmanagerlong-running

### Embed Badge

![Health badge](/badges/php-lrpm-php-lrpm/health.svg)

```
[![Health](https://phpackages.com/badges/php-lrpm-php-lrpm/health.svg)](https://phpackages.com/packages/php-lrpm-php-lrpm)
```

###  Alternatives

[arara/process

Provides a better API to work with processes on Unix-like systems

16861.7k2](/packages/arara-process)[claudiodekker/changelog-updater

A PHP package to programmatically update your changelog file.

1315.5k](/packages/claudiodekker-changelog-updater)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
