Developer API

For plugin developers: start CompatNav scans in the background, read the report and its findings, check code and plugin zips, and react when a scan finishes. CompatNav 1.1.0 and newer.

CompatNav 1.1.0 adds a small PHP API for add-ons: plain functions and actions that other plugins call on the same server. There are no HTTP endpoints, and nothing leaves the site. This page is for developers; site owners don’t need it.

CompatNav 1.2.0 brings API 1.1: check a piece of code in memory, scan a plugin or theme zip without unpacking it, list the report’s files, and a few smaller additions. They are marked (1.1) and described in Added in API 1.1.

Quick start

A small add-on that runs a full scan in WP-Cron and keeps the verdict. Schedule the my_addon_scan event the way you like (for example with wp_schedule_event(), weekly):

my-addon.php

<?php
// Scans the site in WP-Cron and keeps the verdict. Needs CompatNav 1.1.0 or newer.
add_action( 'my_addon_scan', function () {
	if ( ! function_exists( 'compatnav_start_scan' ) ) {
		return; // CompatNav is missing or older than 1.1.0.
	}
	$state = compatnav_get_scan_state();
	if ( ! $state['running'] ) {
		$state = compatnav_start_scan( array( 'initiator' => 'scheduled' ) );
	}
	$stop = microtime( true ) + 15; // A step takes up to 8 s: stay inside a 30 s limit.
	while ( ! is_wp_error( $state ) && $state['running'] && microtime( true ) < $stop ) {
		$state = compatnav_run_scan_step(); // One batch of files per call.
	}
	if ( ! is_wp_error( $state ) && $state['running'] ) {
		wp_schedule_single_event( time(), 'my_addon_scan' ); // Go on in the next WP-Cron run.
	}
} );
add_action( 'compatnav_scan_completed', function ( $scan_id, $summary ) {
	update_option( 'my_addon_verdict', $summary['verdict']['text'], false ); // Store only.
}, 10, 2 );

Output · WordPress 7.1.2, CompatNav 1.1.0, PHP 8.2.29 (WP-CLI)

$ wp cron event run my_addon_scan
Executed the cron event 'my_addon_scan' in 16.561s.
Success: Executed a total of 1 cron event.
$ wp cron event run my_addon_scan
Executed the cron event 'my_addon_scan' in 15.095s.
Success: Executed a total of 1 cron event.
$ wp cron event run my_addon_scan
Executed the cron event 'my_addon_scan' in 16.609s.
Success: Executed a total of 1 cron event.
$ wp cron event run my_addon_scan
Executed the cron event 'my_addon_scan' in 15.323s.
Success: Executed a total of 1 cron event.
$ wp cron event run my_addon_scan
Executed the cron event 'my_addon_scan' in 13.564s.
Success: Executed a total of 1 cron event.
$ wp option get my_addon_verdict
Safe to upgrade to PHP 8.5: nothing active will break

This is the real output: the example was loaded on our test site and its event was run with WP-CLI until it stopped rescheduling itself. Each run starts steps for up to 15 seconds, continues in the next WP-Cron run, and the listener stores the verdict when the scan completes. In a normal WP-Cron run the same code runs inside the cron request; WP-CLI only makes the runs visible.

Ground rules

  • Server-side only. Functions and actions in PHP; no HTTP endpoints, no external requests.
  • No scan work while a page is rendered. compatnav_start_scan() (it reads the headers of the installed plugins and themes) and compatnav_run_scan_step() (it lists and parses files) work only in WP-Cron, WP-CLI and AJAX requests. Anywhere else they return the error wrong_context. The same goes for compatnav_check_code() and package scans (1.1). Every other function only reads or deletes stored data.
  • One request at a time. Functions that change the scan take the same lock as CompatNav’s own screen. While another request holds it, they return busy and change nothing: try again later. The lock expires after 60 seconds if a request dies. Reading functions never take it.
  • One scan at a time, one report. A new scan replaces the report only when it has finished. Cancelling it leaves the last report untouched.
  • Crash-safe. A step works within a budget: up to 200 files, 8 seconds (or a third of PHP’s time limit when that is shorter) and 70% of the memory limit. If PHP dies while reading a file, that file is recorded as not checked and the next step continues after it. If an exception is thrown during a step, you get unexpected_error; the file is recorded the same way, and the next call, even in the same request, continues.
  • No permission checks inside. WP-Cron and WP-CLI have no logged-in user. If you call the API for a visitor (an admin page, an AJAX handler), check compatnav_current_user_can_manage() and a nonce yourself, as CompatNav does.
  • No server paths, no code. Files are given as the report shows them (inside wp-content), and findings carry a fingerprint (a hash), not the code line.

