JPKCom CF7 Upload Path

JPKComGitPluginUpdater

FinalYes

Class JPKComGitPluginUpdater

Handles plugin updates from GitHub-hosted JSON manifests.

Why a family: WordPress only runs the code of ACTIVE plugins, so a copy of this class inside an inactive plugin never hooks anything and that plugin would never be offered an update. Every JPKCom plugin therefore registers its copy in a shared registry, and the copy with the highest UPDATER_VERSION (ties: the first registered) does the work for every installed JPKCom plugin. All other 1.5+ copies stay passive. Copies older than 1.5 do not know the registry and keep serving their own plugin; the leader leaves plugins whose old copy is loaded alone, so the two never disagree about a manifest or a package.

Table of Contents

Constants

UPDATER_VERSION  : mixed = '1.5.0'
Version of this updater implementation. Decides the leader election, so it must be raised with every change to the family logic.
ASSET_PATTERN  : mixed = '~^(jpkcom-[a-z0-9-]+)\.zip\z~'
File name of a JPKCom release asset, lower-cased, as the install gate claims it.
HOST_FAIL_KEY  : mixed = 'jpk_git_update_host_fail'
Host-wide breaker: one dead or slow github.io must not cost a timeout per plugin.
MANIFEST_URL_FORMAT  : mixed = 'https://jpkcom.github.io/%1$s/plugin_%1$s.json'
Manifest location for a plugin whose copy is not loaded.
PACKAGE_PATTERN  : mixed = '~^https://github\.com/(?i:JPKCom)/(jpkcom-[a-z...
Release assets are published as <slug>.zip on the plugin's own repository.
PLUGIN_URI_PATTERN  : mixed = '~^https://github\.com/JPKCom/(jpkcom-[a-z0-9-]...
A JPKCom plugin is recognised by its Plugin URI, which every released version of every JPKCom plugin carries (unlike the Update URI header, added only in the 2026-10-19 releases), together with the canonical file <slug>/<slug>.php.
READ_FETCH_BUDGET  : mixed = 5
Uncached manifest fetches allowed per request while rendering update data.
REGISTRY  : mixed = 'jpkcom_git_updater_registry'
Global registry shared by all copies (each copy lives in its own namespace, so class statics are not shared). Entries are plain arrays with the scalar keys v, id, file, slug, manifest, version. Later versions may add keys but must never rename or retype these.
SLOW_FETCH_SECONDS  : mixed = 3.0
A successful fetch slower than this (seconds) also trips the host breaker.
SLUG_PATTERN  : mixed = '~^jpkcom-[a-z0-9-]+\z~'
Slug of a JPKCom plugin, as used for folder and main file.

Properties

$cache_key  : string
$current_version  : string
$discovered  : array<string, array{slug: string, file: string, version: string, manifest: string, frozen: bool}>|null
$leader_for_size  : int
$leader_id  : int|null
$manifest_url  : string
$members  : array<string, array{slug: string, file: string, version: string, manifest: string, frozen: bool}>|null
$members_signature  : string
$plugin_file  : string
$plugin_slug  : string
$read_fetches  : int
$validated_urls  : array<string, bool>

Methods

__construct()  : mixed
Constructor
check_update()  : mixed
Inject update information for every family member.
clear_cache()  : void
Clear cached manifests after an update.
forget_members()  : void
Drop the plugin scan and member cache after a plugin was deleted or updated.
plugin_info()  : mixed
Provide detailed plugin info in the “View Details” modal.
strip_packages()  : mixed
Remove JPKCom package URLs before the update transient is stored.
verify_download_checksum()  : mixed
Verify download checksum before installation.
cache_key_for()  : string
Manifest cache key, shared with every copy back to 1.4.0.
checksum_error()  : WP_Error
Checksum mismatch error.
discovered()  : array<string, array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool}>
Every installed plugin in the canonical <slug>/<slug>.php layout whose Plugin URI is its JPKCom repository, keyed by plugin file.
elect()  : int|null
Elect the leader among the registered copies.
entry()  : object
Build one update entry.
get_remote_manifest()  : object|null
Fetch and decode a remote manifest file.
icons()  : array<string, string>
Icon URLs from the manifest as an array (core indexes it with []).
is_background()  : bool
Whether this request is cron or WP-CLI, where latency does not matter.
is_leader()  : bool
Whether this copy is the elected leader.
is_plugin_loaded()  : bool
Whether a plugin's main file was actually included in this request.
is_registered_file()  : bool
Whether a plugin file belongs to a registered (1.5+) copy.
is_vcs_checkout()  : bool
Whether a plugin directory is a version-control checkout.
log()  : void
Write a log line in WP_DEBUG mode.
may_fetch_on_read()  : bool
Whether rendering update data may fetch an uncached manifest.
member_by_folder()  : array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool}|null
Find a member by its folder name (the slug WordPress uses for it).
members()  : array<string, array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool}>
The JPKCom plugins this leader serves, keyed by plugin file.
package_url()  : string
The manifest's download URL, if it is a release asset of this very plugin.
register()  : void
Add this copy to the shared registry.
registry()  : array<int, array{v: string, id: int, file: string, slug: string, manifest: string, version: string}>
Valid registry entries, in registration order.
remember_failure()  : void
Back off after a failed manifest fetch.
runs_old_copy()  : bool
Whether a JPKCom plugin's own pre-1.5 updater copy runs in this request.
text()  : string
A manifest value as plain text, or the fallback when it is not a scalar.
url()  : string
A manifest value as a sanitised URL, or the fallback.

