253 lines · 8.4 KB
Raw Download
1
<?php
2
// api.php
3
header('Content-Type: application/json');
4
header('Access-Control-Allow-Origin: *');
5
header('Access-Control-Allow-Methods: POST, OPTIONS');
6
header('Access-Control-Allow-Headers: Content-Type');
7
8
// Handle preflight requests
9
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
10
    http_response_code(200);
11
    exit();
12
}
13
14
// Only allow POST requests
15
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
16
    http_response_code(405);
17
    echo json_encode(['error' => 'Method not allowed. Use POST.']);
18
    exit();
19
}
20
21
// Include required files
22
require_once 'functions.php';
23
require_once 'parse_colors.php';
24
require_once 'process_form.php';
25
26
// Set timezone
27
date_default_timezone_set('America/Chicago');
28
$start_time = microtime(true);
29
30
try {
31
    // Get raw input
32
    $raw_input = file_get_contents('php://input');
33
    
34
    // Try to decode as JSON first
35
    $json_data = json_decode($raw_input, true);
36
    
37
    $colors = null;
38
    $contrast_method = 'wcag'; // Default method
39
    $format = 'auto'; // auto, text, json
40
    
41
    if ($json_data !== null && json_last_error() === JSON_ERROR_NONE) {
42
        // Handle JSON input
43
        if (isset($json_data['colors'])) {
44
            if (is_array($json_data['colors'])) {
45
                // Array of colors
46
                $colors = implode("\n", $json_data['colors']);
47
                $format = 'json';
48
            } else {
49
                // String with newlines
50
                $colors = $json_data['colors'];
51
                $format = 'text';
52
            }
53
        }
54
        
55
        if (isset($json_data['contrast_method'])) {
56
            $contrast_method = $json_data['contrast_method'];
57
        }
58
        
59
        if (isset($json_data['format'])) {
60
            $format = $json_data['format'];
61
        }
62
    } else {
63
        // Handle form data or plain text
64
        if (isset($_POST['colors'])) {
65
            $colors = $_POST['colors'];
66
            $format = 'form';
67
            if (isset($_POST['contrast_method'])) {
68
                $contrast_method = $_POST['contrast_method'];
69
            }
70
        } else {
71
            // Treat as plain text input
72
            $colors = $raw_input;
73
            $format = 'text';
74
        }
75
    }
76
    
77
    // Validate input
78
    if (empty($colors)) {
79
        http_response_code(400);
80
        echo json_encode([
81
            'error' => 'No colors provided',
82
            'message' => 'Please provide colors in the request body'
83
        ]);
84
        exit();
85
    }
86
    
87
    // Validate contrast method
88
    $valid_methods = ['wcag', 'apca', 'both'];
89
    if (!in_array($contrast_method, $valid_methods)) {
90
        http_response_code(400);
91
        echo json_encode([
92
            'error' => 'Invalid contrast method',
93
            'message' => 'Valid methods are: ' . implode(', ', $valid_methods)
94
        ]);
95
        exit();
96
    }
97
    
98
    // Process colors
99
    $result = processColorForm($colors);
100
    $parsed_colors = $result['parsed_colors'];
101
    
102
    if (empty($parsed_colors)) {
103
        http_response_code(400);
104
        echo json_encode([
105
            'error' => 'No valid colors found',
106
            'invalid_colors' => $result['invalid_colors'],
107
            'duplicate_colors' => $result['duplicate_colors']
108
        ]);
109
        exit();
110
    }
111
    
112
    // Generate contrast data
113
    $contrast_data = generateContrastData($parsed_colors, $contrast_method);
114
    
115
    // Calculate processing time
116
    $processing_time = microtime(true) - $start_time;
117
    
118
    // Build response
119
    $response = [
120
        'success' => true,
121
        'meta' => [
122
            'colors_processed' => count($parsed_colors),
123
            'contrast_method' => $contrast_method,
124
            'processing_time_seconds' => round($processing_time, 6),
125
            'input_format' => $format,
126
            'timestamp' => date('c')
127
        ],
128
        'colors' => array_keys($parsed_colors),
129
        'contrast_data' => $contrast_data
130
    ];
131
    
132
    // Add warnings if any
133
    $warnings = [];
134
    if (!empty($result['invalid_colors'])) {
135
        $warnings['invalid_colors'] = $result['invalid_colors'];
136
    }
137
    if (!empty($result['duplicate_colors'])) {
138
        $warnings['duplicate_colors'] = $result['duplicate_colors'];
139
    }
140
    if (!empty($result['semantic_duplicates'])) {
141
        $warnings['semantic_duplicates'] = $result['semantic_duplicates'];
142
    }
143
    if ($result['excess_colors']) {
144
        $warnings['excess_colors'] = 'Only first ' . MAX_COLORS . ' colors were processed';
145
    }
146
    
147
    if (!empty($warnings)) {
148
        $response['warnings'] = $warnings;
149
    }
150
    
151
    // Log usage
152
    $datetime = date('Y-m-d h:i:s A');
153
    $log_entry = $datetime . ", " . count($parsed_colors) . " colors, " . $contrast_method . " method, API" . PHP_EOL;
154
    file_put_contents('stats.txt', $log_entry, FILE_APPEND);
155
    
156
    echo json_encode($response, JSON_PRETTY_PRINT);
157
    
158
} catch (Exception $e) {
159
    http_response_code(500);
160
    echo json_encode([
161
        'error' => 'Internal server error',
162
        'message' => $e->getMessage()
163
    ]);
164
}
165
166
/**
167
 * Generate contrast data for all color combinations
168
 */
