Skip to content

Commit

Permalink
Merge pull request #5 from Astrotomic/issue-3
Browse files Browse the repository at this point in the history
#3 add ability to replace multiple emojis in plain text
  • Loading branch information
Gummibeer authored May 11, 2021
2 parents f6cedad + 01fedd4 commit 3d16837
Show file tree
Hide file tree
Showing 23 changed files with 270 additions and 27 deletions.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ composer require astrotomic/php-twemoji

## Usage

### Single Emojis

You can use the `Twemoji::emoji()` method to get the Twemoji image URL for a single emoji.

```php
use Astrotomic\Twemoji\Twemoji;

Expand All @@ -34,6 +38,39 @@ Twemoji::emoji('πŸŽ‰')->base('https://twemoji.astrotomic.info')->url();
// https://twemoji.astrotomic.info/svg/1f389.svg
```

### Multiple Emojis in Text

If you have a text and want to replace all emojis with Twemoji image tags (Markdown or HTML) you can use the `Twemoji::text()` method.
This isn't aware of emojis in attributes or anything - it just finds and replaces all Emojis in the given string.

```php
use Astrotomic\Twemoji\Twemoji;

Twemoji::text("Hello πŸ‘‹πŸΏ")->toMarkdown();
// Hello ![πŸ‘‹πŸΏ](https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3ff.svg)

Twemoji::text("Hello πŸ‘‹πŸΏ")->png()->toMarkdown();
// Hello ![πŸ‘‹πŸΏ](https://twemoji.maxcdn.com/v/latest/72x72/1f44b-1f3ff.png)
```

In case you want to configure the replacer once and bind it to your container for example you can do that as well.

```php
use Astrotomic\Twemoji\Replacer;

$replacer = (new Replacer())->png();

$replacer->text("Hello πŸ‘‹πŸΏ")->toMarkdown();
// Hello ![πŸ‘‹πŸΏ](https://twemoji.maxcdn.com/v/latest/72x72/1f44b-1f3ff.png)
```

You can also override the replacer configuration for the specific replace operation without altering the replacer configuration.

```php
$replacer->text("Hello πŸ‘‹πŸΏ")->svg()->toMarkdown();
// Hello ![πŸ‘‹πŸΏ](https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3ff.svg)
```

## Testing

