Skip to main content

GravityKit\GravityMigrate\Media\MediaPaths

The rules deciding which uploaded file a bundle may carry, what it is called inside the

archive, and how many of them fit.

Every method here is static and pure: no database, no filesystem, no WordPress. These are the decisions an untrusted bundle is refused by -- a path that climbs out of the media root, an entry meta value that is not the shape its field type promises, a manifest naming a file the archive never carried -- and each stays exercisable without a WordPress install.

Since: %ver%

Source: src/Media/MediaPaths.php:21

Details

  • Kind: class
  • Namespace: GravityKit\GravityMigrate\Media

Methods

MethodDescription
relative_to()The path of an absolute file relative to a base directory, or null if it is not under it.
relative_from_url()Turns a file URL into its path relative to the uploads URL it should sit under.
is_safe_relative_path()Whether a manifest entry is safe to write inside the media root.
extension_of()The extension of a filename, or '' when it does not end in a plain one.
has_allowed_extension()Whether a filename's extension is one a bundle may write.
has_masked_extension()Whether a name hides a refused extension behind an allowed one.
media_types_only()Narrows a WordPress mime map to the types a bundle's files may actually be.
file_urls_from_meta_value()The file URLs a single entry meta value names, given the type of the field that wrote it.
url_from_post_image_value()The URL half of a post_image value.
replace_post_image_url()Rebuilds a post_image value around a new URL, keeping its title, caption and alt text.
image_choice_urls()The image URLs an Image Choice field's choices point at.
select_within_budget()Fills a size budget from a list of sized files, in the order given.

Method Reference

relative_to()

public static function relative_to( string $absolute_path, string $base_dir ): ?string

The path of an absolute file relative to a base directory, or null if it is not under it.

A prefix test, not a substring replace. The obvious implementation -- str_replace( $base, '', $path ) -- rewrites a base that appears anywhere in the string and returns the untouched path when it appears nowhere, so a file outside the uploads directory comes back looking exactly like a file inside it.

Parameters

NameTypeDefaultDescription
$absolute_pathstringAbsolute path to a file.
$base_dirstringAbsolute path to the directory it should sit under.

Returns

  • string | null — Relative path with no leading slash, or null when it is not under $base_dir.

Since: %ver%

Source: src/Media/MediaPaths.php:142

relative_from_url()

public static function relative_from_url( string $file_url, string $uploads_url ): ?string

Turns a file URL into its path relative to the uploads URL it should sit under.

The URL comes out of exported entry meta, so it is another site's data: a URL that merely mentions the uploads URL somewhere in a query string is not a file in it.

The scheme is the one part allowed to differ. A site that moved to https keeps http:// in every entry written before the move, and wp_upload_dir() answers with the scheme the site serves on TODAY -- so on ordinary data a strict comparison matches nothing, and an export asked for its files packages none of them. Host and path are still compared in full, and both sides lose their scheme together, so a file on another host stays refused.

A differing HOST is deliberately not tolerated. www.example.com and example.com are usually one site and a CDN hostname usually fronts the same uploads directory, but this function cannot tell either of those from a stored URL pointing at a genuinely different site, and the cost of guessing wrong is packaging somebody else's files.

Parameters

NameTypeDefaultDescription
$file_urlstringThe stored file URL.
$uploads_urlstringThe source site's uploads base URL.

Returns

  • string | null — Relative path, or null when the URL does not name a file under $uploads_url.

Since: %ver%

Source: src/Media/MediaPaths.php:179

is_safe_relative_path()

public static function is_safe_relative_path( string $relative_path ): bool

Whether a manifest entry is safe to write inside the media root.

Refuses anything that is not a plain relative path to an allowed file type: an absolute path, a Windows drive, a .. segment, a byte that terminates a C string, or an extension on the denylist. Checked on the way into the archive AND on the way out of it, because only the second of those runs against an archive somebody else built.

Parameters

NameTypeDefaultDescription
$relative_pathstringPath relative to the uploads directory.

Returns

  • bool

Since: %ver%

Source: src/Media/MediaPaths.php:248

extension_of()

public static function extension_of( string $path ): string

The extension of a filename, or '' when it does not end in a plain one.

Deliberately NOT pathinfo( $path, PATHINFO_EXTENSION ), which returns everything after the last dot whatever it contains -- so a name whose tail is a denied extension plus trailing junk reports an extension no denylist holds, while the filesystem can still resolve it back to the denied one (Win32 strips a trailing space; an NTFS alternate data stream suffix is not part of the name). Anchoring to [A-Za-z0-9]+ at the very end means a name that is not cleanly extensioned has no extension at all, and is refused rather than waved through.

Parameters

NameTypeDefaultDescription
$pathstringA path or filename.

Returns

  • string — Lowercased extension, or '' when there is not a clean one.

Since: %ver%

Source: src/Media/MediaPaths.php:318