Constants

UPDATER_VERSION

Version of this updater implementation. Decides the leader election, so it must be raised with every change to the family logic.

public mixed UPDATER_VERSION = '1.5.0'

ASSET_PATTERN

File name of a JPKCom release asset, lower-cased, as the install gate claims it.

private mixed ASSET_PATTERN = '~^(jpkcom-[a-z0-9-]+)\.zip\z~'

HOST_FAIL_KEY

Host-wide breaker: one dead or slow github.io must not cost a timeout per plugin.

private mixed HOST_FAIL_KEY = 'jpk_git_update_host_fail'

MANIFEST_URL_FORMAT

Manifest location for a plugin whose copy is not loaded.

private mixed MANIFEST_URL_FORMAT = 'https://jpkcom.github.io/%1$s/plugin_%1$s.json'

PACKAGE_PATTERN

Release assets are published as <slug>.zip on the plugin's own repository.

private mixed PACKAGE_PATTERN = '~^https://github\.com/(?i:JPKCom)/(jpkcom-[a-z0-9-]+)/releases/download/[^/?#]+/\1\.zip\z~'

The organisation is matched case-insensitively because GitHub resolves it that way: a hand-typed lowercase URL must not slip past the checksum gate.

PLUGIN_URI_PATTERN

A JPKCom plugin is recognised by its Plugin URI, which every released version of every JPKCom plugin carries (unlike the Update URI header, added only in the 2026-10-19 releases), together with the canonical file <slug>/<slug>.php.

private mixed PLUGIN_URI_PATTERN = '~^https://github\.com/JPKCom/(jpkcom-[a-z0-9-]+)/?\z~'

The ~ delimiter matters: none of these patterns may contain it.

READ_FETCH_BUDGET

Uncached manifest fetches allowed per request while rendering update data.

private mixed READ_FETCH_BUDGET = 5

REGISTRY

Global registry shared by all copies (each copy lives in its own namespace, so class statics are not shared). Entries are plain arrays with the scalar keys v, id, file, slug, manifest, version. Later versions may add keys but must never rename or retype these.

private mixed REGISTRY = 'jpkcom_git_updater_registry'

SLOW_FETCH_SECONDS

A successful fetch slower than this (seconds) also trips the host breaker.

private mixed SLOW_FETCH_SECONDS = 3.0

SLUG_PATTERN

Slug of a JPKCom plugin, as used for folder and main file.

private mixed SLUG_PATTERN = '~^jpkcom-[a-z0-9-]+\z~'

Properties

$discovered

private array<string, array{slug: string, file: string, version: string, manifest: string, frozen: bool}>|null $discovered = null

Per-request scan of installed JPKCom plugins

$leader_for_size

private int $leader_for_size = -1

Registry size the cached election result belongs to

$members

private array<string, array{slug: string, file: string, version: string, manifest: string, frozen: bool}>|null $members = null

Per-request family member cache

$members_signature

private string $members_signature = ''

Registry signature the member cache was built for

$read_fetches

private int $read_fetches = 0

Uncached manifest fetches made by the read filter in this request

$validated_urls

private array<string, bool> $validated_urls = []

Per-request memo of wp_http_validate_url() results

Methods

__construct()

Constructor

public __construct(string $plugin_file, string $current_version, string $manifest_url) : mixed
Parameters
$plugin_file : string

Absolute path to the main plugin file (FILE).

$current_version : string

Current plugin version.

$manifest_url : string

Full URL to the remote JSON manifest.

check_update()

Inject update information for every family member.

public check_update(mixed $transient) : mixed

Runs on every read of the update_plugins site transient. Entries are built fresh on each read (so the package that strip_packages() removed before storage is back for the upgrader) and never carry autoupdate (core would treat it as a forced auto-update) or translations (installed unverified).

