Skip to main content

PORIFERA: PHP code rewriting instrumentation for expression in AST

Project description

PORIFERA: Php cOde Rewriting Instrumentation For ExpRession in Ast

Porifera (Sponges) are the oldest porous filter-feeding organisms in the ocean. Sponges draw in seawater through micro-pores throughout their bodies, filter it, and capture nutrients. PORIFERA uses this as a metaphor: Like the pores of a sponge, it permeates each expression in the code, silently absorbing runtime values without altering the behavior of the program itself.

PHP AST instrumentation library. Injects runtime probe wrappers around target PHP expressions to capture their values, then restores the original code.

Requires Python >=3.11,<3.13.

Installation

pip install porifera

Quick Start

from php_parser_py import Parser
from porifera import instrument, deinstrument

# Parse PHP project
ast = Parser().parse_file("path/to/project/index.php")

# Define targets: AST node_id -> expr_key label
targets = {
    "node_42": "user_query",
    "node_87": "config_value",
}

# Instrument — wraps targets with probe calls
modified_files = instrument(targets, ast)

# Optionally specify output directory for probe logs
# modified_files = instrument(targets, ast, output_dir=Path("/tmp/logs"))

# Run PHP project... probes log to .porifera_data_<timestamp>.jsonl

# Deinstrument — restores original code
restored_files = deinstrument(ast)

After instrumentation, a PHP expression like:

$row = $db->query($sql);

becomes:

$row = __porifera_probe_a1b2c3d4("user_query", $db->query($sql));

The probe returns the original value transparently and logs it to .porifera_data_<timestamp>.jsonl. Each instrumentation run produces a unique timestamped file to avoid overwriting previous data.

Examples

Log all array access values at runtime

Probe every $config['db_host'], $row['name'], etc.:

from php_parser_py import Parser
from porifera import instrument

ast = Parser().parse_file("app/config.php")

# All Expr_ArrayDimFetch nodes -> targets
fetches = ast.nodes(lambda n: n.node_type == "Expr_ArrayDimFetch")
targets = {n.id: f"array_{n.id[:8]}" for n in fetches}

instrument(targets, ast)

Log all function call return values

Probe every function call:

from php_parser_py import Parser
from porifera import instrument

ast = Parser().parse_file("app/service.php")

# All Expr_FuncCall nodes -> targets
calls = ast.nodes(lambda n: n.node_type == "Expr_FuncCall")
targets = {n.id: f"call_{n.id[:8]}" for n in calls}

instrument(targets, ast)

Log specific function call return values

Probe only mysqli_query(...) calls:

from php_parser_py import Parser
from porifera import instrument

ast = Parser().parse_file("app/db.php")

# All Expr_FuncCall nodes
calls = ast.nodes(lambda n: n.node_type == "Expr_FuncCall")

# Filter by function name
targets = {}
for call in calls:
    name_node = next(ast.succ(call, lambda e: e.get("field") == "name"), None)
    if not name_node or name_node.get_property("parts") != ["mysqli_query"]: continue
    targets[call.id] = f"query_{call.id[:8]}"

instrument(targets, ast)

Broader coverage with ElevatingProbeStrategy

StandardProbeStrategy skips lvalue targets (e.g. $i in $i++). ElevatingProbeStrategy wraps the nearest safe ancestor instead:

from porifera import instrument, ElevatingProbeStrategy

instrument(targets, ast, strategy=ElevatingProbeStrategy())

Documentation

  • Design Specification — API signatures, class responsibilities, strategies, exceptions, and validation rules
  • Implementation Notes — php-parser-py API reference, AST node types, unsafe wrap contexts, and developer instructions
  • AST Structure - the structure of the php-parser-py AST, node types, and properties, and how to navigate it.

Development

# Install dependencies
uv sync

# Run tests
uv run pytest

# Type checking
uv run mypy src/

# Linting
uv run pylint src/

# Formatting
uv run black src/ tests/
uv run isort src/ tests/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

porifera-0.1.0.tar.gz (11.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

porifera-0.1.0-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file porifera-0.1.0.tar.gz.

File metadata

  • Download URL: porifera-0.1.0.tar.gz
  • Upload date:
  • Size: 11.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.12

File hashes

Hashes for porifera-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0bb3c8ac6e51d8328e1713b7048fee730ec496a711ff600c2983d4c437eb302e
MD5 b8fb0c055a7ff6359a0c0e2aebdee0b2
BLAKE2b-256 b0d80752608d0ed8d066efd25e6f8bb6506ec86b6ef6d9d7247abcd96579f2e0

See more details on using hashes here.

File details

Details for the file porifera-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: porifera-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.12

File hashes

Hashes for porifera-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8e93182fe53e9c0a6688e222721c725e77acda0c78e7907e92325ec6c3b81f7c
MD5 c710cb4f22527bac25da53f4a977a2da
BLAKE2b-256 38e5d19479efa3891335701f2d465d89acd8e1f8cef9593cdf223e3cba504390

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page