Xgenious/ docs

Manifest Reference

plugin.json is the only required configuration file. It tells the framework where the entry class lives, what it needs, and what it contributes.

There is no module.json. Menus, legacy Blade hooks, and page-builder addons live inside xilancerMetaData.

Complete example

{
    "name": "My Plugin",
    "slug": "my-plugin",
    "version": "1.0.0",
    "alias": "myplugin",
    "entry": {
        "file": "src/MyPlugin.php",
        "class": "MyPlugin\\MyPlugin"
    },
    "autoload": {
        "psr-4": {
            "MyPlugin\\": "src/"
        }
    },
    "requires": {
        "framework": ">=1.0",
        "php": ">=8.3",
        "plugins": {
            "referrals": ">=1.0"
        }
    },
    "provides": [
        "checkout.price.calculate",
        "dashboard.referral_panel",
        "admin.menu"
    ],
    "capabilities": [
        "outbound-https"
    ],
    "legacyName": "MyPlugin",
    "xilancerMetaData": {
        "plugin_type": "external",
        "hooks": {
            "body_end": [
                "assets.banner"
            ]
        },
        "admin_settings": {
            "menu_item": [
                {
                    "id": "my-plugin-menu",
                    "label": "My Plugin",
                    "route": "admin.my-plugin.index",
                    "parent": null,
                    "icon": "fa-solid fa-star",
                    "permissions": "my-plugin-manage",
                    "class": "bg-success"
                }
            ]
        }
    }
}

Required fields

FieldTypeRules
namestringNon-empty. Display name.
slugstringLowercase letters, numbers, and dashes. Must start with a letter or number. Matches the directory intent.
versionstringNumeric dotted, up to four segments: 1, 1.2, 1.2.3, 1.2.3.4.
entry.filestringPath relative to the plugin directory. Must exist.
entry.classstringFully qualified class name. Must implement PluginsFramework\Plugins\PluginInterface.

The validator reports the first failure and stops. Fix it before continuing.

Entry

"entry": {
    "file": "src/MyPlugin.php",
    "class": "MyPlugin\\MyPlugin"
}
  • The file is loaded with require_once.
  • The class is instantiated as new $class($context).
  • The constructor must accept PluginContext as its first parameter.
  • boot() is called once per request (or per CLI command).

The class may have other constructor parameters only if they have defaults; the manager passes one argument.

Autoload

"autoload": {
    "psr-4": {
        "MyPlugin\\": "src/",
        "MyPlugin\\Support\\": "support/"
    }
}
  • Each namespace maps to a directory relative to the plugin root.
  • The bridge registers these on the Composer loader before boot.
  • Use a trailing backslash in the namespace key.
  • Do not commit a vendor/ directory unless you truly need one. Prefer zero dependencies.

If you need a file outside PSR-4, require_once it from the entry file. Keep that list short.

Requires

"requires": {
    "framework": ">=1.0",
    "php": ">=8.3",
    "plugins": {
        "referrals": ">=1.0",
        "cloud-storage": "^1.2"
    }
}

Supported constraints

FormMeaning
* or emptyAny version
1.2.3Exactly 1.2.3
=1.2.3Exactly 1.2.3
>=1.2At least 1.2
>1.2Greater than 1.2
<=2.0At most 2.0
<2.0Less than 2.0
^1.2At least 1.2, below 2.0
>=1.0 <2.0Both conditions (space or comma separated)

The implementation is a small semver checker, not Composer. It does not support ~, ||, or wildcards other than *.

Framework version

framework is checked against PluginFramework::VERSION, currently 1.0.0.

PHP version

php is checked against PHP_VERSION. Xilancer 4.3.0 requires 8.3 or newer.

Plugin dependencies

plugins maps a slug to a constraint. The manager:

  1. Verifies the plugin is installed.
  2. Verifies it is not in an error state.
  3. Verifies its version satisfies the constraint.
  4. Boots it first.

Missing, unmet, or circular dependencies isolate only the dependent plugin.

Provides

"provides": [
    "checkout.price.calculate",
    "user.registered",
    "dashboard.referral_panel",
    "admin.menu"
]

provides is documentation and discovery metadata. It does not register anything; your boot() method does. Keep it accurate so operators can see what a plugin touches before activating it.

Use the exact hook, slot, or registry domain name.

Capabilities

"capabilities": ["outbound-https"]