Checking for the API

if ( function_exists( 'compatnav_api_version' ) && version_compare( compatnav_api_version(), '1.0', '>=' ) ) {
	// The API is there.
}

For the additions of API 1.1, check for '1.1' and keep a 1.0 path for sites that still run CompatNav 1.1.0:

$has_1_1 = function_exists( 'compatnav_api_version' ) && version_compare( compatnav_api_version(), '1.1', '>=' );

The action compatnav_loaded fires once per request, on plugins_loaded, with the API version.

Which PHP version a scan compares against

A scan reports what changes between the PHP version the website runs and the target version. From the command line that isn’t always the PHP running the code: WP-CLI and real server cron jobs can use a different PHP binary than the website. So CompatNav remembers the version it sees when its admin screen loads or its screen scans, with the date it was last seen (at most one write a day).

compatnav_start_scan() chooses, in this order:

  1. The current_version argument, if you pass one (source argument).
  2. In a web request (WP-Cron over HTTP, AJAX): the PHP running the request (source running_php).
  3. On the command line: the remembered website version (source stored), returned with its date so you can decide whether it is recent enough.
  4. On the command line with nothing remembered and no argument: the scan does not start, error site_php_version_unknown. Open Tools → CompatNav once in wp-admin, or pass current_version.

Functions

compatnav_api_version(): string

The API version: '1.0' for CompatNav 1.1.0, '1.1' for CompatNav 1.2.0.

compatnav_current_user_can_manage(): bool

Whether the current user may use CompatNav, with the screen’s rule: manage_options, or on multisite only super admins.

compatnav_get_site_php_version(): ?array

The remembered website PHP version, or null if it was never seen: [ 'version' => '8.5', 'full' => '8.5.11', 'seen_at' => 1790000000 ] (Unix time).

compatnav_start_scan( array $args = [] ): array|WP_Error

Creates a full scan of all installed plugins and themes, like the screen’s “Start new scan”. It reads the plugins’ and themes’ headers and saves the scan; the first step lists and parses the files.

ArgumentTypeDefaultNotes
targetstringthe screen’s saved target8.0 … 8.5. Doesn’t change the saved setting.
skip_bundledboolthe screen’s saved settingSkip bundled libraries. Doesn’t change the saved setting.
current_versionstringsee above7.4, 8.0 … 8.5.
initiatorstringapischeduled, wp-cli or api. Shown on the report as “Started by: …”.
caller (1.1)stringnoneYour plugin’s name, 1–50 characters: “Started by: scheduled scan (My Add-on)”.

Unknown keys and wrong types are refused; a key set to null counts as not given. Returns the new scan’s state (below). Errors: invalid_argument, invalid_target, invalid_version, invalid_initiator, wrong_context (all before the lock), busy, scan_running, site_php_version_unknown, unexpected_error. Fires compatnav_scan_started.

compatnav_run_scan_step(): array|WP_Error

Does one batch of scan work and returns the scan state. Call it again while running is true and your request has time left. With nothing to do (no scan, or the scan is paused or finished), it returns the state without an error. Errors: invalid_argument (it takes no arguments yet), wrong_context, scan_running, busy, unexpected_error. Fires compatnav_scan_completed when this step finishes the scan.

compatnav_get_scan_state(): array

The current or last scan, always with the same keys (with no scan yet, status is idle and the other keys hold empty values). The main ones:

KeyMeaning
statusidle, collecting, parsing, paused, completed or cancelled
runningcollecting or parsing
stalledrunning, but no step for more than stalled_after_seconds (60)
last_step_at, seconds_since_last_stepwhen a step (or start, pause, resume, cancel) last updated it
scan_id, initiator, callerthe scan and who started it (screen, scheduled, wp-cli, api); caller (1.1) is the name passed at the start, or ''
scopetype: full, or partial (a re-scan of one plugin or theme from the screen), and sources
phase, percent, processed, countersprogress; counters: files, parsed, not_parsable, crashed, skipped, not_checked
current_version, target_version, current_version_source, site_php, skip_bundledthe versions compared and where the current one came from
contexthow the scan runs: initiator, sapi, memory_limit, max_execution_time, limits_varied, current_version_source, site_php (details below)
started_at, finished_at, active_secondstimes
last_reportscan_id, finished_at and versions of the last completed report, or null

