CreaCaptcha

CreationellCaptchaGitPluginUpdater

FinalYes

Class CreationellCaptchaGitPluginUpdater

Handles plugin updates from a GitHub-hosted JSON manifest. Downstream rename of JPKComGitPluginUpdater (Jean Pierre Kolb, shared across his plugin projects — see the file-level docblock for the rename rationale).

Tags
author

Jean Pierre Kolb jpk@jpkc.com

Table of Contents

Constants

FETCH_FAILURE_TTL  = 300
MANIFEST_JSON_DEPTH  = 32
MAX_MANIFEST_BYTES  = 1048576
Largest manifest body that is accepted, in bytes.

Properties

$cache_enabled  : bool
$cache_key  : string
$current_version  : string
$manifest_url  : string
$plugin_file  : string
$plugin_slug  : string

Methods

__construct()  : mixed
Constructor
check_update()  : object
Check for available plugin updates.
clear_cache()  : void
Clear cached manifest after a successful update.
plugin_info()  : mixed
Provide detailed plugin info in the “View Details” modal.
verify_download_checksum()  : bool|string|WP_Error
Verify the package checksum and hand the verified file to the installer.
default_avatar_url()  : string
The wordpress.org avatar URL used when the manifest supplies none.
default_profile_url()  : string
The wordpress.org profile URL used when the manifest supplies none.
failure_key()  : string
Transient key under which a failed manifest fetch is remembered.
get_remote_manifest()  : object|null
Fetch and decode the remote manifest file.
is_https_url()  : bool
Whether a URL is usable as a package/download source: valid per WordPress' own rules *and* https.
manifest_string()  : string
A manifest field as a string, or the fallback when the manifest carried something else (array, object, number, bool, null).
manifest_url_or_default()  : string
Returns a manifest-supplied URL if WordPress considers it a valid http(s) URL, otherwise the given default.
remember_fetch_failure()  : void
Remembers a failed manifest fetch so the next call does not repeat it.
validate_manifest()  : object|null
Structural check of a decoded manifest.

Constants

FETCH_FAILURE_TTL

private int FETCH_FAILURE_TTL = 300

How long a failed manifest fetch is remembered, in seconds.

MANIFEST_JSON_DEPTH

private int MANIFEST_JSON_DEPTH = 32

Maximum nesting depth accepted from the manifest JSON.

MAX_MANIFEST_BYTES

Largest manifest body that is accepted, in bytes.

private mixed MAX_MANIFEST_BYTES = 1048576

The decoded manifest is cached in wp_options for 24 hours, so its size is the site's problem, not the host's. The live manifest measured 180 KB on 2026-07-30 (README-derived readme_html is the bulk of it); 1 MiB is roughly five times that and only has to bound the transient, not track it closely. Raise it here if the README ever grows past that.

Properties

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()

Check for available plugin updates.

public check_update(object $transient) : object
Parameters
$transient : object

WordPress transient data.

Return values
object

clear_cache()

Clear cached manifest after a successful update.

public clear_cache(WP_Upgrader $upgrader, array<string|int, mixed> $options) : void
Parameters
$upgrader : WP_Upgrader

WordPress upgrader instance.

$options : array<string|int, mixed>

Upgrade options.

plugin_info()

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

public plugin_info(mixed $result, string $action, object $args) : mixed
Parameters
$result : mixed

Default response.

$action : string

Current action.

$args : object

API request arguments.

verify_download_checksum()

Verify the package checksum and hand the verified file to the installer.

public verify_download_checksum(bool|string|WP_Error $reply, string $package, WP_Upgrader $upgrader[, array<string, mixed> $hook_extra = [] ]) : bool|string|WP_Error

Runs on upgrader_pre_download, i.e. instead of WP core's own download: WP_Upgrader::download_package() returns the first non-false value this filter produces, unchanged, and only downloads itself when every handler returned false.

That is exactly why this method returns the temp file on success. The previous version downloaded the package, hashed it, deleted it and returned $reply (false) — so core downloaded a second copy and installed that one. The SHA-256 covered a file that was thrown away (LK-1). Now the hashed bytes and the installed bytes are the same file. WP_Upgrader::run() deletes it after the install because the path differs from $options['package'].

Note that core's optional signature verification is not lost here: it only applies to wordpress.org/downloads.wordpress.org/s.w.org (wp_signature_hosts), never to the github.com release asset this updater fetches.

Parameters
$reply : bool|string|WP_Error

Short-circuit value of the filter (default false).

$package : string

The package file name or URL.

$upgrader : WP_Upgrader