Capabilities declare what a plugin may do outside its directory. They are advisory today; the marketplace review process uses them to decide whether a plugin needs extra scrutiny.

CapabilityMeaning
outbound-httpsMakes outbound HTTPS requests
filesystem-writeWrites files outside its own storage
background-jobsDispatches queue jobs
scheduled-tasksRegisters a scheduler entry
payment-gatewayCharges or refunds money
user-dataStores personal data

Do not declare a capability you do not use. Do not omit one you do.

Legacy name

"legacyName": "MyPlugin"

legacyName preserves compatibility with a ported module:

  • Registers the lowercased legacy view namespace.
  • Registers the lowercased legacy translation namespace.
  • Merges config/<legacy>.php under the legacy config key.
  • Lets plugin_resolve_slug() match the old module name.

Example: hourly-job keeps legacyName: "HourlyJob", so hourlyjob:: views and config('hourlyjob') continue to resolve.

Do not set legacyName on a new plugin.

Alias

"alias": "myplugin"

alias is optional. Core's legacy metadata reader exposes it for plugins that still use the module era naming. New plugins do not need it.

xilancerMetaData

This object holds everything that used to live in module.json.

plugin_type

"xilancerMetaData": {
    "plugin_type": "external"
}

Use external for marketplace plugins. Core uses this to list third-party plugins separately from bundled modules.

hooks

Maps a legacy Blade position to view files:

"hooks": {
    "head_end": ["assets.head"],
    "body_end": ["assets.banner", "announcement.popup"]
}

Supported positions: head_start, head_end, body_start, body_end.

Each entry is a view name inside the plugin's namespace. The renderer strips .blade, .blade.php, and .php, then renders <slug>::<name>. For announcements, announcement.popup resolves to announcements::announcement.popup.

Use these only for assets and global overlays. For page-specific HTML, register a slot instead.

admin_settings.menu_item

"admin_settings": {
    "menu_item": [
        {
            "id": "my-plugin-menu",
            "label": "My Plugin",
            "route": "admin.my-plugin.index",
            "parent": null,
            "icon": "fa-solid fa-star",
            "permissions": "my-plugin-manage",
            "class": "bg-success"
        },
        {
            "id": "my-plugin-settings",
            "label": "Settings",
            "route": "admin.my-plugin.settings",
            "parent": "my-plugin-menu",
            "icon": "fa-solid fa-gear",
            "permissions": "my-plugin-manage",
            "class": "bg-success"
        }
    ]
}
KeyRequiredMeaning
idYesUnique menu identifier. Children reference it as parent.
labelYesSidebar label.
routeYesNamed route. Items with undefined routes are skipped.
parentNoParent menu id, or null for a top-level item.
iconNoFont Awesome class.
permissionsNoPermission slug checked before rendering.
classNoExtra CSS class for the item.

Menus are rendered only for active plugins. A user without the permission does not see the item.

pageBuilderAddon

"pageBuilderAddon": [
    { "addon": "MyPlugin\\PageBuilder\\HeroBlock" }
]

Registers page-builder addon classes. Only ported modules need this. New plugins should use slots.

Validation rules

The validator (App\Services\PluginValidator) checks:

  1. Directory exists.
  2. plugin.json exists and is valid JSON.
  3. Required fields are present and valid.
  4. Entry file exists.
  5. Every PHP file passes php -l.
  6. PHP version satisfies requires.php.
  7. Framework version satisfies requires.framework.
  8. Plugin dependencies are active.
  9. Entry class exists, implements PluginInterface, and its constructor accepts PluginContext.
  10. A sandboxed boot test succeeds.

Warnings do not block activation. Errors do. Common warnings:

  • php -l is unavailable.
  • Composer's autoloader is unavailable, so the boot test is skipped.
  • A manifest declares a capability it does not appear to use.

Manifest anti-patterns

Anti-patternProblem
version: "1.0" with a v prefixFails the numeric dotted rule
Uppercase slugFails the slug rule
entry.class without the namespaceClass not found
autoload mapping to the plugin rootNamespace collisions
Declaring every hook in providesMisleading for operators
Shipping module.jsonIgnored; metadata must be in plugin.json
requires.plugins pointing to itselfCircular dependency error
Menu route that does not existItem silently skipped

Last updated: October 21, 2026

Still stuck?
Our support team is ready to help you get set up.
Get support