<?php
/**
 * Figma REST API client.
 *
 * Uses wp_remote_get/wp_remote_post for HTTP so it respects WordPress proxy
 * settings and SSL configuration. Implements a simple cooldown between
 * requests to stay under Figma's 30 req/min rate limit.
 */

if ( ! defined( 'ABSPATH' ) ) exit;

class FTE_Figma_Client {

    private string $token;
    private string $base_url = 'https://api.figma.com';
    private float  $last_request_time = 0;
    private int    $min_interval_ms = 2100; // ~28 req/min

    public function __construct( string $token ) {
        $this->token = $token;
    }

    // -----------------------------------------------------------------------
    // Public API
    // -----------------------------------------------------------------------

    /**
     * Get the current user's profile (for token validation).
     *
     * @return array|WP_Error  { id, handle, email, img_url }
     */
    public function get_me() {
        $url = $this->base_url . '/v1/me';
        return $this->request( $url );
    }

    /**
     * Fetch a full Figma file.
     *
     * @param string   $file_key Figma file key.
     * @param int|null $depth    Optional tree depth limit.
     * @return array|WP_Error    Decoded JSON or error.
     */
    public function get_file( string $file_key, ?int $depth = null ) {
        $params = [];
        if ( $depth !== null ) {
            $params['depth'] = $depth;
        }
        $url = $this->base_url . '/v1/files/' . urlencode( $file_key ) . '?' . http_build_query( $params );
        return $this->request( $url );
    }

    /**
     * Fetch specific nodes from a file.
     *
     * @param string   $file_key Figma file key.
     * @param string[] $node_ids Array of node IDs.
     * @return array|WP_Error
     */
    public function get_nodes( string $file_key, array $node_ids ) {
        $params = [
            'ids' => implode( ',', $node_ids ),
        ];
        $url = $this->base_url . '/v1/files/' . urlencode( $file_key ) . '/nodes?' . http_build_query( $params );
        return $this->request( $url );
    }

    /**
     * Export nodes as images.
     *
     * @param string   $file_key Figma file key.
     * @param string[] $node_ids Node IDs to export.
     * @param string   $format   png|svg|jpg|pdf.
     * @param float    $scale    Export scale (1–4).
     * @return array             Map of nodeId => URL.
     */
    public function export_images( string $file_key, array $node_ids, string $format = 'png', float $scale = 2.0 ): array {
        $all_urls = [];

        // Figma caps at 100 IDs per request
        $batches = array_chunk( $node_ids, 100 );

        foreach ( $batches as $batch ) {
            $params = [
                'ids'    => implode( ',', $batch ),
                'format' => $format,
                'scale'  => $scale,
            ];
            $url    = $this->base_url . '/v1/images/' . urlencode( $file_key ) . '?' . http_build_query( $params );
            $result = $this->request( $url );

            if ( is_wp_error( $result ) ) {
                continue; // Skip failed batches, log if needed
            }

            foreach ( ( $result['images'] ?? [] ) as $id => $image_url ) {
                if ( $image_url ) {
                    $all_urls[ $id ] = $image_url;
                }
            }
        }

        return $all_urls;
    }

    /**
     * Get permanent URLs for image fills (uploaded assets).
     *
     * @param string $file_key
     * @return array Map of imageRef => URL.
     */
    public function get_image_fills( string $file_key ): array {
        $url    = $this->base_url . '/v1/files/' . urlencode( $file_key ) . '/images';
        $result = $this->request( $url );

        if ( is_wp_error( $result ) ) {
            return [];
        }

        return $result['meta']['images'] ?? [];
    }

    // -----------------------------------------------------------------------
    // HTTP layer
    // -----------------------------------------------------------------------

    /**
     * @param string $url Full API URL.
     * @return array|WP_Error
     */
    private function request( string $url ) {
        $this->rate_limit_wait();

        $response = wp_remote_get( $url, [
            'headers' => [
                'X-Figma-Token' => $this->token,
                'Content-Type'  => 'application/json',
            ],
            'timeout' => 60,
        ] );

        $this->last_request_time = microtime( true ) * 1000;

        if ( is_wp_error( $response ) ) {
            return $response;
        }

        $code = wp_remote_retrieve_response_code( $response );
        $body = wp_remote_retrieve_body( $response );

        if ( $code < 200 || $code >= 300 ) {
            return new WP_Error(
                'figma_api_error',
                sprintf( 'Figma API returned HTTP %d: %s', $code, substr( $body, 0, 200 ) )
            );
        }

        $decoded = json_decode( $body, true );
        if ( json_last_error() !== JSON_ERROR_NONE ) {
            return new WP_Error( 'figma_json_error', 'Invalid JSON from Figma API' );
        }

        return $decoded;
    }

    /**
     * Simple rate limit: sleep if the last request was less than
     * min_interval_ms ago.
     */
    private function rate_limit_wait(): void {
        if ( $this->last_request_time > 0 ) {
            $now     = microtime( true ) * 1000;
            $elapsed = $now - $this->last_request_time;
            if ( $elapsed < $this->min_interval_ms ) {
                usleep( (int) ( ( $this->min_interval_ms - $elapsed ) * 1000 ) );
            }
        }
    }
}

כתיבת תגובה

האימייל לא יוצג באתר. שדות החובה מסומנים *


Notice: ob_end_flush(): Failed to send buffer of zlib output compression (1) in /home/greenspacecoil/public_html/wp-includes/functions.php on line 5493

Notice: ob_end_flush(): Failed to send buffer of zlib output compression (1) in /home/greenspacecoil/public_html/wp-includes/functions.php on line 5493