The WP_Upgrader instance.

$hook_extra : array<string, mixed> = []

Extra arguments from the upgrader; carries plugin (the plugin basename) for plugin updates.

Return values
bool|string|WP_Error —

The verified package path, the untouched $reply, or a WP_Error.

default_avatar_url()

The wordpress.org avatar URL used when the manifest supplies none.

private default_avatar_url(string $username) : string
Parameters
$username : string

Contributor name from the manifest.

Return values
string

default_profile_url()

The wordpress.org profile URL used when the manifest supplies none.

private default_profile_url(string $username) : string

sprintf()'s second parameter is variadic; passing it as the named argument values: (as this file did) is an ArgumentCountError at runtime, not a syntax error — positional only. rawurlencode() keeps a manifest-supplied name inside the path segment it belongs to.

Parameters
$username : string

Contributor name from the manifest.

Return values
string

failure_key()

Transient key under which a failed manifest fetch is remembered.

private failure_key() : string
Return values
string

get_remote_manifest()

Fetch and decode the remote manifest file.

private get_remote_manifest() : object|null

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

Return values
object|null —

Decoded manifest or null on failure.

is_https_url()

Whether a URL is usable as a package/download source: valid per WordPress' own rules *and* https.

private is_https_url(string $url) : bool

wp_http_validate_url() accepts plain http, and the file behind this URL is unpacked into wp-content/plugins and executed as PHP on the next request — a downgrade to http is not something the manifest gets to choose (LK-3).

Parameters
$url : string
Return values
bool

manifest_string()

A manifest field as a string, or the fallback when the manifest carried something else (array, object, number, bool, null).

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

Why this exists rather than “the sanitiser will handle it”: the WordPress sanitisers disagree about non-strings, and the ones this class uses land on both sides. Measured against WP 6.9 / PHP 8.3:

  • sanitize_text_field() is safe — _sanitize_text_fields() (wp-includes/formatting.php:5634) returns '' for array and object.
  • esc_url_raw() is not: esc_url() (formatting.php:4483-4487) only short-circuits on '' === $url and then calls ltrim( $url ) → TypeError for array and object.
  • sanitize_title() is not: it reaches preg_match() inside remove_accents() (formatting.php:1612) → TypeError.
  • trim() is not, for either.
  • wp_kses_post() fatals on an object (preg_replace() in wp_kses_no_null(), kses.php:1939) but not on an array: every step of the chain (preg_replace(), str_replace(), preg_replace_callback()) accepts an array subject and returns an array, so the value comes back AS AN ARRAY — element by element filtered, but never turned into the string the caller expects. (W3-12: this line used to read “it returns one, unescaped”, which suggests the content passes unfiltered. The defect is the TYPE, not the escaping.)

So a manifest with e.g. "homepage": ["x"] would white-screen the “View details” modal, and "icons": {"default": ["x"]} would do it on every wp-admin page load via check_update(). validate_manifest() bounds only the three fields the updater acts on (see there); this is where the rest is bounded (LK-6).

Parameters
$value : mixed

Raw value from the manifest.

$fallback : string = ''

Value to use when $value is not a string.

Return values
string

manifest_url_or_default()

Returns a manifest-supplied URL if WordPress considers it a valid http(s) URL, otherwise the given default.

private manifest_url_or_default(mixed $url, string $default) : string
Parameters
$url : mixed

Raw value from the manifest.

$default : string

Fallback URL built by this class.

Return values
string

remember_fetch_failure()

Remembers a failed manifest fetch so the next call does not repeat it.

private remember_fetch_failure() : void

The window is deliberately short: a manifest host that is down for a minute must not hide an update for a day. The cost of the miss is one delayed update check, the cost of not having it is a 15-second stall on every admin page load while the host is unreachable (LK-5).

validate_manifest()

Structural check of a decoded manifest.

private validate_manifest(mixed $remote) : object|null

Only the three fields the updater acts on are validated — version (drives the update decision), download_url (becomes the package URL) and checksum_sha256 (is the gate). Every other field keeps whatever type the JSON carried and is dealt with at the point of use — which for a field that ends up in a string sink means manifest_string() first, because the WordPress sanitisers are not uniformly type-safe (see there). A manifest that fails any of these is discarded whole: a half-trusted manifest is worse than none, and in particular a present-but-malformed checksum must never degrade into the “no checksum” branch (LK-2/LK-6).

Parameters
$remote : mixed

Decoded manifest.

Return values
object|null —

The manifest, or null when it does not match the schema.


        
On this page

Search results