82 lines
3.5 KiB
PHP
82 lines
3.5 KiB
PHP
<?php
|
|
// api/emoji.php (route: /api/emoji?slug=... or rewrite to /api/emoji/{slug})
|
|
require_once __DIR__.'/../helpers/auth.php';
|
|
require_once __DIR__.'/../helpers/filters.php';
|
|
|
|
// ---- Skin tone helpers (Fitzpatrick modifiers) ----
|
|
if (!function_exists('dw_supports_skin_tone')) {
|
|
function dw_supports_skin_tone(array $emoji): bool {
|
|
// Respect explicit flag if present in source data
|
|
if (isset($emoji['supports_skin_tone'])) {
|
|
return (bool)$emoji['supports_skin_tone'];
|
|
}
|
|
$cat = strtolower(trim($emoji['category'] ?? ''));
|
|
$sub = strtolower(trim($emoji['subcategory'] ?? ''));
|
|
$name = strtolower(trim($emoji['name'] ?? ''));
|
|
// 1) category must be People & Body
|
|
if ($cat !== 'people & body') return false;
|
|
// 2) Exclude faces: many face emojis are not tone-modifiable
|
|
if (preg_match('~\bface\b~', $name) || preg_match('~\bface\b~', $sub)) return false;
|
|
// 3) Allowlist of tone-capable subcategories (based on Unicode charts)
|
|
$allowSubs = [
|
|
'hand-fingers-open','hand-fingers-partial','hand-fingers-closed','hand-single-finger',
|
|
'hands','hand-prop','handshake','body-parts','person','person-gesture','person-activity','person-resting'
|
|
];
|
|
if (in_array($sub, $allowSubs, true) || str_starts_with($sub, 'hand')) return true;
|
|
// Fallback by keywords mentioning body parts / gestures
|
|
$keys = [];
|
|
foreach (['keywords','keywords_en','keywords_id'] as $k) {
|
|
if (!empty($emoji[$k]) && is_array($emoji[$k])) $keys = array_merge($keys, $emoji[$k]);
|
|
}
|
|
$hay = strtolower(' '.implode(' ', $keys).' ');
|
|
return (bool)preg_match('~\b(hand|hands|wave|clap|thumb|finger|palm|point|ear|nose|leg|foot|person|man|woman|boy|girl|salute)\b~', $hay);
|
|
}
|
|
}
|
|
if (!function_exists('dw_skin_tone_modifiers')) {
|
|
function dw_skin_tone_modifiers(): array {
|
|
return ["\u{1F3FB}", "\u{1F3FC}", "\u{1F3FD}", "\u{1F3FE}", "\u{1F3FF}"]; // light .. dark
|
|
}
|
|
}
|
|
if (!function_exists('dw_strip_tone')) {
|
|
function dw_strip_tone(string $emoji): string {
|
|
return preg_replace('/\x{1F3FB}|\x{1F3FC}|\x{1F3FD}|\x{1F3FE}|\x{1F3FF}/u', '', $emoji);
|
|
}
|
|
}
|
|
if (!function_exists('dw_build_tone_variants')) {
|
|
function dw_build_tone_variants(string $emojiBase): array {
|
|
$mods = ["\u{1F3FB}", "\u{1F3FC}", "\u{1F3FD}", "\u{1F3FE}", "\u{1F3FF}"];
|
|
return array_map(fn($m) => $emojiBase.$m, $mods);
|
|
}
|
|
}
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
$tier = detectTier();
|
|
$slug = trim($_GET['slug'] ?? '');
|
|
|
|
$data = json_decode(file_get_contents(__DIR__.'/../data/emojis.json'), true);
|
|
$found = null;
|
|
foreach ($data['emojis'] as $e) {
|
|
if (($e['slug'] ?? '') === $slug) { $found = $e; break; }
|
|
}
|
|
if (!$found) { http_response_code(404); echo json_encode(['error'=>'not_found']); exit; }
|
|
|
|
$out = filterEmoji($found, $tier, false);
|
|
// Add tone support & modifiers for single emoji response
|
|
$out['supports_skin_tone'] = dw_supports_skin_tone($found) || dw_supports_skin_tone($out);
|
|
if (!isset($out['skin_tone_modifiers'])) {
|
|
$out['skin_tone_modifiers'] = dw_skin_tone_modifiers();
|
|
}
|
|
if (!empty($out['supports_skin_tone']) && !empty($out['emoji'])) {
|
|
$base = dw_strip_tone($out['emoji']);
|
|
if ($base !== '') {
|
|
$out['emoji_base'] = $base;
|
|
$out['variants'] = dw_build_tone_variants($base);
|
|
}
|
|
}
|
|
|
|
$etag = '"'.sha1(json_encode([$slug,$tier,$out])).'"';
|
|
header('ETag: '.$etag);
|
|
header('Cache-Control: public, max-age=300');
|
|
if (($_SERVER['HTTP_IF_NONE_MATCH'] ?? '') === $etag) { http_response_code(304); exit; }
|
|
|
|
echo json_encode($out, JSON_UNESCAPED_UNICODE); |