compatnav_cancel_scan(): array|WP_Error

Cancels a running or paused scan and deletes its rows; the last report stays. With nothing to cancel it returns the state. Errors: scan_running (from a listener), busy, unexpected_error. Fires compatnav_scan_cancelled.

compatnav_get_report_summary(): ?array

The last completed report, with the same verdict and counts as the screen, or null. Ignored findings are left out of all counts, and “active” is judged now, as on the screen.

KeyMeaning
verdictcode: safe, not_ready or not_fully_checked; text: the sentence the screen shows. safe only when nothing active will break and every active file was checked.
active, inactivecounts of places that will break (errors) and deprecation notices (warnings)
filestotal, checked, not_checked, not_checked_active
sourcesone entry per plugin or theme with findings or unchecked files: id, type, slug, name, version_scanned, version_now, installed, active, status (update_available, no_update, not_wporg, custom, unknown, removed), new_version, counts
coveragethe files that could not be checked, with a reason code (at most 100, as on the screen; all of them through compatnav_get_files() (1.1)), and the files skipped on purpose. Each item’s file is the path inside the plugin or theme folder; (1.1) path is the same file inside wp-content, as in findings
scope, full_scan, rescannedwhat changed the report last, the full scan it is based on, and plugins re-scanned since
ignored, updates_checked_at, context, versions, times, legacyas named

compatnav_get_findings( array $args = [] ): array|WP_Error

The findings of the last report, in chunks, never all at once.

ArgumentDefaultNotes
afternonethe previous chunk’s next_after (a string like "57.4.12345"); leave it out for the first chunk
limit1001–500
sourceallone plugin or theme ID from the summary, e.g. plugin:akismet/akismet.php
severityallerror or warning
activeallall, active or inactive
ignoredexcludeexclude (like the report), include or only
scan_id (1.1)the reporta completed package scan instead of the report (active and ignored don’t apply)

It returns scan_id, items and next_after (null after the last chunk). Each item has id, source_id, source_type, source_active, file, line, rule_id, php_version, severity, confidence, message, more_count (further places of the same check in the file), bundled, fingerprint and ignored. The fingerprint identifies a finding across scans, even when its line number changes.

Paging never mixes two reports. The cursor carries the report and its revision. If a scan finishes between two chunks (or a re-scan is merged into the report), the next call returns report_changed: start again from the first chunk. Within one report, you get every finding once, in ID order, without gaps.

Counting the places per check in the active plugins and themes of our test site.

findings-by-rule.php

<?php
// Counts the findings of the active plugins and themes per check, in chunks of 500.
$counts = array();
$after  = null; // No cursor for the first chunk.
do {
	$chunk = compatnav_get_findings( array( 'active' => 'active', 'limit' => 500, 'after' => $after ) );
	if ( is_wp_error( $chunk ) ) {
		echo $chunk->get_error_code(), "\n"; // 'report_changed': a scan finished; start again later.
		return;
	}
	foreach ( $chunk['items'] as $finding ) {
		$counts[ $finding['rule_id'] ] = ( $counts[ $finding['rule_id'] ] ?? 0 ) + 1 + $finding['more_count'];
	}
	$after = $chunk['next_after'];
} while ( null !== $after );
arsort( $counts );
foreach ( $counts as $rule_id => $places ) {
	echo str_pad( $rule_id, 36 ), $places, "\n";
}

Output · WordPress 7.1.2, CompatNav 1.1.0, PHP 8.2.29 (WP-CLI)

php84.implicitly_nullable           9
php84.trigger_error_user_error      4
php84.csv_escape_default            4
php82.relative_callable             3
php80.libxml_disable_entity_loader  2
php85.reflection_set_accessible     2
php84.deprecated_function           1
php85.deprecated_function           1
php82.dynamic_property              1
php82.utf8_encode_decode            1

compatnav_get_rules(): array

Every check, keyed by its rule ID: php_version, severity, explanation (the plain-language text the report shows), fix_before and fix_after. It includes syntax_incompatible, the finding for code the target version can’t read at all, so every rule_id a finding can carry is listed (CompatNav 1.1.0 and 1.2.0: 58 checks plus syntax_incompatible).

Added in API 1.1

CompatNav 1.2.0. Nothing of 1.0 changes: these are new functions, new optional arguments and new keys.

compatnav_get_supported_targets(): array

The target versions a scan accepts, the newest, and the screen’s saved target: [ 'versions' => [ '8.0', '8.1', '8.2', '8.3', '8.4', '8.5' ], 'newest' => '8.5', 'saved' => '8.5' ].

