<?phpnamespace App\Chat;/** * The smileys of the 2002 applet (net.spray.chat.a.g::for), found in a segment of text: * * - composed faces: eyes + nose + mouth, ":-)", ";o)", "8-D"... (the nose is required: ":)" stays text); a digit * after it colours the face (":-)2" pink), then "#c" / "#d" a left hand (thumb up / finger), and one more * "c" / "d" a right hand (":-)#cd"); * - "#00".."#14": drawn pictures (bomb, heart...); #02 #04 #13 #14 take a colour digit ("#135"); * - "@00".."@59": the pictures of smiley.gif (3 rows of 20 cells of 32 x 20, assets/smileys/smiley.png), the ones * of the applet's "Smileys" panel. */final class Smileys{ public const EYES = ':;8\'<|[B>%]}{O('; public const NOSES = '*!&-#=>oO$<'; public const MOUTHS = ')(D|OoXxpP/\\*!{}#[$'; public const DEFAULT_COLOR = 10; /** The pictures of smiley.gif: 20 a row, 3 rows. */ public const IMAGES = 60; public const BIG_COUNT = 15; /** * Cuts a text into pieces: strings, and smileys as arrays. * * @return list<string|array{kind: string, eyes?: int, nose?: int, mouth?: int, hands?: int, color?: int, n?: int, source: string}> */ public static function parse(string $text, int $images = self::IMAGES): array { $out = []; $plain = ''; $len = strlen($text); $i = 0; while ($i < $len) { $smiley = self::at($text, $i, $len, $images); if ($smiley === null) { $plain .= $text[$i]; $i++; continue; } if ($plain !== '') { $out[] = $plain; $plain = ''; } $out[] = $smiley; $i += strlen($smiley['source']); } if ($plain !== '') { $out[] = $plain; } return $out; } /** @return array{kind: string, eyes?: int, nose?: int, mouth?: int, hands?: int, color?: int, n?: int, source: string}|null */ private static function at(string $s, int $i, int $len, int $images): ?array { if ($len - $i < 3) { return null; } $e = strpos(self::EYES, $s[$i]); if ($e !== false) { $n = strpos(self::NOSES, $s[$i + 1]); $m = strpos(self::MOUTHS, $s[$i + 2]); if ($n === false || $m === false) { return null; } $extra = 0; $color = self::DEFAULT_COLOR; if ($len - $i >= 4 && ctype_digit($s[$i + 3])) { $color = (int)$s[$i + 3]; $extra = 1; } $hands = 0; if ($len - $i >= 5 + $extra && $s[$i + 3 + $extra] === '#' && in_array($s[$i + 4 + $extra], ['c', 'd'], true)) { $hands |= $s[$i + 4 + $extra] === 'd' ? 1 : 2; $extra += 2; if ($len - $i >= 4 + $extra && in_array($s[$i + 3 + $extra], ['c', 'd'], true)) { $hands |= $s[$i + 3 + $extra] === 'd' ? 4 : 8; $extra += 1; } } return ['kind' => 'face', 'eyes' => $e, 'nose' => $n, 'mouth' => $m, 'color' => $color, 'hands' => $hands, 'source' => substr($s, $i, 3 + $extra)]; } $c = $s[$i]; if (($c !== '#' && $c !== '@') || !ctype_digit(substr($s, $i + 1, 2))) { return null; } $nn = (int)substr($s, $i + 1, 2); if ($c === '#' && $nn < self::BIG_COUNT) { $color = in_array($nn, [13, 14], true) ? 1 : self::DEFAULT_COLOR; $length = 3; if (in_array($nn, [2, 4, 13, 14], true) && $len - $i >= 4 && ctype_digit($s[$i + 3])) { $color = (int)$s[$i + 3]; $length = 4; } return ['kind' => 'big', 'n' => $nn, 'color' => $color, 'source' => substr($s, $i, $length)]; } if ($c === '@' && $nn < $images) { return ['kind' => 'image', 'n' => $nn, 'source' => substr($s, $i, 3)]; } return null; }}