PHP Censor: a self-hosted CI server for PHP, and the Beanstalkd dependency it hands you
PHP Censor is an open source self-hosted continuous integration server for PHP projects.
At a glance
- What is it?
- PHP Censor is a BSD-2-Clause, self-hosted continuous integration server for PHP projects, forked from PHPCI. It runs a fixed plugin pipeline over a build queue, and the queue is Beanstalkd, which is the adoption decision most teams will actually have to make.
- Who is it for?
- Adopt PHP Censor if you already run Beanstalkd and a MySQL, MariaDB or PostgreSQL instance on a Unix host, and you want your CI configuration to live in a .php-censor.yml file next to the code. Do not adopt it if your build hosts are Windows, if you cannot run a queue daemon, or if you need container-per-build isolation.
- Can I use it commercially?
- Yes. BSD-2-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 108 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem PHP Censor solves, and the teams it fits
PHP Censor exists for PHP teams that want a CI server they host themselves, configured per project rather than per pipeline definition language. The README describes it as an open source, self-hosted continuous integration server for PHP projects and a fork of PHPCI. That fork lineage matters: it means the design predates the YAML-heavy pipeline DSLs that most hosted CI services now use, and the configuration surface still reflects that.
The target user is a team with a PHP application, a Unix server, and a database it can point CI at. The feature list covers the chores that PHP projects repeat: cloning from GitHub, Bitbucket, GitLab, plain Git, Mercurial, Subversion or a local directory; installing Composer dependencies; running PHPUnit, Atoum, Behat, Codeception and PHPSpec; and checking code with Lint, PHPParallelLint, Pdepend, PHPCodeSniffer, PHPCpd, PHPCsFixer, PHPDocblockChecker, PHPLoc, PHPMessDetector, PHPTalLint and TechnicalDebt.
Database-backed test setup is called out separately in the README, with dedicated plugins for PostgreSQL, MySQL and SQLite. For a project whose test suite needs a real schema, that is the part that usually forces a choice: either the CI server knows how to create and tear down the database, or every build script reinvents it. PHP Censor puts that in the plugin layer.
It is not aimed at teams who want ephemeral, container-per-build isolation. Nothing in the supplied material describes containers, and the system requirements list a persistent web server plus a queue daemon, which points at a long-running host rather than a fresh sandbox per build.
How a build actually flows: config file, queue, plugins
The configuration model is the clearest part of the design. The README gives three ways to set a project up. You can add a project with no config at all, in which case PHP Censor runs what it calls zero-config plugins: Composer, TechnicalDebt, PHPLoc, PHPCpd, PHPCodeSniffer, PHPMessDetector, PHPDocblockChecker, PHPParallelLint, PHPUnit and Codeception. You can add a .php-censor.yml file to the root of your repository, which the README compares to Travis CI. Or you can paste the config into the project page in the PHP Censor UI, and the README states that doing so cancels the file config from the repository.
That last rule is worth reading twice. If someone edits the project config in the web interface, the .php-censor.yml in the repository stops applying. Teams that expect the file to be the single source of truth will be surprised the first time a UI edit silently overrides it.
The config itself is a flat set of stages. The README example uses setup, test and complete. Under setup, the composer plugin takes an action of install and a directory of ".". Under test, php_unit takes a config path of phpunit.xml; php_mess_detector and php_cpd each take allow_failures: true; php_code_sniffer takes a standard of PSR2. Under complete, email_notify takes a default_mailto_address.
The allow_failures key is the most useful detail in that example. It lets a noisy checker report without failing the build, which is how most teams phase in static analysis on an existing codebase. The plugin names in the YAML are snake_case versions of the tool names in the feature list, so the mapping from tool to config key is mechanical once you see one example.
What the README does not show is the internal execution path: how the queue hands work to a worker, how the stages are ordered relative to one another, or how plugin output is aggregated. Those details live in docs/en/configuring_project.md, which is referenced but not included in the supplied material. Treat the stage ordering as something to confirm from the documentation rather than assume.
Getting it running: requirements, migrations, and the console
The system requirements are short and firm. A Unix-like OS, with an explicit note that Windows is not supported. PHP 7.4 or newer, with OpenSSL support and the functions exec(), shell_exec() and proc_open() enabled. A web server, Nginx or Apache2. A database, MySQL, MariaDB or PostgreSQL. And Beanstalkd as the queue.
That last requirement is not optional and is not a plugin. If your environment cannot run Beanstalkd, PHP Censor is the wrong tool regardless of how well the rest fits.
The README points to docs/en/installing.md for the install procedure itself, so the exact setup steps are not in the supplied material. What is in the material is the maintenance command set. Migrations are applied with:
cd /path/to/php-censor ./bin/console php-censor-migrations:migrate
and a new migration is scaffolded with:
cd /path/to/php-censor ./bin/console php-censor-migrations:create NewMigrationName
The presence of a migration system tells you something about upgrades: schema changes ship as migrations you run yourself, not as an automatic step. The README also links docs/en/updating.md and, for the 2.0 line, docs/UPGRADE_2.0.md, which implies the v1 to v2 jump needed more than a package update.
Code style for the project itself is:
./vendor/bin/php-cs-fixer fix --allow-risky=yes
and its test suite is:
./vendor/bin/phpunit --configuration ./phpunit.xml.dist --coverage-html ./tests/runtime/coverage -vvv --colors=always
The README notes that Phar plugin tests need phar.readonly set to Off in php.ini, otherwise those tests are skipped, and that database tests need empty MySQL and PostgreSQL databases created on localhost with credentials matching the environment variables in phpunit.xml.dist. Those two notes are the kind of detail that saves an afternoon, and they are also a hint about how much of the project's own test suite depends on external services being present.
The Beanstalkd requirement is the real adoption cost
Most CI tools ask you for a runner binary. PHP Censor asks you for a queue. Beanstalkd is a separate daemon with its own lifecycle, its own supervision, and its own failure modes, and the README lists it as a bare requirement alongside the web server and the database.
The practical consequence is that a PHP Censor deployment is at least four moving parts: web server, PHP-FPM or equivalent, database, and Beanstalkd. Every one of them has to be up for a build to complete. If Beanstalkd is down, the web interface may still accept a build request while nothing consumes it, which is a worse failure than an outright error because the build simply sits there.
The supplied material does not describe worker supervision, retry behaviour, or what happens to queued jobs when the queue restarts. Those are the questions to answer from docs/en/installing.md and the deployment documentation before you commit, because they determine whether a dropped queue means a delayed build or a lost one.
There is a second constraint in the same list: Windows is not supported. If your build agents are Windows machines, this is a hard stop, not a configuration problem.
And a third: PHP 7.4 or newer is the floor across the supported 2.0 and 2.1 lines. The version table shows 1.0 through 1.3 capped at PHP below 8.0 and marked unsupported, with 2.0 and 2.1 both requiring 7.4 or newer. If you are still on a PHP 5.x codebase, the supported path does not exist.
Version lines, upgrade cost, and what the licence does not settle
The version table is unusually honest. Four older lines, 1.0 Morty Smith through 1.3 Jerry Smith, are labelled Old version and UNSUPPORTED, each pinned to PHP >=5.6 and <8.0. The 2.0 line, Rick Sanchez, is described as the last stable version and points at an upgrade guide from v1 to v2. The 2.1 line, Mr. Meeseeks, is the current stable version. A 2.2 line is marked WIP on the master branch.
Recent releases in the supplied material are 2.1.6 and 2.0.14, both dated 2025-03-15, and 2.1.5 from 2024-05-04. The 2.0 line is still receiving releases alongside 2.1, which suggests the project maintains two branches rather than forcing everyone forward. That is a real cost for maintainers and a real benefit for anyone mid-upgrade.
The upgrade cost itself is not a package bump. Migrations are a manual console command, and the existence of a separate UPGRADE_2.0.md document implies the v1 to v2 transition involved more than dependency changes. Budget for reading that document and running php-censor-migrations:migrate against a staging database before touching production.
The licence is BSD-2-Clause, confirmed by the Packagist licence badge in the README. That is a permissive licence, which in practice means you can modify and redistribute the server, including in a commercial setting, provided the copyright notice and licence text are retained. It says nothing about the licences of the plugins you enable, the tools those plugins invoke, or your own project's dependencies. If you fork PHP Censor and ship it internally, the plugin licences are a separate question, and this is not legal advice.
Where PHP Censor sits against a hosted CI service
The obvious alternative is a hosted CI service, and the README itself points at the comparison by saying the .php-censor.yml approach is similar to Travis CI. The difference in approach is where the pipeline definition lives and who owns the runtime.
With a hosted service, you write a config file in the repository and the provider supplies the machines, the queue, the isolation and the upgrade path. You trade control and data locality for operational work you do not do. With PHP Censor, you supply the Unix host, the web server, the database and Beanstalkd, and in exchange the build runs on hardware you control, with source that never leaves your network, and with a plugin set you can modify because the whole server is BSD-licensed PHP you can read.
The concrete difference shows up in the plugin model. PHP Censor's plugins are named after the tools they wrap, and the config keys map directly onto tool arguments, as with php_code_sniffer taking a standard of PSR2. A hosted service typically asks you to install the tool yourself inside the build script and invoke it. PHP Censor has already written the integration and given it a config key, which is less flexible but less repetitive.
The second alternative is running the tools directly from a git hook or a Makefile. That has no queue, no database, no web interface and no build history, but it also has no Beanstalkd. For a single-developer project with a test suite that finishes in seconds, the hook is the smaller system. PHP Censor earns its four moving parts when several projects share one server, when build history and notifications matter, and when database setup per build is something you would otherwise script by hand.
What to check before you install it
Confirm the environment first, because two of the requirements are disqualifying rather than inconvenient. Windows is not supported. Beanstalkd is required, not optional. If either fails, stop there.
Then confirm your PHP build exposes exec(), shell_exec() and proc_open(). Many hardened production PHP configurations disable exactly these functions, and PHP Censor's plugins invoke external tools, so a disabled function set will surface as plugin failures rather than an install error.
Then read docs/en/README.md and locate the plugin you depend on most. The README's feature list is long, but a listed tool and a documented plugin with a stable config key are not the same thing, and the supplied material only shows the plugin index by reference. Check that the key you intend to write in .php-censor.yml appears in that index.
Finally, decide how you will handle the two configuration sources. A .php-censor.yml in the repository, plus a project config in the web interface, is a conflict the README resolves in favour of the interface. If you want the file to win, do not create a UI config, and say so in whatever runbook your team keeps.
One more thing worth verifying on your own infrastructure rather than assuming: how the queue behaves when Beanstalkd restarts mid-build. The documentation referenced by the README is where that answer lives, and it is the difference between a retry and a rebuild.
Editorial conclusion
Adopt PHP Censor if you already run Beanstalkd and a MySQL, MariaDB or PostgreSQL instance on a Unix host, and you want your CI configuration to live in a .php-censor.yml file next to the code. Do not adopt it if your build hosts are Windows, if you cannot run a queue daemon, or if you need container-per-build isolation. Before committing, verify three things on your own hardware: that PHP 7.4 or newer has exec(), shell_exec() and proc_open() enabled, that your database and Beanstalkd are reachable from the web process, and that the plugin you depend on most appears in docs/en/README.md with a config key that matches your .php-censor.yml.
Community notes