```bash
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
},
"require-dev": {
"pestphp/pest": "^0.3.0",
"spatie/emoji": "^2.2.0"
"spatie/emoji": "^2.3.0",
"spatie/pest-plugin-snapshots": "^1.0"
},
"suggest": {
"spatie/emoji": "*"
Expand Down
40 changes: 40 additions & 0 deletions src/Concerns/Configurable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace Astrotomic\Twemoji\Concerns;

use Astrotomic\Twemoji\Twemoji;

trait Configurable
{
protected string $type = Twemoji::SVG;

protected string $base = 'https://twemoji.maxcdn.com/v/latest';

public function base(string $base): self
{
$this->base = rtrim($base, '/');

return $this;
}

public function type(string $type): self
{
$this->type = $type;

return $this;
}

public function svg(): self
{
$this->type = Twemoji::SVG;

return $this;
}

public function png(): self
{
$this->type = Twemoji::PNG;

return $this;
}
}
74 changes: 74 additions & 0 deletions src/EmojiText.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace Astrotomic\Twemoji;

use Astrotomic\Twemoji\Concerns\Configurable;
use Closure;

/**
* @internal
*/
class EmojiText
{
use Configurable;

protected string $text;

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

public function toMarkdown(?Closure $alt = null): string
{
return $this->replace('![%{alt}](%{src})', $alt);
}

public function toHtml(?Closure $alt = null, array $attributes = []): string
{
$attributes = array_merge([
'width' => 72,
'height' => 72,
'loading' => 'lazy',
'class' => 'twemoji',
], $attributes);

$attrs = implode(' ', array_map(
fn (string $key, string $value): string => "{$key}=\"{$value}\"",
array_keys($attributes),
array_values($attributes)
));

return $this->replace('<img src="%{src}" alt="%{alt}" '.$attrs.' />', $alt);
}

protected function replace(string $replacement, ?Closure $alt = null): string
{
$text = $this->text;

$text = preg_replace_callback(
$this->regexp(),
fn (array $matches): string => str_replace(
['%{alt}', '%{src}'],
[
$alt
? $alt($matches[0])
: $matches[0],
Twemoji::emoji($matches[0])
->base($this->base)
->type($this->type)
->url(),
],
$replacement
),
$text
);

return $text;
}

protected function regexp(): string
{
return '/(?:'.json_decode(file_get_contents(dirname(__FILE__).'/regexp.json')).')/u';
}
}
17 changes: 17 additions & 0 deletions src/Replacer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Astrotomic\Twemoji;

use Astrotomic\Twemoji\Concerns\Configurable;

class Replacer
{
use Configurable;

public function text(string $text): EmojiText
{
return (new EmojiText($text))
->base($this->base)
->type($this->type);
}
}
31 changes: 7 additions & 24 deletions src/Twemoji.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

namespace Astrotomic\Twemoji;

use Astrotomic\Twemoji\Concerns\Configurable;
use JsonSerializable;

class Twemoji implements JsonSerializable
{
protected const SVG = 'svg';
protected const PNG = 'png';
use Configurable;

public const SVG = 'svg';
public const PNG = 'png';

/** @var string[] */
protected array $codepoints;

protected string $type = self::SVG;

protected string $base = 'https://twemoji.maxcdn.com/v/latest';

/**
* @param string[] $codepoints
*/
Expand All @@ -38,25 +37,9 @@ public static function emoji(string $emoji): self
return new static($normalized);
}

public function base(string $base): self
{
$this->base = rtrim($base, '/');

return $this;
}

public function svg(): self
{
$this->type = self::SVG;

return $this;
}

public function png(): self
public static function text(string $text): EmojiText
{
$this->type = self::PNG;

return $this;
return new EmojiText($text);
}

public function url(): string
Expand Down
1 change: 1 addition & 0 deletions src/regexp.json

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions tests/Datasets/SpatieEmojis.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
use Spatie\Emoji\Emoji;

dataset('spatie-emojis', function () {
$class = new ReflectionClass(Emoji::class);
foreach ($class->getConstants() as $name => $emoji) {
foreach (Emoji::all() as $name => $emoji) {
yield [$emoji, $name];
}
});
52 changes: 52 additions & 0 deletions tests/Unit/ReplacerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

use Astrotomic\Twemoji\Replacer;
use function Spatie\Snapshots\assertMatchesTextSnapshot;

it('can replace emojis in plain text to markdown', function () {
$replacer = new Replacer();
assertMatchesTextSnapshot($replacer->text("Hello \u{1F44B},\nEmojis are so cool! πŸš€πŸŽ‰")->toMarkdown());
});

it('can replace emojis in plain text to markdown using png', function () {
$replacer = new Replacer();
$replacer->png();
assertMatchesTextSnapshot($replacer->text("Hello \u{1F44B},\nEmojis are so cool! πŸš€πŸŽ‰")->toMarkdown());
assertMatchesTextSnapshot($replacer->text("Hello \u{1F44B},\nEmojis are so cool! πŸŽ‰πŸš€")->toMarkdown());
});

it('can replace emojis in plain text to markdown using png once', function () {
$replacer = new Replacer();
assertMatchesTextSnapshot($replacer->text("Hello \u{1F44B},\nEmojis are so cool! πŸš€πŸŽ‰")->png()->toMarkdown());
assertMatchesTextSnapshot($replacer->text("Hello \u{1F44B},\nEmojis are so cool! πŸŽ‰πŸš€")->toMarkdown());
});

it('can replace emojis in plain text to html', function () {
$replacer = new Replacer();
assertMatchesTextSnapshot($replacer->text("Hello \u{1F44B},\nEmojis are so cool! πŸš€πŸŽ‰")->toHtml());
});

it('can replace emojis in plain text to html using png', function () {
$replacer = new Replacer();
$replacer->png();
assertMatchesTextSnapshot($replacer->text("Hello \u{1F44B},\nEmojis are so cool! πŸš€πŸŽ‰")->toHtml());
assertMatchesTextSnapshot($replacer->text("Hello \u{1F44B},\nEmojis are so cool! πŸŽ‰πŸš€")->toHtml());
});

it('can replace emojis in plain text to html using png once', function () {
$replacer = new Replacer();
assertMatchesTextSnapshot($replacer->text("Hello \u{1F44B},\nEmojis are so cool! πŸš€πŸŽ‰")->png()->toHtml());
assertMatchesTextSnapshot($replacer->text("Hello \u{1F44B},\nEmojis are so cool! πŸŽ‰πŸš€")->toHtml());
});

it('can replace multi codepoint emojis in plain text', function () {
$replacer = new Replacer();
assertMatchesTextSnapshot($replacer->text(implode(PHP_EOL, [
'Hello πŸ‘‹',
'Hello πŸ‘‹πŸ»',
'Hello πŸ‘‹πŸΌ',
'Hello πŸ‘‹πŸ½',
'Hello πŸ‘‹πŸΎ',
'Hello πŸ‘‹πŸΏ',
]))->toMarkdown());
});
9 changes: 9 additions & 0 deletions tests/Unit/TwemojiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use Astrotomic\Twemoji\Twemoji;
use function PHPUnit\Framework\assertEquals;
use function PHPUnit\Framework\assertMatchesRegularExpression;
use function Spatie\Snapshots\assertMatchesTextSnapshot;

it('can generate url', function (string $emoji, string $twemoji) {
assertEquals(
Expand Down Expand Up @@ -38,3 +39,11 @@
Twemoji::emoji($emoji)->url()
);
})->with('spatie-emojis');

it('can replace emojis in plain text to markdown', function () {
assertMatchesTextSnapshot(Twemoji::text("Hello \u{1F44B},\nEmojis are so cool! πŸš€πŸŽ‰")->toMarkdown());
});

it('can replace emojis in plain text to html', function () {
assertMatchesTextSnapshot(Twemoji::text("Hello \u{1F44B},\nEmojis are so cool! πŸš€πŸŽ‰")->toHtml());
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello <img src="https://twemoji.maxcdn.com/v/latest/svg/1f44b.svg" alt="πŸ‘‹" width="72" height="72" loading="lazy" class="twemoji" />,
Emojis are so cool! <img src="https://twemoji.maxcdn.com/v/latest/svg/1f680.svg" alt="πŸš€" width="72" height="72" loading="lazy" class="twemoji" /><img src="https://twemoji.maxcdn.com/v/latest/svg/1f389.svg" alt="πŸŽ‰" width="72" height="72" loading="lazy" class="twemoji" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello <img src="https://twemoji.maxcdn.com/v/latest/72x72/1f44b.png" alt="πŸ‘‹" width="72" height="72" loading="lazy" class="twemoji" />,
Emojis are so cool! <img src="https://twemoji.maxcdn.com/v/latest/72x72/1f680.png" alt="πŸš€" width="72" height="72" loading="lazy" class="twemoji" /><img src="https://twemoji.maxcdn.com/v/latest/72x72/1f389.png" alt="πŸŽ‰" width="72" height="72" loading="lazy" class="twemoji" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello <img src="https://twemoji.maxcdn.com/v/latest/72x72/1f44b.png" alt="πŸ‘‹" width="72" height="72" loading="lazy" class="twemoji" />,
Emojis are so cool! <img src="https://twemoji.maxcdn.com/v/latest/72x72/1f389.png" alt="πŸŽ‰" width="72" height="72" loading="lazy" class="twemoji" /><img src="https://twemoji.maxcdn.com/v/latest/72x72/1f680.png" alt="πŸš€" width="72" height="72" loading="lazy" class="twemoji" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello <img src="https://twemoji.maxcdn.com/v/latest/72x72/1f44b.png" alt="πŸ‘‹" width="72" height="72" loading="lazy" class="twemoji" />,
Emojis are so cool! <img src="https://twemoji.maxcdn.com/v/latest/72x72/1f680.png" alt="πŸš€" width="72" height="72" loading="lazy" class="twemoji" /><img src="https://twemoji.maxcdn.com/v/latest/72x72/1f389.png" alt="πŸŽ‰" width="72" height="72" loading="lazy" class="twemoji" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello <img src="https://twemoji.maxcdn.com/v/latest/svg/1f44b.svg" alt="πŸ‘‹" width="72" height="72" loading="lazy" class="twemoji" />,
Emojis are so cool! <img src="https://twemoji.maxcdn.com/v/latest/svg/1f389.svg" alt="πŸŽ‰" width="72" height="72" loading="lazy" class="twemoji" /><img src="https://twemoji.maxcdn.com/v/latest/svg/1f680.svg" alt="πŸš€" width="72" height="72" loading="lazy" class="twemoji" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello ![πŸ‘‹](https://twemoji.maxcdn.com/v/latest/svg/1f44b.svg),
Emojis are so cool! ![πŸš€](https://twemoji.maxcdn.com/v/latest/svg/1f680.svg)![πŸŽ‰](https://twemoji.maxcdn.com/v/latest/svg/1f389.svg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello ![πŸ‘‹](https://twemoji.maxcdn.com/v/latest/72x72/1f44b.png),
Emojis are so cool! ![πŸš€](https://twemoji.maxcdn.com/v/latest/72x72/1f680.png)![πŸŽ‰](https://twemoji.maxcdn.com/v/latest/72x72/1f389.png)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello ![πŸ‘‹](https://twemoji.maxcdn.com/v/latest/72x72/1f44b.png),
Emojis are so cool! ![πŸŽ‰](https://twemoji.maxcdn.com/v/latest/72x72/1f389.png)![πŸš€](https://twemoji.maxcdn.com/v/latest/72x72/1f680.png)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello ![πŸ‘‹](https://twemoji.maxcdn.com/v/latest/72x72/1f44b.png),
Emojis are so cool! ![πŸš€](https://twemoji.maxcdn.com/v/latest/72x72/1f680.png)![πŸŽ‰](https://twemoji.maxcdn.com/v/latest/72x72/1f389.png)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello ![πŸ‘‹](https://twemoji.maxcdn.com/v/latest/svg/1f44b.svg),
Emojis are so cool! ![πŸŽ‰](https://twemoji.maxcdn.com/v/latest/svg/1f389.svg)![πŸš€](https://twemoji.maxcdn.com/v/latest/svg/1f680.svg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Hello ![πŸ‘‹](https://twemoji.maxcdn.com/v/latest/svg/1f44b.svg)
Hello ![πŸ‘‹πŸ»](https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3fb.svg)
Hello ![πŸ‘‹πŸΌ](https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3fc.svg)
Hello ![πŸ‘‹πŸ½](https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3fd.svg)
Hello ![πŸ‘‹πŸΎ](https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3fe.svg)
Hello ![πŸ‘‹πŸΏ](https://twemoji.maxcdn.com/v/latest/svg/1f44b-1f3ff.svg)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello <img src="https://twemoji.maxcdn.com/v/latest/svg/1f44b.svg" alt="πŸ‘‹" width="72" height="72" loading="lazy" class="twemoji" />,
Emojis are so cool! <img src="https://twemoji.maxcdn.com/v/latest/svg/1f680.svg" alt="πŸš€" width="72" height="72" loading="lazy" class="twemoji" /><img src="https://twemoji.maxcdn.com/v/latest/svg/1f389.svg" alt="πŸŽ‰" width="72" height="72" loading="lazy" class="twemoji" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello ![πŸ‘‹](https://twemoji.maxcdn.com/v/latest/svg/1f44b.svg),
Emojis are so cool! ![πŸš€](https://twemoji.maxcdn.com/v/latest/svg/1f680.svg)![πŸŽ‰](https://twemoji.maxcdn.com/v/latest/svg/1f389.svg)

0 comments on commit 3d16837

Please sign in to comment.