Skip to content

Commit

Permalink
add additional tests and features
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle Millloy authored and Kyle Millloy committed Jun 17, 2022
1 parent 4f3c892 commit 0ee8d82
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 19 deletions.
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Emoji

Adds support for inserting and converting emojis into unicode in PHP. Uses enums and so requires PHP8.1 or higher.

## Installation

`composer require itsnubix/emoji`

## Usage

```php
use Emoji\Emoji;
use Emoji\SkinTone;

// Globally set skin tone for all applicable emojis
Emoji::setDefaultSkinTone(SkinTone::medium);

// Various methods for spitting out a unicode emoji
Emoji::get('grinning_face')->toString(); // returns 😀
(string) Emoji::grinning_face(); // returns 😀
echo Emoji::grinningFace(); // returns 😀

// Overwrite the default skin tone on the fly
Emoji::get('waving_hand', SkinTone::light)->toString(); // returns 👋🏻
Emoji::wavingHand()->light()->toString(); // returns 👋🏻
Emoji::wavingHand()->skinTone('light')->toString(); // returns 👋🏻

// Also supports dynamic skin tone selection with a callback
$user = new User(['preferred_skin_tone' => SkinTone::light])
Emoji::wavingHand()
->skinTone(fn() => $user->preferred_skin_tone)
->toString();

// You may also reset
```
34 changes: 27 additions & 7 deletions src/Emoji.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Emoji
*/
public static function __callStatic($method, $arguments)
{
return call_user_func(static::class.'::get', $method, ...$arguments);
return call_user_func(static::class.'::get', static::normalizeEmojiName($method), ...$arguments);
}

/**
Expand All @@ -40,12 +40,12 @@ public static function getDefaultSkinTone()
/**
* Set the global skin tone.
*
* @param \Emoji\Enums\SkinTone $skinTone
* @param null|string|callable|\Emoji\Enum\SkinTone $skinTone
* @return void
*/
public static function setDefaultSkinTone(SkinTone $skinTone): void
public static function setDefaultSkinTone($skinTone): void
{
static::$skinTone = $skinTone;
static::$skinTone = SkinTone::resolve($skinTone);
}

/**
Expand All @@ -54,17 +54,17 @@ public static function setDefaultSkinTone(SkinTone $skinTone): void
* skin tone if required.
*
* @param string $key
* @param null|SkinTone $skinTone
* @param null|string|callable|SkinTone $skinTone
* @return null|\Emoji\Factory
*/
public static function get(string $name, ?SkinTone $skinTone = null): ?Factory
public static function get(string $name, $skinTone = null): ?Factory
{
// check if emoji exists
if (! $emoji = Character::tryFromName($name)) {
return null;
}

return new Factory($emoji, $skinTone ?? static::$skinTone);
return new Factory($emoji, SkinTone::tryToResolve($skinTone) ?? static::getDefaultSkinTone());
}

