Skip to content

[PHP] Add FileIndexDiffProcessor to share file-index comparison logic between push and pull - #541

Merged
adamziel merged 10 commits into
trunkfrom
codex/file-index-diff-processor
Aug 11, 2026
Merged

[PHP] Add FileIndexDiffProcessor to share file-index comparison logic between push and pull#541
adamziel merged 10 commits into
trunkfrom
codex/file-index-diff-processor

Conversation

@adamziel

@adamziel adamziel commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Adds FileIndexDiffProcessor, a single-pass, resumable traversal over an old and new path-sorted filesystem index. It labels each current path as added, modified, deleted, or unchanged, so callers choose an operation without rebuilding the index comparison. PushPlan now uses the same processor.

Usage

Create the comparison and select its first path:

$index_diff = FileIndexDiffProcessor::create(
    $old_index_file,
    $new_index_file
);
$has_path = $index_diff->next_path();

get_path_transition() describes the difference between the old and new index records. A path present in both indexes is modified when its type, size, or ctime differs:

while ($has_path) {
    $path = $index_diff->get_path();

    switch ($index_diff->get_path_transition()) {
        case "added":
            handle_added_path($path);
            break;
        case "modified":
            handle_modified_path($path);
            break;
        case "deleted":
            handle_deleted_path($path);
            break;
        case "unchanged":
            break;
    }

    $has_path = $index_diff->next_path();
    save_cursor($index_diff->get_cursor());
}

$index_diff->close();

The per-index getters expose record details when an operation needs them:

if ($index_diff->get_path_transition() === "modified") {
    compare_sizes(
        $index_diff->get_size_in_old_index(),
        $index_diff->get_size_in_new_index()
    );
}

When a path is absent from an index, the neighboring-path getters describe the position where it would occur. For a path deleted from the new index:

if ($index_diff->get_path_transition() === "deleted") {
    $preceding_path = $index_diff->get_preceding_path_in_new_index();
    $following_path = $index_diff->get_following_path_in_new_index();
}

For a path added to the new index, inspect the following path in the old index:

if ($index_diff->get_path_transition() === "added") {
    $following_path = $index_diff->get_following_path_in_old_index();
}

A following-path getter requires the current path to be absent from that index. Otherwise the processor has not read the entry after the current path and rejects the call.

Resume from the cursor stored after next_path() advances past a processed path:

$index_diff = FileIndexDiffProcessor::resume(
    $old_index_file,
    $new_index_file,
    $stored_cursor
);

A selected path is not part of the cursor until the following next_path() call advances past it. Closing before that call leaves the path available for replay after resume. Both index files must remain unchanged while a cursor may be resumed.

The processor holds at most one unread entry from each index. Its dedicated tests cover all four transition labels, decoded-path ordering, preceding and following paths, cursor movement, resume, missing indexes, EOF, and close.

#468 follows this PR in the stack.

Testing

cd tests
../vendor/bin/phpunit Import/FileIndexDiffProcessorTest.php Import/PushPlanTest.php
../vendor/bin/phpcs ../packages/reprint-client/src/lib/index/class-file-index-diff-processor.php Import/FileIndexDiffProcessorTest.php
cd ..
vendor/bin/phpstan analyze --memory-limit=1G
git diff --check

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Pull pipeline performance — large-directory

Site: large-directory · 2,000+ plus targeted file-transfer scenarios files · 10,000 posts · 25,000 postmeta · PHP 8.5.9

Stage PR trunk Δ Status Details
playground-sqlite-db-pull 9.36 s 9.29 s ⚪ +68 ms (+0.7%) condition=db-pull in PHP.wasm
runtime=php.wasm 8.3
wp_mysql_parser=enabled
mode=lexer
native_lexer=verified
native_token_stream=WP_MySQL_Native_Token_Stream
native_token_count=18
native_parser=selected
trunk: condition=db-pull in PHP.wasm
runtime=php.wasm 8.3
wp_mysql_parser=enabled
mode=lexer
native_lexer=verified
native_token_stream=WP_MySQL_Native_Token_Stream
native_token_count=18
native_parser=selected
playground-sqlite-db-apply 3.86 s 3.58 s ⚪ +274 ms (+7.7%) condition=db-apply to SQLite in PHP.wasm
runtime=php.wasm 8.3
wp_mysql_parser=enabled
mode=parser
native_lexer=verified
native_token_stream=WP_MySQL_Native_Token_Stream
native_token_count=18
native_parser=verified
native_ast=WP_MySQL_Native_Parser_Node
sqlite_driver_parser=verified
trunk: condition=db-apply to SQLite in PHP.wasm
runtime=php.wasm 8.3
wp_mysql_parser=enabled
mode=parser
native_lexer=verified
native_token_stream=WP_MySQL_Native_Token_Stream
native_token_count=18
native_parser=verified
native_ast=WP_MySQL_Native_Parser_Node
sqlite_driver_parser=verified
Total 13.22 s 12.87 s ⚪ +342 ms (+2.7%)

Numbers carry runner noise; treat single-run deltas as directional, not authoritative.

📈 Trunk performance history — commit-by-commit timeline.

@adamziel adamziel changed the title [PHP] Share sorted file-index comparison between push and pull [PHP] Add FileIndexDiffProcessor to share file-index comparison logic between push and pull Aug 10, 2026
@adamziel
adamziel merged commit 0e57e19 into trunk Aug 11, 2026
21 checks passed
@adamziel
adamziel deleted the codex/file-index-diff-processor branch August 11, 2026 00:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant