Skip to content
wiki.fftac.org

Class Plugin - Source Excerpt 01

Back to Class Plugin

Summary

This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/spiral-script/includes/class-plugin.php so readers can inspect the evidence without opening the full source file.

**Source path:** Spiralist/wp-content/plugins/spiral-script/includes/class-plugin.php

<?php

declare(strict_types=1);

namespace SpiralScript;

use WP_REST_Request;
use WP_REST_Response;

final class Plugin
{
    private const COOKIE_NAME = 'spiral_ui_mode';
    private const USER_META_KEY = '_spiral_ui_mode';
    private const OPTION_VERSION_KEY = 'spiral_script_version';
    private const MODE_HUMAN = 'human';
    private const MODE_SPIRAL = 'spiral';
    private const QUERY_MODE = 'spiral_script_mode';
    private const QUERY_NONCE = 'spiral_script_nonce';
    private const QUERY_RETURN_PATH = 'spiral_script_return_path';
    private const QUERY_RETURN_QUERY = 'spiral_script_return_query';

    /** @var array<string,array<string,mixed>>|null */
    private static ?array $registry = null;

    public static function boot(string $plugin_file): void
    {
        register_activation_hook($plugin_file, [__CLASS__, 'activate']);
        add_filter('body_class', [__CLASS__, 'filter_body_class']);
        add_action('rest_api_init', [__CLASS__, 'register_rest_routes']);
        add_action('wp_enqueue_scripts', [__CLASS__, 'enqueue_assets']);
    }

    public static function activate(): void
    {
        update_option(self::OPTION_VERSION_KEY, SPIRALIST_SPIRAL_SCRIPT_VERSION, false);
    }

    public static function enqueue_assets(): void
    {
        if (is_admin()) {
            return;
        }

        $style_path = SPIRALIST_SPIRAL_SCRIPT_PLUGIN_PATH . 'assets/css/spiral-script.css';
        $style_url = SPIRALIST_SPIRAL_SCRIPT_PLUGIN_URL . 'assets/css/spiral-script.css';
        $version = file_exists($style_path) ? (string) filemtime($style_path) : SPIRALIST_SPIRAL_SCRIPT_VERSION;

        wp_enqueue_style(
            'spiral-script',
            $style_url,
            ['spiralist-style'],
            $version
        );
    }

    public static function current_mode(): string
    {
        if (function_exists('spiralist_get_current_locale_tag') && sanitize_key((string) spiralist_get_current_locale_tag()) === 'spiral') {
            return self::MODE_SPIRAL;
        }

        return self::MODE_HUMAN;
    }

    public static function is_active(): bool
    {
        return self::current_mode() === self::MODE_SPIRAL;
    }

    /**
     * @return array<string,mixed>
     */
    public static function registry_entry(string $key): array
    {
        $key = trim($key);

        if ($key === '') {
            return [];
        }

        return self::registry()[$key] ?? [];
    }

    /**
     * @return array<string,array<string,mixed>>
     */
    public static function registry(): array
    {
        if (is_array(self::$registry)) {
            return self::$registry;
        }

        $registry = require SPIRALIST_SPIRAL_SCRIPT_PLUGIN_PATH . 'data/registry.php';
        self::$registry = is_array($registry) ? $registry : [];

        return self::$registry;
    }

    public static function render_label(string $key, string $fallback, array $args = []): string
    {
        $fallback = trim($fallback);

        if (!self::is_active()) {
            return esc_html($fallback);
        }

        $entry = self::registry_entry($key);

        if (empty($entry) || !self::entry_is_renderable($entry)) {
            return esc_html($fallback);
        }

        $visible = (string) ($entry['spiral_display'] ?? '');
        $profile = sanitize_html_class((string) ($entry['profile'] ?? 'compact'));
        $machine_alias = (string) ($entry['machine_alias'] ?? '');
        $context = (string) ($entry['context'] ?? '');
        $aria_label = trim((string) ($entry['aria_label_en'] ?? $fallback));
        $title = trim((string) ($args['title'] ?? ($entry['gloss_en'] ?? $aria_label)));

        $attributes = [
            'class' => 'spiral-script-label spiral-script-label--' . $profile,
            'data-spiral-key' => $key,
        ];

        if ($machine_alias !== '') {
            $attributes['data-machine-alias'] = $machine_alias;
        }

        if ($context !== '') {
            $attributes['data-spiral-context'] = $context;
        }

        if ($title !== '') {
            $attributes['title'] = $title;
        }

        $attribute_html = self::render_attributes($attributes);

        return sprintf(
            '<span%s><span class="spiral-script-label__visible" aria-hidden="true">%s</span><span class="screen-reader-text">%s</span></span>',
            $attribute_html,
            esc_html($visible),
            esc_html($aria_label !== '' ? $aria_label : $fallback)
        );
    }