169
function generateContrastData($parsed_colors, $contrast_method) {
170
    $contrast_data = [];
171
    
172
    foreach ($parsed_colors as $bg_color => $bg_data) {
173
        $background_data = [
174
            'color' => $bg_color,
175
            'rgb' => $bg_data['rgb'],
176
            'alpha' => $bg_data['alpha'],
177
            'luminance' => $bg_data['luminance'],
178
            'combinations' => []
179
        ];
180
        
181
        foreach ($parsed_colors as $fg_color => $fg_data) {
182
            if ($fg_color === $bg_color) continue;
183
            
184
            $combination = [
185
                'foreground_color' => $fg_color,
186
                'foreground_rgb' => $fg_data['rgb'],
187
                'foreground_alpha' => $fg_data['alpha']
188
            ];
189
            
190
            if ($contrast_method === 'wcag') {
191
                $fg_lum = getLuminance($fg_data['rgb'], $fg_data['alpha'], $bg_data['rgb']);
192
                $contrast_ratio = getContrastRatio($bg_data['luminance'], $fg_lum);
193
                $wcag_level = getWCAGLevel($contrast_ratio);
194
                
195
                $combination['wcag'] = [
196
                    'contrast_ratio' => round($contrast_ratio, 2),
197
                    'level' => $wcag_level,
198
                    'passes_aa' => $contrast_ratio >= 4.5,
199
                    'passes_aa_large' => $contrast_ratio >= 3.0,
200
                    'passes_aaa' => $contrast_ratio >= 7.0
201
                ];
202
                
203
            } elseif ($contrast_method === 'apca') {
204
                $apca_contrast = getAPCAContrast($bg_data['rgb'], $fg_data['rgb'], $bg_data['alpha'], $fg_data['alpha']);
205
                $apca_level = getAPCALevel($apca_contrast);
206
                
207
                $combination['apca'] = [
208
                    'contrast_value' => round($apca_contrast, 1),
209
                    'level' => $apca_level,
210
                    'absolute_value' => round(abs($apca_contrast), 1)
211
                ];
212
                
213
            } else { // both
214
                // WCAG
215
                $fg_lum = getLuminance($fg_data['rgb'], $fg_data['alpha'], $bg_data['rgb']);
216
                $wcag_contrast = getContrastRatio($bg_data['luminance'], $fg_lum);
217
                $wcag_level = getWCAGLevel($wcag_contrast);
218
                
219
                // APCA
220
                $apca_contrast = getAPCAContrast($bg_data['rgb'], $fg_data['rgb'], $bg_data['alpha'], $fg_data['alpha']);
221
                $apca_level = getAPCALevel($apca_contrast);
222
                
223
                // Combined
224
                $combined_level = getCombinedLevel($wcag_contrast, $apca_contrast);
225
                
226
                $combination['wcag'] = [
227
                    'contrast_ratio' => round($wcag_contrast, 2),
228
                    'level' => $wcag_level,
229
                    'passes_aa' => $wcag_contrast >= 4.5,
230
                    'passes_aa_large' => $wcag_contrast >= 3.0,
231
                    'passes_aaa' => $wcag_contrast >= 7.0
232
                ];
233
                
234
                $combination['apca'] = [
235
                    'contrast_value' => round($apca_contrast, 1),
236
                    'level' => $apca_level,
237
                    'absolute_value' => round(abs($apca_contrast), 1)
238
                ];
239
                
240
                $combination['combined'] = [
241
                    'level' => $combined_level
242
                ];
243
            }
244
            
245
            $background_data['combinations'][] = $combination;
246
        }
247
        
248
        $contrast_data[] = $background_data;
249
    }
250
    
251
    return $contrast_data;
252
}
253
?>