Parameters
$transient : mixed

WordPress transient data.

clear_cache()

Clear cached manifests after an update.

public clear_cache(mixed $upgrader, mixed $options) : void

Single updates pass plugin, bulk updates (AJAX, update-core, WP-CLI) pass plugins.

Parameters
$upgrader : mixed

WordPress upgrader instance.

$options : mixed

Upgrade options.

forget_members()

Drop the plugin scan and member cache after a plugin was deleted or updated.

public forget_members() : void

plugin_info()

Provide detailed plugin info in the “View Details” modal.

public plugin_info(mixed $result, mixed $action, mixed $args) : mixed

Answers for every family member, so the modal of an inactive plugin works and never falls through to wordpress.org, where a squatted slug would show someone else's plugin.

Parameters
$result : mixed

Default response.

$action : mixed

Current action.

$args : mixed

API request arguments.

strip_packages()

Remove JPKCom package URLs before the update transient is stored.

public strip_packages(mixed $value) : mixed

The leader re-injects them on every read. A request in which no 1.5+ copy is loaded (every JPKCom plugin deactivated, WP-CLI --skip-plugins, a multisite main site without JPKCom code) then finds an empty package, which core refuses ("Automatic update is unavailable"), instead of a URL it would install without any checksum. Best effort: a third party that writes the option directly bypasses this filter.

Parameters
$value : mixed

Transient value about to be stored.

verify_download_checksum()

Verify download checksum before installation.

public verify_download_checksum(mixed $reply, mixed $package[, mixed $upgrader = null ][, mixed $hook_extra = [] ]) : mixed

Ownership is decided first: a package belongs to the family when the upgrader names a member plugin ($hook_extra['plugin'], set for every update) or when its URL is a JPKCom release asset (installs, which carry no plugin name). Anything else passes through untouched. For a family package nothing is trusted without a hash match against the manifest, including a file an earlier filter already returned.

Parameters
$reply : mixed

Whether to bail without returning the package (default false).

$package : mixed

The package file name or URL.

$upgrader : mixed = null

The WP_Upgrader instance.

$hook_extra : mixed = []

Extra arguments passed to hooked filters.

Return values
mixed —

Path to the verified package, $reply when the package is not ours, WP_Error if verification fails.

cache_key_for()

Manifest cache key, shared with every copy back to 1.4.0.

private cache_key_for(string $slug) : string
Parameters
$slug : string

Plugin slug.

Return values
string

checksum_error()

Checksum mismatch error.

private checksum_error(string $calculated[, string $expected = '' ]) : WP_Error
Parameters
$calculated : string

Calculated hash or a placeholder.

$expected : string = ''

Expected hash.

Return values
WP_Error

discovered()

Every installed plugin in the canonical <slug>/<slug>.php layout whose Plugin URI is its JPKCom repository, keyed by plugin file.

private discovered() : array<string, array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool}>

Reads the headers of jpkcom-* candidates only, not the whole plugin directory, because this runs on every read of the update transient.

Return values
array<string, array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool}>

elect()

Elect the leader among the registered copies.

private elect(array<string|int, mixed> $entries) : int|null

Deliberately looser than registry(): any copy inside a jpkcom-* plugin with a comparable version takes part, so a future copy that changes its manifest location or the shape of its entry still wins over this one instead of leaving two leaders.

Parameters
$entries : array<string|int, mixed>

Raw registry.

Return values
int|null —

Object id of the leader.

entry()

Build one update entry.

private entry(array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool} $member, string $new_version, string $package, array<string, string> $icons, string $tested, string $requires, string $requires_php) : object
Parameters
$member : array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool}

Member.

$new_version : string

Manifest version.

$package : string

Validated package URL or ''.

$icons : array<string, string>

Icons.

$tested : string

Tested up to.

$requires : string

Requires at least.

$requires_php : string

Requires PHP.

Return values
object

get_remote_manifest()

Fetch and decode a remote manifest file.

private get_remote_manifest(string $slug, string $manifest_url[, string $context = 'action' ]) : object|null

Uses a locking mechanism to prevent race conditions when multiple requests try to fetch the manifest simultaneously.

Parameters
$slug : string

Plugin slug.

$manifest_url : string

Manifest URL.

$context : string = 'action'

'read' (rendering update data: may be denied a fetch, counts against the request budget of an interactive request, honours the host breaker) or 'action' (install, update, details modal).

Return values
object|null —

Decoded manifest or null on failure.

icons()

Icon URLs from the manifest as an array (core indexes it with []).

private icons(object $remote, string $slug) : array<string, string>
Parameters
$remote : object

Manifest.

$slug : string

Plugin slug.

Return values
array<string, string>

is_background()