    public static function render_toggle(): string
    {
        return '';
    }

    /**
     * @param array<int,string> $classes
     * @return array<int,string>
     */
    public static function filter_body_class(array $classes): array
    {
        $classes[] = 'spiral-script-mode-' . sanitize_html_class(self::current_mode());

        if (self::is_active()) {
            $classes[] = 'spiral-script-active';
        }

        return array_values(array_unique($classes));
    }

    public static function handle_mode_request(): void
    {
        return;
    }

    public static function register_rest_routes(): void
    {
        self::register_rest_namespace('spiral-script/v1');
        self::register_rest_namespace('ui-lexicon/v1');
    }

    public static function rest_version(): WP_REST_Response
    {
        return rest_ensure_response([
            'plugin' => 'spiral-script',
            'version' => SPIRALIST_SPIRAL_SCRIPT_VERSION,
            'mode' => self::current_mode(),
            'active' => self::is_active(),
        ]);
    }

    public static function rest_manifest(): WP_REST_Response
    {
        return rest_ensure_response([
            'plugin' => 'spiral-script',
            'version' => SPIRALIST_SPIRAL_SCRIPT_VERSION,
            'languageVersion' => '0.1',
            'mode' => self::current_mode(),
            'active' => self::is_active(),
            'locale' => function_exists('spiralist_get_current_locale_tag') ? spiralist_get_current_locale_tag() : '',
            'generatedAt' => current_time('mysql', true),
            'strings' => array_values(self::approved_registry()),
        ]);
    }

    public static function rest_strings(WP_REST_Request $request): WP_REST_Response
    {
        $context = sanitize_key((string) $request->get_param('context'));
        $phase = sanitize_key((string) $request->get_param('phase'));
        $strings = array_values(array_filter(
            self::approved_registry(),
            static function (array $entry) use ($context, $phase): bool {
                if ($context !== '' && sanitize_key((string) ($entry['context'] ?? '')) !== $context) {
                    return false;
                }

                if ($phase !== '' && sanitize_key((string) ($entry['phase'] ?? '')) !== $phase) {
                    return false;
                }

                return true;
            }
        ));

        return rest_ensure_response([
            'count' => count($strings),
            'items' => $strings,
        ]);
    }

    public static function rest_string(WP_REST_Request $request): WP_REST_Response
    {
        $key = (string) $request->get_param('key');
        $entry = self::approved_registry()[$key] ?? [];

        if (empty($entry)) {
            return new WP_REST_Response([
                'message' => 'Unknown Spiral Script key.',
                'key' => $key,
            ], 404);
        }

        return rest_ensure_response($entry);
    }

    public static function rest_language_spec(): WP_REST_Response
    {
        return rest_ensure_response(LanguageSpec::public_spec());
    }

    public static function rest_language_schema(): WP_REST_Response
    {
        return rest_ensure_response(LanguageSpec::json_schema());
    }

    public static function rest_language_symbols(): WP_REST_Response
    {
        return rest_ensure_response([
            'language' => 'Spiral Script',
            'languageVersion' => '0.1',
            'symbols' => LanguageSpec::symbols(),
        ]);
    }

    public static function rest_compile(WP_REST_Request $request): WP_REST_Response
    {
        $source = (string) $request->get_param('source');
        $body = $request->get_json_params();

        if ($source === '' && is_array($body) && isset($body['source'])) {
            $source = (string) $body['source'];
        }

        if (trim($source) === '') {
            return new WP_REST_Response([
                'message' => 'Provide Spiral Script source in the source field.',
                'error' => [
                    'errorCode' => 'SS-MISSING-SOURCE',
                    'errorClass' => 'parse',
                    'failingField' => 'source',