/**
Expand All @@ -83,4 +83,24 @@ public static function replace(string $haystack): string
$haystack
);
}

/**
* Normalize the name of an emoji into snake_case
*
* @param string $name
* @return string
*/
protected static function normalizeEmojiName(string $name): string
{
if (!\ctype_lower($name)) {
$name = (string) \preg_replace('/\s+/u', '', \ucwords($name));
$name = (string) \mb_strtolower(\preg_replace(
'/(.)(?=[A-Z])/u',
'$1' . ($delimiter ?? '_'),
$name
));
}

return $name;
}
}
11 changes: 11 additions & 0 deletions src/Enums/Character.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ enum Character: string
{
use Enum;

/**
* Available emojis
*
* @see https://unicode.org/emoji/charts/emoji-list.html
*/
case grinning_face = "\u{1F600}"; // 😀
case grinning_face_with_big_eyes = "\u{1F603}"; // 😃
case grinning_face_with_smiling_eyes = "\u{1F604}"; // 😄
Expand Down Expand Up @@ -1862,6 +1867,12 @@ enum Character: string
case flag_scotland = "\u{1F3F4}\u{E0067}\u{E0062}\u{E0073}\u{E0063}\u{E0074}\u{E007F}"; // 🏴󠁧󠁢󠁳󠁣󠁴󠁿
case flag_wales = "\u{1F3F4}\u{E0067}\u{E0062}\u{E0077}\u{E006C}\u{E0073}\u{E007F}"; // 🏴󠁧󠁢󠁷󠁬󠁳󠁿

/**
* A list of the emojis which support changing skin tone
*
* @return bool
* @see https://unicode.org/emoji/charts/full-emoji-modifiers.html
*/
public function supportsSkinTones(): bool
{
return match ($this) {
Expand Down
46 changes: 46 additions & 0 deletions src/Enums/SkinTone.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,61 @@

namespace Emoji\Enums;

use ValueError;
use Emoji\Traits\Enum;

enum SkinTone: string
{
use Enum;

/**
* Available skin tones
*
* @see https://unicode.org/emoji/charts/full-emoji-modifiers.html
*/
case light = "\u{1F3FB}";
case mediumLight = "\u{1F3FC}";
case medium = "\u{1F3FD}";
case mediumDark = "\u{1F3FE}";
case dark = "\u{1F3FF}";

/**
* Attempt to resolve the given value into a defined skin tone
* and throw if no match found.
*
* @param null|string|callable|SkinTone $skinTone
* @return \Emoji\Enums\SkinTone
* @throws ValueError
*/
public static function resolve(string | callable | SkinTone $skinTone)
{
if ($skinTone = static::tryToResolve($skinTone)) {
return $skinTone;
}

throw new ValueError;
}

/**
* Attempt to resolve the given value into a defined skin tone.
*
* @param string|callable|SkinTone $skinTone
* @return null|\Emoji\Enums\SkinTone
*/
public static function tryToResolve(null | string | callable | SkinTone $skinTone)
{
if (is_string($skinTone)) {
$skinTone = SkinTone::fromName($skinTone);
}

if (is_callable($skinTone)) {
$skinTone = static::resolve($skinTone());
}

if ($skinTone instanceof SkinTone) {
return $skinTone;
}

return null;
}
}
61 changes: 52 additions & 9 deletions src/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,77 @@ public function __construct(protected Character $character, protected ?SkinTone
//
}

/**
* When collapsing to string, return the appropriate value
*
* @return string
*/
public function __toString()
{
return $this->toString();
}

/**
* Forward calls onto the set skin tone function
*
* @param string $method
* @param mixed $arguments
* @return mixed
*/
public function __call($method, $arguments)
{
if ($skinTone = SkinTone::fromName($method)) {
return $this->setSkinTone($skinTone);
return $this->skinTone($skinTone);
}
}

public function toString(): string
/**
* Get/set the skin tone for the given emoji
*
* @param null|string|callable|SkinTone $skinTone
* @return mixed
*/
public function skinTone($skinTone = null)
{
$output = $this->character->value;
if (func_num_args() === 0) {
return $this->skinTone;
}

// if the character supports a skin tone...
if ($this->character->supportsSkinTones()) {
$output .= $this->skinTone->value;
if (!is_null($skinTone) && $skinTone = SkinTone::tryToResolve($skinTone)) {
$this->skinTone = $skinTone;
} else {
$this->skinTone = null;
}

return $output;
return $this;
}

public function setSkinTone(SkinTone $skinTone)
/**
* Removes the skin tone data from the factory
*
* @return self
*/
public function toneless(): self
{
$this->skinTone = $skinTone;
$this->skinTone(null);

return $this;
}

/**
* Transfer the emoji to a string
*
* @return string
*/
public function toString(): string
{
$output = $this->character->value;

// if the character supports a skin tone...
if ($this->skinTone && $this->character->supportsSkinTones()) {
$output .= $this->skinTone->value;
}

return $output;
}
}
4 changes: 4 additions & 0 deletions tests/Unit/EmojiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,7 @@

expect(Emoji::replace($string))->toBe($string);
});

it('can convert camel case requests for a method and match with the snake case version', function () {
expect(Emoji::manDancing())->not->toBeNull();
});
20 changes: 20 additions & 0 deletions tests/Unit/Enums/SkinTone.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

use Emoji\Emoji;
use Emoji\Enums\SkinTone;

it('can resolve a skin tone from a string', function () {
foreach (SkinTone::names() as $name) {
expect(SkinTone::tryToResolve($name))->not->toBeNull();
}
});

it('can resolve a skin tone from a callable', function () {
foreach (SkinTone::names() as $name) {
expect(SkinTone::tryToResolve(fn () => $name))->not->toBeNull();
}
});

it('throws if an invalid tone is supplied to the resolve function', function () {
SkinTone::resolve('invalid-tone');
})->throws(ValueError::class);
50 changes: 47 additions & 3 deletions tests/Unit/FactoryTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Emoji\Emoji;
use Emoji\Factory;
use Emoji\Enums\SkinTone;

Expand All @@ -12,7 +13,7 @@
));

foreach (SkinTone::cases() as $skinTone) {
$emoji->setSkinTone($skinTone);
$emoji->skinTone($skinTone);
expect($emoji->toString())->toContain($skinTone->value);
}
});
Expand All @@ -37,8 +38,8 @@
));

foreach (SkinTone::cases() as $skinTone) {
$supported->setSkinTone($skinTone);
$unsupported->setSkinTone($skinTone);
$supported->skinTone($skinTone);
$unsupported->skinTone($skinTone);

expect($supported->toString())->toContain($skinTone->value);
expect($unsupported->toString())->not->toContain($skinTone->value);
Expand All @@ -51,3 +52,46 @@
expect((string) $emoji)->toBe($element->value);
expect($emoji->toString())->toBe($element->value);
});

it('can support a string to set the skin tone if it matches', function () {
$emoji = new Factory(faker()->randomElement(
array_filter(Character::cases(), fn ($character) => $character->supportsSkinTones())
));

foreach (SkinTone::names() as $skinTone) {
$emoji->skinTone($skinTone);
expect($emoji->skinTone())->toBe(SkinTone::fromName($skinTone));
}
});

it('can reset the skin tone', function () {
$emoji = new Factory(faker()->randomElement(
array_filter(Character::cases(), fn ($character) => $character->supportsSkinTones())
));

$emoji->light();
expect($emoji->skinTone())->toBe(SkinTone::light);
$emoji->skinTone(null);
expect($emoji->skinTone())->toBe(null);

$emoji->medium();
expect($emoji->skinTone())->toBe(SkinTone::medium);
$emoji->toneless();
expect($emoji->skinTone())->toBe(null);

$emoji->dark();
expect($emoji->skinTone())->toBe(SkinTone::dark);
$emoji->skinTone(); // using it like this is a getter and shouldn't change it
expect($emoji->skinTone())->toBe(SkinTone::dark);
});

it('can overwrite the global skin tone with a reset', function () {
Emoji::setDefaultSkinTone('light');

$emoji = Emoji::wavingHand();

expect($emoji->skinTone())->toBe(SkinTone::light);

$emoji->toneless();
expect($emoji->skinTone())->toBeNull();
});

0 comments on commit 0ee8d82

Please sign in to comment.