PHPackages                             x3p0-dev/x3p0-asset - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. x3p0-dev/x3p0-asset

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

x3p0-dev/x3p0-asset
===================

A small, modern asset-resolution library for WordPress plugins and themes.

11PHP

Since Jul 9Pushed 1mo agoCompare

[ Source](https://github.com/x3p0-dev/x3p0-asset)[ Packagist](https://packagist.org/packages/x3p0-dev/x3p0-asset)[ RSS](/packages/x3p0-dev-x3p0-asset/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

X3P0: Asset
===========

[](#x3p0-asset)

A small, modern asset-resolution library for WordPress plugins and themes. It turns a project-relative path (`public/css/screen.css`) into a value object that knows its public URL, absolute path, and — when the file was built with [`@wordpress/scripts`](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-scripts/)— its dependencies and cache-busting version, read automatically from the generated `.asset.php` file.

[![License](https://camo.githubusercontent.com/26f8b6541ea045cc1dbc2267208158b5a7ebbf5cf437c4b486d80fee9386f77e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d47504c2d2d322e302d2d6f722d2d6c617465722d626c75652e737667)](LICENSE.md)[![PHP Version](https://camo.githubusercontent.com/04744bae0a61d2ffe29c26f07a9612eae20445fc6feaeb77b3af1f0e9be6447c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344382e312d3838393242462e737667)](https://php.net)

Features
--------

[](#features)

- **One value type**: `Asset` describes any bundled file — script, style, image, font, audio — with its URL and filesystem path.
- **Automatic build metadata**: reads the `.asset.php` file emitted by `@wordpress/scripts`, so `dependencies()` and `version()` are filled in for you.
- **Sensible fallback**: files with no `.asset.php` fall back to the file's modification time for cache busting.
- **Location resolvers**: pick where paths resolve — the active theme, the parent theme, or a plugin — behind one abstract `AssetResolver`.
- **WordPress-friendly, core-free values**: only the resolvers call WordPress functions; the `Asset` value itself stays free of WordPress dependencies.
- **Type-Safe**: full PHP 8.1+ type declarations for better IDE support.

Requirements
------------

[](#requirements)

- PHP 8.1 or higher
- WordPress (recommended latest version)
- Composer

Installation
------------

[](#installation)

Install via Composer:

```
composer require x3p0-dev/x3p0-asset
```

**Important:** If you're releasing this as part of a theme or plugin bundle, please vendor prefix your installation to avoid conflicts with other plugins/themes. See [Vendor Prefixing](#vendor-prefixing) below.

Quick Start
-----------

[](#quick-start)

### 1. Pick a resolver

[](#1-pick-a-resolver)

A resolver decides where relative paths are resolved from. Bundle a single resolver of your chosen direction and ask it for every asset:

```
use X3P0\Asset\ThemeAssetResolver;

$assetResolver = new ThemeAssetResolver();
```

### 2. Get an asset

[](#2-get-an-asset)

```
$style = $assetResolver->asset('public/css/screen.css');
```

### 3. Use it

[](#3-use-it)

The asset carries everything the WordPress enqueue functions need:

```
wp_enqueue_style(
	'my-theme-screen',
	$style->fileUrl(),
	$style->dependencies(),
	$style->version()
);

// Register the path so WordPress can inline the stylesheet when it's small.
wp_style_add_data('my-theme-screen', 'path', $style->filePath());
```

For a script whose build emitted `public/js/editor.asset.php`, the dependencies and version come straight from that file:

```
$script = $assetResolver->asset('public/js/editor.js');

wp_enqueue_script(
	'my-theme-editor',
	$script->fileUrl(),
	$script->dependencies(), // e.g. ['wp-blocks', 'wp-element']
	$script->version(),      // e.g. the build hash
	true
);
```

Core Concepts
-------------

[](#core-concepts)

### The `Asset` value

[](#the-asset-value)

`Asset` extends `SplFileInfo`, so every filesystem helper you already know (`getSize()`, `getMTime()`, `getExtension()`, …) is available alongside these:

MethodReturns`fileUrl()`Public URL to the file`filePath()`Absolute filesystem path to the file`dependencies()`Registered dependencies, or `[]` when there's no `.asset.php``version()`Build hash from the `.asset.php`, otherwise the file's modification time`hasDataFile()`Whether the `.asset.php` data file existsAn `Asset` is location-agnostic: it's constructed with an already-resolved absolute path and public URL, so it holds no knowledge of themes or plugins. You normally don't construct it directly — a resolver mints it for you.

### The `.asset.php` data file

[](#the-assetphp-data-file)

When you build scripts and styles with `@wordpress/scripts`, the [dependency-extraction plugin](https://www.npmjs.com/package/@wordpress/dependency-extraction-webpack-plugin)emits a companion **asset file** next to each entry point — `editor.js` gets `editor.asset.php` — returning an array of `dependencies` and a `version`.

`Asset` finds that file automatically (its own name with the extension swapped for `.asset.php`) and uses it:

- **`dependencies()`** returns the array's `dependencies`, or `[]` if the file is absent.
- **`version()`** returns the array's `version`, or the built file's modification time if the file is absent.
- **`hasDataFile()`** reports whether the file exists — useful for skipping registration when a build output is missing.

The lookup is memoized, so the file is checked and included at most once per `Asset`.

### Resolvers

[](#resolvers)

An `AssetResolver` resolves relative paths against a base location and mints `Asset` objects that live there. The base — the one thing that differs between a plugin and a theme — is supplied by the concrete resolvers:

ResolverResolves againstBacking functions`ThemeAssetResolver`the active theme (child-overridable)`get_theme_file_path()` / `get_theme_file_uri()``ParentThemeAssetResolver`the parent theme (always ships from the parent)`get_parent_theme_file_path()` / `get_parent_theme_file_uri()``PluginAssetResolver`a plugin directory`plugin_dir_path()` / `plugins_url()`Each exposes:

```
$assetResolver->asset('public/js/app.js');   // Asset from a relative path
$assetResolver->fromFile($splFileInfo);      // Asset from a discovered file
$assetResolver->path('public/js/app.js');    // absolute filesystem path (string)
$assetResolver->url('public/js/app.js');     // public URL (string)
$assetResolver->relativize($absolutePath);   // absolute path -> base-relative path
```

`PluginAssetResolver` takes the plugin's main file so it can anchor both the path and the URL:

```
use X3P0\Asset\PluginAssetResolver;

// Typically from the plugin's bootstrap file.
$assetResolver = new PluginAssetResolver(__FILE__);
```

### Choosing a resolver

[](#choosing-a-resolver)

- **`ThemeAssetResolver`** lets a child theme override a bundled file by shipping its own copy at the same relative path. Good for assets a child theme should be able to replace (images, fonts, editor styles).
- **`ParentThemeAssetResolver`** always loads from the theme that ships the file, even when a child theme is active. Good for built scripts and styles that belong to the parent.
- **`PluginAssetResolver`** resolves against a plugin directory.

A project generally binds one resolver and uses it everywhere.

### Discovering assets

[](#discovering-assets)

`fromFile()` mints an `Asset` from an already-discovered `SplFileInfo`, deriving its base-relative path via `relativize()`. This pairs well with directory iteration — for example, collecting every built block stylesheet in a folder:

```
foreach ($cssFiles as $file) {
	$asset = $assetResolver->fromFile($file);

	if ($asset->hasDataFile()) {
		// register/enqueue $asset->fileUrl(), $asset->dependencies(), ...
	}
}
```

Errors
------

[](#errors)

Every exception the package throws implements the `AssetException` marker interface (which extends `Throwable`), so you can catch anything originating here in a single block. Each concrete exception also extends the most fitting SPL class, so code that only cares about the SPL type keeps working too.

ExceptionExtendsThrown when`PathOutsideBaseException``InvalidArgumentException``relativize()` / `fromFile()` receive a path that isn't within the resolver's base`InvalidAssetDataException``UnexpectedValueException`an `.asset.php` file exists but does not return an array (a malformed build)```
use X3P0\Asset\AssetException;

try {
	$asset = $assetResolver->fromFile($file);
	$deps  = $asset->dependencies();
} catch (AssetException $e) {
	// Any failure from this package: bad path, malformed build metadata, etc.
}
```

Both are programmer/build errors rather than routine conditions — in correct usage neither fires — so catching them is optional. The exceptions carry no WordPress dependency, so they behave the same whether or not WordPress is loaded.

Dependency Injection
--------------------

[](#dependency-injection)

The library has no container of its own, but it's designed to be bound in one. Bind the abstract `AssetResolver` to the concrete resolver your project uses, then type-hint `AssetResolver` wherever you need assets:

```
use X3P0\Asset\AssetResolver;
use X3P0\Asset\ThemeAssetResolver;

// Wherever you register bindings:
$container->singleton(AssetResolver::class, ThemeAssetResolver::class);
```

```
use X3P0\Asset\AssetResolver;

final class FrontendAssets
{
	public function __construct(private readonly AssetResolver $assetResolver)
	{}

	public function enqueue(): void
	{
		$style = $this->assetResolver->asset('public/css/screen.css');

		wp_enqueue_style(
			'my-theme-screen',
			$style->fileUrl(),
			$style->dependencies(),
			$style->version()
		);
	}
}
```

Swapping the whole project between the active theme and the parent theme is then a one-line change to the binding.

Vendor Prefixing
----------------

[](#vendor-prefixing)

Because WordPress loads every active plugin and theme into the same PHP process, two of them shipping the same un-prefixed library will collide. If you distribute your plugin or theme, **prefix this package's namespace** so your copy is isolated from everyone else's.

The X3P0 projects do this at build time with [`x3p0-dev/x3p0-prelude`](https://github.com/x3p0-dev), which copies the dependency into your project and rewrites its namespace under your own — for example, `X3P0\Asset` becomes `Acme\MyPlugin\Asset`. A general-purpose alternative is [PHP-Scoper](https://github.com/humbug/php-scoper).

Prefix at release time, not during development, and point your autoloader at the prefixed copy.

License
-------

[](#license)

X3P0: Asset is licensed under the [GPL-2.0-or-later](LICENSE.md) license.

Credits
-------

[](#credits)

Created and maintained by [Justin Tadlock](https://github.com/justintadlock)under the [X3P0](https://github.com/x3p0-dev) umbrella.

Support
-------

[](#support)

- [GitHub Issues](https://github.com/x3p0-dev/x3p0-asset/issues)
- [Packagist](https://packagist.org/packages/x3p0-dev/x3p0-asset)

###  Health Score

20

—

LowBetter than 12% of packages

Maintenance60

Regular maintenance activity

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/ef868a4b45ef0263f49361744b8bb04e8f3cfcb8f3f79b274fc62823115ee596?d=identicon)[justintadlock](/maintainers/justintadlock)

---

Top Contributors

[![justintadlock](https://avatars.githubusercontent.com/u/1816309?v=4)](https://github.com/justintadlock "justintadlock (1 commits)")

### Embed Badge

![Health badge](/badges/x3p0-dev-x3p0-asset/health.svg)

```
[![Health](https://phpackages.com/badges/x3p0-dev-x3p0-asset/health.svg)](https://phpackages.com/packages/x3p0-dev-x3p0-asset)
```

###  Alternatives

[emreyarligan/enum-concern

A PHP package for effortless Enumeration handling with Laravel Collections 📦 ✨

21264.1k3](/packages/emreyarligan-enum-concern)[sudiptpa/guid

A minimal GUID generator package for PHP.

14146.7k1](/packages/sudiptpa-guid)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