compatnav_get_report_url(): string

The address of the report screen (Tools → CompatNav) for the current site, for links in emails and on other screens. It checks no permission: the screen does that when it is opened.

compatnav_get_files( array $args = [] ): array|WP_Error

Every file of the last report, in chunks, with the same cursor and the same guarantee as compatnav_get_findings() (report_changed when the report changed). Unlike the summary’s coverage list, it has no limit of 100, so you can compare what two scans checked exactly.

ArgumentDefaultNotes
afternonethe previous chunk’s next_after
limit1001–500
sourceallone plugin or theme ID
statusallchecked, not_checked (the screen’s “could not be checked”) or skipped (skipped on purpose: test folders, bundled libraries when the setting says so, test files inside bundled libraries)
activeallall, active or inactive

Each item has id, source_id, source_type, source_active, file (inside wp-content, as in findings), status, reason (a code, '' when checked), reason_text, line, bundled, size (bytes) and mtime (Unix time).

compatnav_check_code( string $code, array $args = [] ): array|WP_Error

Checks the code of one file in memory, with the same rules as a scan, and stores nothing. Use it to verify a change: check the old and the new code the same way and compare. Arguments: target and current_version, as for compatnav_start_scan() (on the command line, the remembered website version, or site_php_version_unknown).

It returns status (checked, or why the code couldn’t be checked: not_parsable, too_complex, too_large over 2 MB, crashed), reason_text, line (of a parse error, when known), current_version, target_version and findings (each with line, rule_id, php_version, severity, confidence, message, more_count).

It is a one-file check: what other files declare (a polyfill, a parent class, a namespace’s own functions) isn’t known, so a scan of the whole plugin can report less. Like a scan step, it works only in WP-Cron, WP-CLI and AJAX requests (it loads the parser): wrong_context elsewhere.

The same function before and after replacing each(), checked for an upgrade from PHP 7.4 to 8.5.

check-code.php

<?php
// Checks a piece of code in memory, before and after a change: nothing is stored, no file is read.
$before = <<<'CODE'
<?php
function acme_list( array $items ) {
	while ( list( $key, $value ) = each( $items ) ) {
		echo $key . ': ' . $value . "\n";
	}
}
CODE;
$after  = <<<'CODE'
<?php
function acme_list( array $items ) {
	foreach ( $items as $key => $value ) {
		echo $key . ': ' . $value . "\n";
	}
}
CODE;

foreach ( array( 'before' => $before, 'after' => $after ) as $name => $code ) {
	$result = compatnav_check_code( $code, array( 'current_version' => '7.4', 'target' => '8.5' ) );
	if ( is_wp_error( $result ) ) {
		echo $name, ': ', $result->get_error_code(), "\n";
		continue;
	}
	printf( "%s: %s, PHP %s -> %s, %d finding(s)\n", $name, $result['status'], $result['current_version'], $result['target_version'], count( $result['findings'] ) );
	foreach ( $result['findings'] as $finding ) {
		printf( "  line %d  %s  %s  %s\n", $finding['line'], $finding['rule_id'], $finding['severity'], $finding['message'] );
	}
}

Output · WordPress 7.1.2, CompatNav 1.2.0, PHP 8.3.35 (WP-CLI)

before: checked, PHP 7.4 -> 8.5, 1 finding(s)
  line 3  php80.removed_function  error  each() was removed in PHP 8.0. Use foreach instead.
after: checked, PHP 7.4 -> 8.5, 0 finding(s)

Package scans

Scans a local zip file of a plugin or theme that isn’t installed, with the same engine, rules and versions as a normal scan, in steps. Nothing is extracted: PHP’s zip extension reads the PHP files one at a time, in memory; no code runs. The results are kept under their own scan ID and never change the report.

FunctionReturns
compatnav_start_package_scan( string $zip_path, array $args = [] )the package scan’s state
compatnav_run_package_scan_step()one batch of work; the state
compatnav_get_package_scan( int $scan_id )its state and, once completed, its summary; null if unknown
compatnav_get_package_scans()the kept package scans, newest first: scan_id, label, status, finished_at
compatnav_cancel_package_scan()stops the running package scan and deletes its rows
compatnav_delete_package_scan( int $scan_id )deletes a kept package scan: true
compatnav_get_findings( [ 'scan_id' => $id ] )its findings, once completed; file is the path inside the zip

