193 lines
7.1 KiB
PHP
193 lines
7.1 KiB
PHP
<?php
|
|
namespace App\Support\Modules;
|
|
|
|
final class ModuleManifest
|
|
{
|
|
/**
|
|
* @param array<string, mixed> $manifest
|
|
* @return array<string, mixed>
|
|
*/
|
|
public static function normalize(array $manifest): array
|
|
{
|
|
$normalized = $manifest;
|
|
|
|
$name = trim((string) ($manifest['name'] ?? ''));
|
|
$normalized['name'] = $name;
|
|
$normalized['display_name'] = self::normalizeNullableString($manifest['display_name'] ?? null) ?? self::normalizeNullableString($manifest['title'] ?? null) ?? $name;
|
|
$normalized['description'] = self::normalizeNullableString($manifest['description'] ?? null);
|
|
$normalized['version'] = self::normalizeNullableString($manifest['version'] ?? null) ?? '0.0.0';
|
|
$normalized['author'] = self::normalizeNullableString($manifest['author'] ?? null);
|
|
$normalized['license'] = self::normalizeNullableString($manifest['license'] ?? null) ?? 'free';
|
|
$normalized['category'] = self::normalizeNullableString($manifest['category'] ?? null);
|
|
$normalized['dependencies'] = self::normalizeStringList($manifest['dependencies'] ?? []);
|
|
$normalized['optional_dependencies'] = self::normalizeStringList($manifest['optional_dependencies'] ?? []);
|
|
$normalized['hooks'] = self::normalizeStringList($manifest['hooks'] ?? []);
|
|
$normalized['features'] = self::normalizeStringList($manifest['features'] ?? []);
|
|
$normalized['permissions'] = self::normalizePermissions($manifest['permissions'] ?? []);
|
|
$normalized['menu_items'] = self::normalizeMenuItems($manifest['menu_items'] ?? []);
|
|
$normalized['requirements'] = is_array($manifest['requirements'] ?? null) ? $manifest['requirements'] : [];
|
|
$normalized['config'] = is_array($manifest['config'] ?? null) ? $manifest['config'] : [];
|
|
$normalized['manifest_version'] = (int) ($manifest['manifest_version'] ?? 1);
|
|
|
|
return $normalized;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $value
|
|
*/
|
|
private static function normalizeNullableString(mixed $value): ?string
|
|
{
|
|
if (! is_scalar($value)) {
|
|
return null;
|
|
}
|
|
|
|
$normalized = trim((string) $value);
|
|
|
|
return $normalized !== '' ? $normalized : null;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $value
|
|
* @return array<int, string>
|
|
*/
|
|
private static function normalizeStringList(mixed $value): array
|
|
{
|
|
if (! is_array($value)) {
|
|
return [];
|
|
}
|
|
|
|
$normalized = [];
|
|
foreach ($value as $item) {
|
|
$item = self::normalizeNullableString($item);
|
|
if ($item !== null) {
|
|
$normalized[] = $item;
|
|
}
|
|
}
|
|
|
|
return array_values(array_unique($normalized));
|
|
}
|
|
|
|
/**
|
|
* @param mixed $value
|
|
* @return array<int, array{name:string,display_name:string,description:?string,guard_name:string}>
|
|
*/
|
|
private static function normalizePermissions(mixed $value): array
|
|
{
|
|
if (! is_array($value)) {
|
|
return [];
|
|
}
|
|
|
|
$permissions = [];
|
|
|
|
if (self::isAssociative($value)) {
|
|
foreach ($value as $name => $description) {
|
|
$permissionName = self::normalizeNullableString($name);
|
|
if ($permissionName === null) {
|
|
continue;
|
|
}
|
|
|
|
$display = self::normalizeNullableString($description) ?? self::humanizePermissionName($permissionName);
|
|
|
|
$permissions[] = [
|
|
'name' => $permissionName,
|
|
'display_name' => $display,
|
|
'description' => self::normalizeNullableString($description) ?? $display,
|
|
'guard_name' => 'web',
|
|
];
|
|
}
|
|
|
|
return $permissions;
|
|
}
|
|
|
|
foreach ($value as $permission) {
|
|
if (is_string($permission)) {
|
|
$permissionName = self::normalizeNullableString($permission);
|
|
if ($permissionName === null) {
|
|
continue;
|
|
}
|
|
|
|
$permissions[] = [
|
|
'name' => $permissionName,
|
|
'display_name' => self::humanizePermissionName($permissionName),
|
|
'description' => null,
|
|
'guard_name' => 'web',
|
|
];
|
|
|
|
continue;
|
|
}
|
|
|
|
if (! is_array($permission)) {
|
|
continue;
|
|
}
|
|
|
|
$permissionName = self::normalizeNullableString($permission['name'] ?? null);
|
|
if ($permissionName === null) {
|
|
continue;
|
|
}
|
|
|
|
$permissions[] = [
|
|
'name' => $permissionName,
|
|
'display_name' => self::normalizeNullableString($permission['display_name'] ?? null) ?? self::humanizePermissionName($permissionName),
|
|
'description' => self::normalizeNullableString($permission['description'] ?? null),
|
|
'guard_name' => self::normalizeNullableString($permission['guard_name'] ?? null) ?? 'web',
|
|
];
|
|
}
|
|
|
|
return $permissions;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $value
|
|
* @return array<int, array<string, mixed>>
|
|
*/
|
|
private static function normalizeMenuItems(mixed $value): array
|
|
{
|
|
if (! is_array($value)) {
|
|
return [];
|
|
}
|
|
|
|
$menuItems = [];
|
|
foreach ($value as $item) {
|
|
if (! is_array($item)) {
|
|
continue;
|
|
}
|
|
|
|
$title = self::normalizeNullableString($item['title'] ?? null);
|
|
$route = self::normalizeNullableString($item['route'] ?? null);
|
|
if ($title === null || $route === null) {
|
|
continue;
|
|
}
|
|
|
|
$menuItems[] = [
|
|
'title' => $title,
|
|
'route' => $route,
|
|
'icon' => self::normalizeNullableString($item['icon'] ?? null),
|
|
'permission' => self::normalizeNullableString($item['permission'] ?? null),
|
|
'parent' => self::normalizeNullableString($item['parent'] ?? null),
|
|
'description' => self::normalizeNullableString($item['description'] ?? null),
|
|
'order' => isset($item['order']) && is_numeric($item['order']) ? (int) $item['order'] : 0,
|
|
'badge' => $item['badge'] ?? null,
|
|
];
|
|
}
|
|
|
|
usort($menuItems, static fn(array $left, array $right): int => ($left['order'] ?? 0) <=> ($right['order'] ?? 0));
|
|
|
|
return $menuItems;
|
|
}
|
|
|
|
/**
|
|
* @param array<mixed> $value
|
|
*/
|
|
private static function isAssociative(array $value): bool
|
|
{
|
|
return array_keys($value) !== range(0, count($value) - 1);
|
|
}
|
|
|
|
private static function humanizePermissionName(string $permissionName): string
|
|
{
|
|
$label = str_replace(['.', '_', '-'], ' ', $permissionName);
|
|
|
|
return ucfirst(trim(preg_replace('/\s+/', ' ', $label) ?? $label));
|
|
}
|
|
}
|