has_allowed_extension()

public static function has_allowed_extension( string $path ): bool

Whether a filename's extension is one a bundle may write.

Parameters

NameTypeDefaultDescription
$pathstringA path or filename.

Returns

  • bool

Since: %ver%

Source: src/Media/MediaPaths.php:338

has_masked_extension()

public static function has_masked_extension( string $path ): bool

Whether a name hides a refused extension behind an allowed one.

An extension check reads the LAST extension, so a name whose interior extension is a denied one still passes as an image. Apache's mod_mime assigns a handler from ANY extension in the name, so such a file is handled by that handler while every last-extension check calls it media. sanitize_file_name() does not close it -- core leaves a two-part name untouched (if ( count( $parts ) <= 2 ) in wp-includes/formatting.php), so the underscore it would add never arrives.

Only interior extensions are judged, and only against the same rule the last one faces, so a name that is merely dotted stays allowed: an export is named for the site it came from, and gk-migrate-www.example.com-2026-01-01-1.zip carries three dots by design.

Parameters

NameTypeDefaultDescription
$pathstringA path or filename.

Returns

  • bool

Since: %ver%

Source: src/Media/MediaPaths.php:389

media_types_only()

public static function media_types_only( array $mimes ): array

Narrows a WordPress mime map to the types a bundle's files may actually be.

get_allowed_mime_types() answers "what will this site accept from a logged-in author", and for an administrator on single-site that answer includes text/html and application/javascript: core strips those two keys only if ( empty( $unfiltered ) ), and both staging and import run inside the importing administrator's own request. A migration bundle has no reason to carry markup, so the packaged-file gate asks a different question -- what a file-upload field collects -- and this is where it is asked.

Narrowing, not replacing: a type the site itself refuses is still refused, because the caller passes the site's own map in. Each alternation is filtered separately, so jpg|jpeg|jpe survives intact while htm|html disappears entirely.

Parameters

NameTypeDefaultDescription
$mimesarrayExtension pattern => mime type, as WordPress reports it.

Returns

  • array — The same shape, carrying only media types with allowed extensions.

Since: %ver%

Source: src/Media/MediaPaths.php:431

file_urls_from_meta_value()

public static function file_urls_from_meta_value( string $meta_value, string $field_type ): array

The file URLs a single entry meta value names, given the type of the field that wrote it.

Three storage shapes, one per field type:

  • fileupload: one URL, or a JSON array of them for a multi-file field.
  • post_image: URL|:|title|:|caption|:|description|:|alt.
  • signature: a bare filename resolved against {uploads}/gravity_forms/signatures/.

Parameters

NameTypeDefaultDescription
$meta_valuestringThe stored value.
$field_typestringOne of \self::UPLOAD_FIELD_TYPES().

Returns

  • string[] — Zero or more URLs, in the order the value names them.

Since: %ver%

Source: src/Media/MediaPaths.php:503

url_from_post_image_value()

public static function url_from_post_image_value( string $meta_value ): ?string

The URL half of a post_image value.

The value is URL|:|title|:|caption|:|description|:|alt, and an image field left empty still stores the separators, so an empty first part is a field with no image rather than a malformed one.

Parameters

NameTypeDefaultDescription
$meta_valuestringThe stored value.

Returns

  • string | null

Since: %ver%

Source: src/Media/MediaPaths.php:553

replace_post_image_url()

public static function replace_post_image_url( string $meta_value, string $new_url ): string

Rebuilds a post_image value around a new URL, keeping its title, caption and alt text.

Parameters

NameTypeDefaultDescription
$meta_valuestringThe stored value.
$new_urlstringThe URL the image now lives at.

Returns

  • string

Since: %ver%

Source: src/Media/MediaPaths.php:570

image_choice_urls()

public static function image_choice_urls( array $display_meta ): array

The image URLs an Image Choice field's choices point at.

Unlike every other type here this is form data, not entry data: the images belong to the form's field definition, so a form with no entries at all still carries them.

Parameters

NameTypeDefaultDescription
$display_metaarrayA form's decoded display_meta.

Returns

  • string[]

Since: %ver%

Source: src/Media/MediaPaths.php:588

select_within_budget()

public static function select_within_budget( array $files, int $budget ): array

Fills a size budget from a list of sized files, in the order given.

The caller orders the list newest-entry-first, so a bundle that cannot carry everything carries the most recent uploads rather than an arbitrary slice. A single file larger than the whole budget is skipped and the walk continues, rather than stopping: one 90 MB video must not cost the operator the two hundred small images behind it.

Parameters

NameTypeDefaultDescription
$filesarrayList of [ 'path' => string, 'size' => int ], most wanted first.
$budgetintMaximum total bytes to include. Zero or less includes nothing.

Returns

  • array{included:string,excluded:string,included_size:int,excluded_size:int}

Since: %ver%

Source: src/Media/MediaPaths.php:635