kminet/php-obfuscator
最新稳定版本:0.0.1
Composer 安装命令:
composer require kminet/php-obfuscator
包简介
PHP source code obfuscator — transforms namespaces, classes, methods and variables into hashed equivalents while preserving full runtime behaviour
README 文档
README
⚠️ Draft / Work in Progress — This library is not yet production-ready. APIs, behaviour, and output format may change without notice. Use at your own risk.
A PHP code obfuscator that transforms PHP source into functionally identical but hard-to-read code. It uses a "backplate" mapping system to track original-to-obfuscated name transformations and supports multiple obfuscation levels with increasing coverage.
Requirements
- PHP 8.3+
- Composer
Installation
PhpObfuscator can be used in three ways depending on your workflow.
Standalone / global tool
Install it globally and use it from anywhere on your system:
composer global require kminet/php-obfuscator phpobf --level 1 src/
Per-project dev dependency
Install it inside your project and run it as part of your build process:
composer require --dev kminet/php-obfuscator vendor/bin/phpobf --level 1 src/
Cloned locally
Clone the repository and run directly:
git clone https://github.com/KminekMatej/PhpObfuscator.git
cd php-obfuscator
composer install
bin/phpobf --level 1 /path/to/your/project
Usage
CLI
Arguments:
<source> Path to the PHP file or directory to obfuscate
Options:
--config <file> Path to a Neon config file (see Configuration below)
--level <0-2> Obfuscation level — overrides the value in the config file
0 - structural only (namespaces, classes, enums)
1 - + methods and functions
2 - + variables
--output <dir> Output directory (default: temp/<id>)
--test-mode Keep original name prefix in obfuscated names (for debugging)
--keep-paths Do not relocate files to obfuscated PSR-4 directory structure
--keep-backplate-file Save backplate.json to the output directory (default: off)
--log-level <level> Log verbosity: debug, info, notice, warning, error (default: info)
--help Show help message
Examples:
# Use auto-detected config, override level via CLI flag phpobf --level 1 --test-mode src/ # Point to a custom config file phpobf --config phpobf.json src/
Configuration
PhpObfuscator is configured via a Neon file — the same format used by PHPStan. JSON is also accepted when passing a .json file via --config, which can be easier to generate from shell scripts or CI pipelines. The tool resolves the config file in this order:
- Path given by
--config <file>(explicit, highest priority) phpobf.neonin the current working directory (project-level config, committed to your repo)config/phpobf.default.neonbundled with phpobf (the built-in fallback)
CLI flags always take precedence over the config file — they override any matching key.
Creating a project config
Drop a phpobf.neon in your project root alongside composer.json. PhpObfuscator picks it up automatically without any --config flag:
# Obfuscation level preset (0–2) level: 1 obfuscatePaths: true testMode: false keepBackplateFile: false features: namespaces: null classes: null interfaces: null enums: null methods: null functions: null constants: null variables: null properties: null comments: null block: namespaces: classes:
Config reference
| Key | Type | Default | Description |
|---|---|---|---|
level |
int (0–2) |
1 |
Obfuscation level. Acts as a preset — defines which features are enabled by default. |
obfuscatePaths |
bool |
true |
Relocate output files to match the obfuscated PSR-4 namespace structure. |
testMode |
bool |
false |
Prefix obfuscated names with the original (e.g. Teacher_abc123) for easier debugging. |
keepBackplateFile |
bool |
false |
Save backplate.json alongside obfuscated output. ⚠️ Never distribute this file. |
features |
object |
(from level) | Fine-grained feature toggles. See below. |
block.namespaces |
string[] |
[] |
Regex patterns for namespaces to skip entirely (e.g. "/^Psr\\\\/" preserves PSR interfaces). |
block.classes |
string[] |
[] |
Regex patterns for class names to skip entirely. |
Feature flags
The features object lets you override individual obfuscation features on top of the level preset — the same way JavaScript Obfuscator handles presets. Each key accepts true, false, or null:
null(default) — inherit from the level presettrue— force this feature on, even if the level wouldn't enable itfalse— force this feature off, even if the level would enable it
| Feature | Level 0 | Level 1 | Level 2 | Description |
|---|---|---|---|---|
comments |
✓ | ✓ | ✓ | Strip all comments from source |
namespaces |
✓ | ✓ | ✓ | Rename namespace declarations |
classes |
✓ | ✓ | ✓ | Rename class, interface, and enum names |
interfaces |
✓ | ✓ | ✓ | Rename interface names (subset of classes) |
enums |
✓ | ✓ | ✓ | Rename enum names (subset of classes) |
methods |
✗ | ✓ | ✓ | Rename method and function definitions and call sites |
functions |
✗ | ✓ | ✓ | Rename standalone function definitions and call sites |
constants |
✗ | ✓ | ✓ | Rename class constants and enum cases |
variables |
✗ | ✗ | ✓ | Rename all variables (local, global, parameters) |
properties |
✗ | ✗ | ✓ | Rename class property declarations and $this->prop accesses |
Example — level 1 but without method obfuscation, and with comments preserved:
level: 1 features: methods: false comments: false
Example — block PSR and Symfony namespaces from obfuscation:
level: 2 block: namespaces: - /^Psr\// - /^Symfony\//
Ignoring specific declarations
You can exclude individual PHP declarations from obfuscation using two annotations. Both work for classes, interfaces, enums, methods, functions, properties, constants, enum cases, and variables.
@phpobf-ignore-line
The annotation and the declaration must be on the exact same line:
private string $apiKey; // @phpobf-ignore-line public function getToken(): string { ... } // @phpobf-ignore-line external API contract
@phpobf-ignore-next-line
The annotation must be on the line immediately before the declaration:
// @phpobf-ignore-next-line private string $apiKey; // @phpobf-ignore-next-line required by external contract public function getToken(): string { ... } /* @phpobf-ignore-next-line */ const VERSION = '1.0.0';
An optional reason can follow either annotation — it is recorded in backplate.json alongside the ignored: true flag for traceability. Both // and /* */ comment styles are supported.
Note: Ignoring a class does not automatically ignore its methods or properties. Each declaration must be annotated individually if you want to exclude multiple members.
In-code API
The obfuscator can also be used programmatically via the fluent builder API:
use Monolog\Handler\StreamHandler; use Monolog\Logger; use PhpObfuscator\Obfuscator\Obfuscator; $logger = (new Logger('phpobf'))->pushHandler(new StreamHandler('php://stdout')); $obf = new Obfuscator(outputDir: '/path/to/output'); $obf->setLogger($logger); $obf->setSource('/path/to/your/project') ->setLevel(1) ->setTestMode(false) ->setObfuscatePaths(true) ->obfuscate();
The obfuscator uses a PSR-3 LoggerInterface — any compliant logger works. When no logger is set, log output is silently discarded. The CLI entry point wires up a Monolog logger with colour-coded output automatically.
API Reference
| Method | Default | Description |
|---|---|---|
setSource(string $source) |
(required) | Path to the PHP file or directory to obfuscate. Directories are scanned recursively. |
setLevel(int $level) |
0 |
Obfuscation level (0–2). See below. |
setTestMode(bool $testMode) |
false |
When true, obfuscated names are prefixed with the original (e.g. Teacher_abc123) for easier debugging. |
setObfuscatePaths(bool $obfuscatePaths) |
true |
When true, files are relocated to match the obfuscated namespace directory structure. |
setKeepBackplateFile(bool $keep) |
false |
When true, saves backplate.json to the output directory alongside obfuscated sources. |
setLogger(LoggerInterface $logger) |
(none) | Attach a PSR-3 logger for progress and diagnostic output. |
new Obfuscator(outputDir: $dir) |
temp/{id}/ |
Custom output directory. Defaults to temp/{obfuscationId}/ relative to the library root. |
Obfuscation Levels
Levels are stacked — each level applies everything from the previous levels plus its own transformations.
Level 0 — Structural obfuscation (namespaces, classes, interfaces, enums)
- Renames all namespace declarations to hashed equivalents
- Renames all class, interface, and enum names to hashed equivalents
- Rewrites all fully-qualified references (type hints,
newexpressions, static calls) to match - Removes
usestatements in production; rewrites them to obfuscated FQCNs in test mode - Strips all comments
- Relocates files to match the obfuscated PSR-4 directory structure (when
setObfuscatePaths(true)) - Skips any class or namespace matched by the block list (e.g.
/^Psr/)
// Input namespace App\Service; use App\Model\User; // Creates a user class UserService { public function create(User $user): void {} } // Output namespace aB3kQz\xR9mLp; class fT7wNq { public function create(\aB3kQz\qP2vYm $user): void {} }
Level 1 — Method & function obfuscation (everything from Level 0, plus):
- Renames method definitions to hashed equivalents (magic methods like
__constructare always skipped) - Rewrites instance method calls (
$obj->method()) by resolving the receiver's type via PHPStan inference, AST walking for chainednewexpressions, or$thisclass reflection - Rewrites static method calls (
Foo::method()) using the fully-qualified class name - Renames standalone function definitions and their call sites
- Leaves a method call unobfuscated (with a warning) if the receiver type cannot be resolved (union types, mixed, or dynamic dispatch
$obj->$method())
// Input namespace App\Service; function helperFn(): void {} class UserService { public function create(User $user): void { $this->validate($user); } private function validate(User $user): void {} } $service = new UserService(); $service->create(new User()); helperFn(); UserService::staticMethod(); // Output (methods and functions renamed on top of Level 0 structural obfuscation) namespace aB3kQz\xR9mLp; function kQ8mZp(): void {} class fT7wNq { public function nX4vRt(\aB3kQz\qP2vYm $user): void { $this->pL6hWs($user); } private function pL6hWs(\aB3kQz\qP2vYm $user): void {} } $service = new fT7wNq(); $service->nX4vRt(new \aB3kQz\qP2vYm()); kQ8mZp(); fT7wNq::mR2jYk();
Level 2 — Variable & property obfuscation (everything from Level 1, plus):
- Renames all variables (local, global, function parameters) to hashed equivalents
- Renames class property declarations (
private string $name→private string $xR9mLp) - Renames
$this->propertyaccess expressions to match - Skips
$thisand all PHP superglobals ($_SERVER,$_GET,$_POST,$_COOKIE,$_FILES,$_SESSION,$_REQUEST,$_ENV,$GLOBALS) - Throws a
ScopeResolutionExceptionand aborts the file if a dynamic variable-variable ($$var) is encountered, as its runtime name cannot be statically determined - Property accesses on non-
$thisreceivers (e.g.$obj->name) are intentionally skipped — resolving the owner class without full type inference risks renaming unrelated properties that share the same name across different classes
Note: Because the hash is purely name-based, the same variable name always maps to the same obfuscated name everywhere it appears — across methods, closures, and arrow functions — so no per-scope tracking is required.
// Input class School { private string $name; public function __construct(string $name) { $this->name = $name; } } $hogwarts = new School("Hogwarts"); $teacher = new Teacher("Albus", "Dumbledore"); $hogwarts->addTeacher($teacher); // Output (variables and properties renamed on top of Level 1 obfuscation) class fT7wNq { private string $xR9mLp; public function __construct(string $xR9mLp) { $this->xR9mLp = $xR9mLp; } } $nXvRtQp = new fT7wNq("Hogwarts"); $kQmZpYs = new aB3kQz("Albus", "Dumbledore"); $nXvRtQp->pL6hWs($kQmZpYs);
Output
Obfuscated sources are written to the output directory. A backplate.json file containing the full original-to-obfuscated name mapping can optionally be saved alongside the output using --keep-backplate-file (CLI) or setKeepBackplateFile(true) (API). This file is useful for correlating obfuscated symbols back to their originals (e.g. for stack trace interpretation).
⚠️⚠️ Never distribute
backplate.jsonwith your obfuscated sources. ⚠️⚠️The backplate is a complete reverse-mapping of every obfuscated name back to its original. Anyone with this file can trivially deobfuscate your entire codebase. Keep it private, treat it like a secret key, and delete it from the output directory before shipping.
If the source project contains a composer.json and setObfuscatePaths(true) is used, the autoloader in the output is automatically updated to use a classmap. Run composer dump-autoload in the output directory after obfuscation:
composer dump-autoload --working-dir=/path/to/output
License
This project is licensed under the GNU Lesser General Public License v3.0 (LGPL-3.0).
You are free to use this library in any software, including commercial and closed-source products. However, any modifications to the library itself must be published under the same LGPL-3.0 license.
See the LICENSE file for the full license text.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: LGPL-3.0-only
- 更新时间: 2026-06-11