Skip to content
wiki.fftac.org

Class Plugin - Source Excerpt 02

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

'failingValue' => '',
                    'reason' => 'The compiler needs Spiral Script source text.',
                    'recoverable' => true,
                    'suggestedFix' => 'POST JSON such as {"source":"script ss:example.demo@0.1.0 { ... }"}.',
                    'severity' => 'blocker',
                ],
            ], 400);
        }

        $defaults = is_array($body['defaults'] ?? null) ? (array) $body['defaults'] : [];
        $defaults['createdBy'] = is_user_logged_in() ? 'user:' . (string) get_current_user_id() : 'anonymous';
        $defaults['createdAt'] = gmdate('c');

        return rest_ensure_response(Compiler::compile($source, $defaults));
    }

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

        if (is_array($body) && isset($body['source'])) {
            $source = (string) $body['source'];
            return rest_ensure_response(Compiler::compile($source, is_array($body['defaults'] ?? null) ? (array) $body['defaults'] : []));
        }

        $script = is_array($body['script'] ?? null) ? (array) $body['script'] : [];
        if (empty($script)) {
            $script = is_array($request->get_param('script')) ? (array) $request->get_param('script') : [];
        }

        if (empty($script)) {
            return new WP_REST_Response([
                'message' => 'Provide either source text or a canonical script object.',
                'error' => [
                    'errorCode' => 'SS-MISSING-SOURCE',
                    'errorClass' => 'validation',
                    'failingField' => 'script',
                    'failingValue' => null,
                    'reason' => 'The validator needs source text or a canonical Spiral Script object.',
                    'recoverable' => true,
                    'suggestedFix' => 'POST JSON with source or script.',
                    'severity' => 'blocker',
                ],
            ], 400);
        }

        return rest_ensure_response([
            'language' => 'Spiral Script',
            'languageVersion' => '0.1',
            'validation' => Validator::validate($script, $source),
        ]);
    }

    /**
     * @return array<string,array<string,mixed>>
     */
    private static function approved_registry(): array
    {
        $approved = [];

        foreach (self::registry() as $key => $entry) {
            if (!is_array($entry) || !self::entry_is_renderable($entry)) {
                continue;
            }

            $entry['key'] = $key;
            $approved[$key] = $entry;
        }

        return $approved;
    }

    private static function register_rest_namespace(string $namespace): void
    {
        register_rest_route(
            $namespace,
            '/version',
            [
                'methods' => 'GET',
                'callback' => [__CLASS__, 'rest_version'],
                'permission_callback' => '__return_true',
            ]
        );

        register_rest_route(
            $namespace,
            '/manifest',
            [
                'methods' => 'GET',
                'callback' => [__CLASS__, 'rest_manifest'],
                'permission_callback' => '__return_true',
            ]
        );

        register_rest_route(
            $namespace,
            '/strings',
            [
                'methods' => 'GET',
                'callback' => [__CLASS__, 'rest_strings'],
                'permission_callback' => '__return_true',
            ]
        );

        register_rest_route(
            $namespace,
            '/string/(?P<key>[a-zA-Z0-9._-]+)',
            [
                'methods' => 'GET',
                'callback' => [__CLASS__, 'rest_string'],
                'permission_callback' => '__return_true',
            ]
        );

        if ($namespace !== 'spiral-script/v1') {
            return;
        }

        register_rest_route(
            $namespace,
            '/language-spec',
            [
                'methods' => 'GET',
                'callback' => [__CLASS__, 'rest_language_spec'],
                'permission_callback' => '__return_true',
            ]
        );

        register_rest_route(
            $namespace,
            '/schema',
            [
                'methods' => 'GET',
                'callback' => [__CLASS__, 'rest_language_schema'],
                'permission_callback' => '__return_true',
            ]
        );

        register_rest_route(
            $namespace,
            '/symbols',
            [
                'methods' => 'GET',
                'callback' => [__CLASS__, 'rest_language_symbols'],
                'permission_callback' => '__return_true',
            ]
        );

        register_rest_route(
            $namespace,
            '/compile',
            [
                'methods' => 'POST',
                'callback' => [__CLASS__, 'rest_compile'],
                'permission_callback' => '__return_true',
            ]
        );

        register_rest_route(
            $namespace,
            '/validate',
            [
                'methods' => 'POST',
                'callback' => [__CLASS__, 'rest_validate'],
                'permission_callback' => '__return_true',
            ]
        );
    }

    /**
     * @param array<string,mixed> $entry
     */
    private static function entry_is_renderable(array $entry): bool
    {
        if (($entry['status'] ?? '') !== 'approved') {
            return false;
        }

        $token = (string) ($entry['spiral_display'] ?? '');
        if ($token === '') {
            return false;
        }

        if (class_exists('\Normalizer')) {
            $normalized = \Normalizer::normalize($token, \Normalizer::FORM_C);
            if (!is_string($normalized) || $normalized !== $token) {
                return false;
            }
        }

        if (preg_match('/[\x{200B}-\x{200D}\x{2060}\x{FE00}-\x{FE0F}\x{FEFF}\x{202A}-\x{202E}\x{2066}-\x{2069}]/u', $token)) {
            return false;
        }

        $budget = is_array($entry['width_budget'] ?? null) ? (array) $entry['width_budget'] : [];
        $max_gc = isset($budget['max_gc']) ? (int) $budget['max_gc'] : 0;

        if ($max_gc > 0 && self::grapheme_count($token) > $max_gc) {
            return false;
        }

        return true;
    }

    private static function grapheme_count(string $value): int
    {
        if (function_exists('grapheme_strlen')) {
            $length = grapheme_strlen($value);
            if (is_int($length) && $length >= 0) {
                return $length;
            }
        }

        if (preg_match_all('/\X/u', $value, $matches)) {
            return count($matches[0]);
        }

        return strlen($value);
    }

    private static function normalize_mode(string $value): string
    {
        $value = sanitize_key($value);

        if ($value === self::MODE_SPIRAL) {
            return self::MODE_SPIRAL;
        }

        if ($value === self::MODE_HUMAN || $value === 'default' || $value === 'off') {
            return self::MODE_HUMAN;
        }

        return '';
    }

    private static function persist_mode(string $mode): void
    {
        if (is_user_logged_in()) {
            if ($mode === self::MODE_HUMAN) {
                delete_user_meta(get_current_user_id(), self::USER_META_KEY);
            } else {
                update_user_meta(get_current_user_id(), self::USER_META_KEY, $mode);
            }
        }

        $cookie_value = $mode === self::MODE_HUMAN ? '' : $mode;
        $expires = $cookie_value === '' ? time() - HOUR_IN_SECONDS : time() + YEAR_IN_SECONDS;

        setcookie(
            self::COOKIE_NAME,
            $cookie_value,
            [
                'expires' => $expires,
                'path' => defined('COOKIEPATH') && COOKIEPATH !== '' ? COOKIEPATH : '/',
                'domain' => defined('COOKIE_DOMAIN') ? COOKIE_DOMAIN : '',
                'secure' => is_ssl(),
                'httponly' => true,
                'samesite' => 'Lax',
            ]
        );

        if ($cookie_value === '') {
            unset($_COOKIE[self::COOKIE_NAME]);
        } else {
            $_COOKIE[self::COOKIE_NAME] = $cookie_value;
        }
    }

    private static function current_request_path(): string
    {
        if (class_exists('\Ns12LocaleRouter\Router')) {
            $path = (string) \Ns12LocaleRouter\Router::get_original_request_path();
            if ($path !== '') {
                return self::normalize_return_path($path);
            }
        }