stephp-cap-std
April 28, 2026 · View on GitHub
Version 0.5.0
stephp-cap-std is an experimental PHP extension written in Rust. It provides bindings to the cap-std crate, offering a capability-based security approach for filesystem access. This project is made possible thanks to the ext-php-rs project, which provides the tools to build PHP extensions with Rust.
The primary goal is to enable robust sandboxing within PHP scripts. Unlike native PHP filesystem functions, this extension ensures that once a directory handle is obtained, it is impossible to access files outside of that directory tree (e.g., via ../../), providing strong protection against directory traversal attacks.
🤖 AI Statement
This project leverages Artificial Intelligence. This README.md file, as well as significant portions of the source code and the test suite, have been generated or refined by AI models.
Prerequisites
- Linux (or a compatible Unix system)
- Rust (via
rustup, 2024 edition as specified inCargo.toml) - PHP (version 8.0+, with development headers installed, e.g.,
php-devorphp-devel) - Clang (required for binding generation by
ext-php-rs)
Compilation and Installation
-
Clone the repository:
git clone <repository-url> cd php-cap-std -
Build the project: Use Cargo to build the dynamic library.
cargo build --releaseThe generated artifact will be located at
target/release/libstephp_cap_std.so. -
Enable the extension:
Option A: Command line (for testing)
php -d extension=target/release/libstephp_cap_std.so your_script.phpOption B: Permanent configuration Copy the
.sofile to your PHP extensions directory and add the following line to yourphp.ini:extension=libstephp_cap_std.so
Usage Example
Capability-based security means you don't use global paths. You start with an "Ambient Authority" to open a specific entry-point directory, and then work exclusively through that handle.
<?php
// 1. Obtain the ambient authority
$auth = stephp_cap_std_ambient_authority();
// 2. Open a root directory for the sandbox
try {
$dir = stephp_cap_std_open_ambient_dir($auth, '/tmp/sandbox');
// Write a file with specific options
$options = new StephpCapStdOpenOptions();
$options->write(true);
$options->create(true);
$options->truncate(true);
$file = $dir->open_with("data.log", $options);
$file->write("Log entry at " . date('Y-m-d H:i:s') . "\n");
$file->flush();
// Operations between directories (Capability approach)
$backupDir = $dir->open_dir("backups");
$dir->copy("data.log", $backupDir, "data.log.bak");
// Metadata and Time
$meta = $dir->metadata("data.log");
$mtime = $meta->modified();
echo "Last modified: " . $mtime->to_unix_timestamp_seconds_utc() . "\n";
// Safe listing
foreach ($dir->read_dir(".") as $name) {
echo "Found: $name\n";
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
Available API
As this extension provides direct bindings to the Rust cap-std library, it is highly recommended to consult its official documentation to understand the detailed behavior of each method and their respective parameters.
Global Functions
stephp_cap_std_ambient_authority(): StephpCapStdAmbientAuthoritystephp_cap_std_open_ambient_dir(StephpCapStdAmbientAuthority $auth, string $path): StephpCapStdDir
StephpCapStdDir (Directory Handle)
Main entry point for scoped filesystem operations.
- Files:
open(string $path),open_with(string $path, StephpCapStdOpenOptions $opts),create(string $path),read(string $path),read_to_string(string $path),write(string $path, string|Binary $data),remove_file(string $path) - Directories:
open_dir(string $path),create_dir(string $path),create_dir_all(string $path),remove_dir(string $path),remove_dir_all(string $path),read_dir(string $path): StephpCapStdEntries - Inter-Directory:
copy(string $from, StephpCapStdDir $to_dir, string $to),rename(string $from, StephpCapStdDir $to_dir, string $to),hard_link(string $src, StephpCapStdDir $dst_dir, string $dst) - Metadata/Info:
exists(string $path),is_file(string $path),is_dir(string $path),metadata(string $path),symlink_metadata(string $path),dir_metadata(),canonicalize(string $path),read_link(string $path) - System:
set_permissions(string $path, StephpCapStdPermissions $p),symlink(string $orig, string $link)
StephpCapStdFile (File Handle)
- IO:
read(int $len),read_to_end(),read_to_string(),write(string $data),flush() - Position:
seek(int $offset, int $whence),seek_relative(int $offset),rewind(),stream_position(),stream_len() - Management:
sync_all(),sync_data(),set_len(int $size),metadata(),set_permissions(StephpCapStdPermissions $p)
StephpCapStdOpenOptions
read(bool),write(bool),append(bool),truncate(bool),create(bool),create_new(bool)
StephpCapStdMetadata
is_dir(),is_file(),is_symlink(),len(),size(),permissions()modified(),accessed(),created()(ReturnsStephpCapStdSystemTime)- Linux/Unix specific:
dev(),ino(),mode(),uid(),gid(),nlink(),blksize(),blocks(),atime(),mtime(),ctime()
StephpCapStdEntries
Implements Iterator and Countable. Returned by read_dir().
count(),rewind(),current(),key(),next(),valid()
Supporting Classes
StephpCapStdPermissions:readonly(),set_readonly(bool),mode(),set_mode(int)StephpCapStdSystemTime:to_unix_timestamp_seconds_utc()StephpCapStdFileType:is_dir(),is_file(),is_symlink()
🧪 Testing
The project includes a comprehensive set of tests located in the php/ directory. These tests serve as both verification and usage examples.
To run the tests:
cd php/
php -d extension=../target/release/libstephp_cap_std.so test.php
Limitations and Warnings
⚠️ PHP Stat Cache (clearstatcache)
This extension bypasses PHP's standard streams. PHP's internal cache for functions like file_exists() might not reflect changes made via this extension.
Solution: Use the methods on the $dir object (e.g., $dir->exists()) or call clearstatcache() in PHP.
Performance
FFI boundary crossings between PHP and Rust have a minor overhead. For security-critical sandboxing, this is a negligible trade-off.
License
This project is licensed under the MIT License.