Whether this request is cron or WP-CLI, where latency does not matter.

private is_background() : bool
Return values
bool

is_leader()

Whether this copy is the elected leader.

private is_leader() : bool

Highest UPDATER_VERSION wins, ties go to the first registered copy. Not memoised: a transient read at init priority 5 can happen before every copy has registered, and the answer must settle once they have.

Return values
bool

is_plugin_loaded()

Whether a plugin's main file was actually included in this request.

private is_plugin_loaded(string $file) : bool

Asks PHP, not the active_plugins option: wp --skip-plugins=<slug> keeps the option but never loads the file, and a plugin listed as active but not loaded must be served like an inactive one.

Parameters
$file : string

Plugin file relative to the plugins directory.

Return values
bool

is_registered_file()

Whether a plugin file belongs to a registered (1.5+) copy.

private is_registered_file(string $file) : bool
Parameters
$file : string

Plugin file.

Return values
bool

is_vcs_checkout()

Whether a plugin directory is a version-control checkout.

private is_vcs_checkout(string $dir) : bool

Updating one replaces the directory and deletes the working tree, so such a member is never offered an update. Same rule and filter as core's automatic updater.

Parameters
$dir : string

Absolute plugin directory.

Return values
bool

log()

Write a log line in WP_DEBUG mode.

private log(string $message) : void
Parameters
$message : string

Message.

may_fetch_on_read()

Whether rendering update data may fetch an uncached manifest.

private may_fetch_on_read() : bool

Only for users who can act on updates, cron and WP-CLI. An anonymous front end request never fetches; installs, updates and the details modal always may (they pass $context 'action').

Return values
bool

member_by_folder()

Find a member by its folder name (the slug WordPress uses for it).

private member_by_folder(string $folder) : array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool}|null
Parameters
$folder : string

Plugin folder.

Return values
array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool}|null

members()

The JPKCom plugins this leader serves, keyed by plugin file.

private members() : array<string, array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool}>

Members are (a) every loaded 1.5+ copy from the registry and (b) every discovered JPKCom plugin whose code is NOT loaded in this request. A plugin that is loaded but not registered runs an older updater copy, which serves it against its own cache; the leader stays out of its way.

A member is "frozen" (only ever listed as up to date, never offered a package) when it is a version-control checkout, which an update would wipe, or when its folder is not named after its slug (e.g. jpkcom-foo-main from a GitHub source download): the release ZIP unpacks into jpkcom-foo/, so an update would delete the renamed folder and leave the active plugin missing.

The cache is tied to the registry, because copies register one by one during init and a transient read can happen in between.

Return values
array<string, array{slug: string, folder: string, file: string, version: string, manifest: string, frozen: bool}>

package_url()

The manifest's download URL, if it is a release asset of this very plugin.

private package_url(object $remote, string $slug) : string

Anything else is never handed to WordPress, so the checksum gate always recognises what this class emits.

Parameters
$remote : object

Manifest.

$slug : string

Plugin slug.

Return values
string —

Validated URL or ''.

registry()

Valid registry entries, in registration order.

private registry() : array<int, array{v: string, id: int, file: string, slug: string, manifest: string, version: string}>

Malformed entries and entries that do not describe a JPKCom plugin (main file .php, manifest on jpkcom.github.io for that slug) are skipped, also for the leader election. No method is ever called on an entry, so a broken entry cannot break this copy.

Return values
array<int, array{v: string, id: int, file: string, slug: string, manifest: string, version: string}>

remember_failure()

Back off after a failed manifest fetch.

private remember_failure(string $cache_key, bool $host_wide) : void

Keeps a short-lived marker so the next requests skip the remote call instead of re-running it (and its timeout) on every page load.

Parameters
$cache_key : string

Manifest cache key.

$host_wide : bool

Whether the failure concerns the host, not the plugin.

runs_old_copy()

Whether a JPKCom plugin's own pre-1.5 updater copy runs in this request.

private runs_old_copy(string $file) : bool
Parameters
$file : string

Plugin file.

Return values
bool

text()

A manifest value as plain text, or the fallback when it is not a scalar.

private text(mixed $value[, string $fallback = '' ]) : string

Manifests are remote data and are cached by every copy since 1.4.0; an object or array where a string belongs must not turn into a fatal error on every admin page.

Parameters
$value : mixed

Manifest value.

$fallback : string = ''

Value for a missing or non-scalar field.

Return values
string

url()

A manifest value as a sanitised URL, or the fallback.

private url(mixed $value[, string $fallback = '' ]) : string
Parameters
$value : mixed

Manifest value.

$fallback : string = ''

Value for a missing or non-string field.

Return values
string
On this page

Search results