Starting. Arguments: target, current_version, skip_bundled and caller as for compatnav_start_scan(), plus label (up to 100 characters, e.g. “Acme Booking 2.4.0”). The path must be a local file: a URL is refused. Before anything is stored, the whole zip is refused when:

  • PHP’s zip extension is missing: zip_unavailable. There is no fallback that unpacks files.
  • It isn’t a readable zip file: invalid_package.
  • An entry is unsafe (a .. folder, an absolute path, a drive letter, a backslash, a control character, a symbolic link), or the zip is too large (more than 20,000 entries, more than 200 MB unpacked, or an entry that would expand more than 100 times to over 1 MB): unsafe_package, with the reason and the entry in the error’s data.

Only .php, .inc and .phtml entries are read, with a normal scan’s rules (tests and node_modules folders skipped, bundled libraries recognised). An entry over 2 MB is listed as not checked.

Steps follow the same rules as scan steps (budget, lock, crash safety, WP-Cron, WP-CLI and AJAX only). One package scan runs at a time (package_scan_running), and the report goes first: while a normal scan is unfinished, a package step does nothing and returns scan_running; a normal scan can always start. The zip is checked at every step: if it was changed or removed, the package scan is cancelled and the step returns package_changed.

Keeping. Completed package scans are kept until you delete them; only the 20 newest are kept. Normal scans never delete them; uninstalling CompatNav does. The zip stays yours: CompatNav never moves or deletes it.

A zip of the fictional Acme Booking plugin (two PHP files), scanned from WP-CLI without installing it.

package-scan.php

<?php
// Scans a plugin zip that is not installed: read in memory, never extracted, kept apart from the report.
// $zip is the path of a local zip file (WP-Cron, WP-CLI or AJAX only; here: WP-CLI).
$state = compatnav_start_package_scan(
	$zip,
	array(
		'label'  => 'Acme Booking 2.3.0',
		'caller' => 'My Add-on',
	)
);
while ( ! is_wp_error( $state ) && $state['running'] ) {
	$step = compatnav_run_package_scan_step();
	if ( is_wp_error( $step ) && 'busy' === $step->get_error_code() ) {
		sleep( 2 ); // Another request holds the scan lock for a moment: try again.
		continue;
	}
	$state = $step;
}
if ( is_wp_error( $state ) ) {
	echo $state->get_error_code(), ': ', $state->get_error_message(), "\n"; // e.g. unsafe_package
	return;
}

$scan = compatnav_get_package_scan( $state['scan_id'] );
printf(
	"%s (%s): %s, PHP files: %d, will break: %d, notices: %d, PHP %s -> %s\n",
	$scan['label'],
	$scan['source']['name'],
	$scan['status'],
	$scan['files']['total'],
	$scan['errors'],
	$scan['warnings'],
	$scan['current_version'],
	$scan['target_version']
);
$findings = compatnav_get_findings( array( 'scan_id' => $scan['scan_id'], 'limit' => 500 ) );
foreach ( $findings['items'] as $finding ) {
	printf( "  %s:%d  %s  %s\n", $finding['file'], $finding['line'], $finding['rule_id'], $finding['severity'] );
}

compatnav_delete_package_scan( $scan['scan_id'] ); // Or keep it: the 20 newest are kept.

Output · WordPress 7.1.2, CompatNav 1.2.0, PHP 8.3.35 (WP-CLI)

Acme Booking 2.3.0 (Acme Booking): completed, PHP files: 2, will break: 1, notices: 1, PHP 7.4 -> 8.5
  acme-booking/legacy-hooks.php:4  php80.removed_function  error
  acme-booking/legacy-hooks.php:8  php82.utf8_encode_decode  warning

Tabs on the CompatNav screen

Your plugin can add a tab to the CompatNav screen that links to its own admin page:

add_filter( 'compatnav_screen_tabs', function ( array $tabs ) {
	$tabs['my-addon'] = array( 'label' => 'My Add-on', 'url' => admin_url( 'tools.php?page=my-addon' ) );
	return $tabs;
} );

The screen shows a tab bar only when a tab was added. It keeps at most 5 added tabs, shortens labels to 40 characters and accepts only this site’s admin addresses. On your own page, call compatnav_render_screen_tabs( 'my-addon' ) to show the same bar with your tab selected.

Smaller additions

  • caller when starting a scan (see compatnav_start_scan()), shown in “Started by”, in the scan state, the report summary and the actions’ $context.
  • While a scan that wasn’t started on the screen runs, the screen’s progress box says who started it, e.g. “Scheduled scan (My Add-on) in progress”.
  • Coverage items carry path, the findings’ path format.

Errors

CodeMeaning
invalid_argumentan unknown key, a wrong type, a malformed cursor, a limit out of range, arguments to the step
invalid_targetnot one of 8.0 … 8.5
invalid_versioncurrent_version not one of 7.4, 8.0 … 8.5
invalid_initiatornot scheduled, wp-cli or api
wrong_contextstart or step outside WP-Cron, WP-CLI and AJAX (also compatnav_check_code() and package scans, 1.1)
busyanother request holds the scan lock; try again later
scan_runninga scan is already running or paused, or the call comes from a listener of a scan action; (1.1) a package step or start while a normal scan is unfinished
site_php_version_unknowncommand line, no remembered website version, no current_version
unexpected_erroran exception during the operation; the file being read is recorded as not checked, and the next call continues
report_changedthe report changed since your cursor (or the package scan was deleted); start again from the first chunk
zip_unavailable (1.1)PHP’s zip extension is missing on this server
invalid_package (1.1)not a readable local zip file
unsafe_package (1.1)an unsafe entry or a too-large zip; the error data has reason and entry
package_scan_running (1.1)another package scan is unfinished, or you tried to delete the running one (cancel it instead)
no_package_scan (1.1)a package step with no package scan to continue
package_changed (1.1)the zip changed or disappeared during the package scan, which was cancelled
scan_not_completed (1.1)findings of a package scan that hasn’t completed yet
unknown_package_scan (1.1)deleting a package scan that isn’t kept

The codes are stable. The messages are for people, may change, and come in the site’s language.

Actions

ActionArgumentsWhen
compatnav_loadedstring $api_versiononce per request, on plugins_loaded
compatnav_scan_startedint $scan_id, array $contextevery scan start, including the screen’s
compatnav_scan_completedint $scan_id, array $summary, array $contextwhen a scan has finished and its report is stored; $summary is the report summary at that moment
compatnav_scan_cancelledint $scan_id, array $contextafter a cancel
compatnav_package_scan_completed (1.1)int $scan_id, array $summarywhen a package scan has finished; $summary as compatnav_get_package_scan()
compatnav_package_scan_cancelled (1.1)int $scan_idafter a package scan was cancelled (also after package_changed)

$context has scope, initiator, caller (1.1), target_version, current_version, current_version_source, site_php, skip_bundled and context.

Rules for listeners. The actions run inside the scan request, after its work is saved, while the scan lock is still held. So a listener must be fast, must not start, step or cancel scans (those calls return scan_running), and must not send emails or call remote services. Store what you need and do the rest in a separate request, for example wp_schedule_single_event( time(), 'my_addon_after_scan', array( $scan_id ) ).

A listener that throws can’t stop the scan: CompatNav catches it, writes one line per action and request to the PHP error log, in the form CompatNav: a listener of <action> failed and was skipped: <exception class>: <message>, and the scan continues. The exception ends WordPress’s loop over that action, so later listeners of the same action don’t run in that request: catch your own errors.

Comparing scans

  • Compare full scans with full scans. A re-scan of one plugin or theme (scope.type = partial) replaces that plugin’s rows in the report; it is not a new baseline.
  • Coverage is not a problem. Which heavy files can be checked depends on how a scan ran. On our test site, WP-CLI ran without a memory limit and checked a very dense file that scans with a 128 MB or 256 MB limit skipped. The context (SAPI, memory and time limits, the version source) says how each scan ran, so a file that moved between “could not be checked” and “checked” can be told apart from a new problem.

Stability

  • Within API 1.x, changes are additive: new functions, new optional arguments, new keys in returned arrays, new actions. Existing ones keep their meaning. Ignore keys you don’t know.
  • Something that has to go is marked deprecated first and kept for at least two minor releases of CompatNav; it is removed only in API 2.0.
  • Rule IDs are part of the contract: an existing ID keeps its meaning, and a withdrawn one is never reused. The same goes for error codes, verdict codes, statuses and scope types.
  • Texts come in the site’s language (Settings → General), not the current user’s, and may be reworded between releases: compare codes and IDs, never texts.

Changelog

  • 1.1 (CompatNav 1.2.0): one-file code checks in memory, package scans (a zip read in memory, never extracted), the report’s files in chunks, supported targets, the report screen’s address, tabs on the CompatNav screen, a caller name in “Started by”, and the findings’ path on coverage items. Nothing of 1.0 changes.
  • 1.0 (CompatNav 1.1.0): first version.