master eab2c5ef2f0a cached
38 files
65.1 KB
17.7k tokens
89 symbols
1 requests
Download .txt
Repository: eduardokum/laravel-mail-auto-embed
Branch: master
Commit: eab2c5ef2f0a
Files: 38
Total size: 65.1 KB

Directory structure:
gitextract_3h0c0u8y/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── format_php.yml
│       └── test.yml
├── .gitignore
├── .php_cs.dist
├── LICENSE
├── README.md
├── composer.json
├── config/
│   └── mail-auto-embed.php
├── phpunit.xml
├── src/
│   ├── Contracts/
│   │   ├── Embedder/
│   │   │   ├── EntityEmbedder.php
│   │   │   └── UrlEmbedder.php
│   │   └── Listeners/
│   │       └── EmbedImages.php
│   ├── Embedder/
│   │   ├── AttachmentEmbedder.php
│   │   ├── Base64Embedder.php
│   │   └── Embedder.php
│   ├── Listeners/
│   │   ├── SwiftEmbedImages.php
│   │   └── SymfonyEmbedImages.php
│   ├── Models/
│   │   └── EmbeddableEntity.php
│   └── ServiceProvider.php
└── tests/
    ├── Embedder/
    │   ├── AttachmentEmbedderTest.php
    │   └── Base64EmbedderTest.php
    ├── FormatTest.php
    ├── MailTest.php
    ├── TestCase.php
    ├── Traits/
    │   └── InteractsWithMessage.php
    ├── fixtures/
    │   ├── PictureEntity.php
    │   └── WrongEntity.php
    └── lib/
        ├── embed-can-skip.html
        ├── embed-when-enabled.html
        ├── formats/
        │   ├── html5-custom-embeds.html
        │   ├── html5-user-generated.html
        │   └── html5-valid.html
        ├── graceful-fails.html
        ├── manual-embed-when-disabled.html
        ├── override-to-attachment.html
        ├── override-to-base64.html
        └── raw-message.txt

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
github: eduardokum

================================================
FILE: .github/workflows/format_php.yml
================================================
name: Format (PHP)

on:
  pull_request:
    paths:
      - "**.php"

jobs:
  php-cs-fixer:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup PHP with php-cs-fixer
        uses: shivammathur/setup-php@master
        with:
          php-version: '8.0'
          tools: friendsofphp/php-cs-fixer:^2.19

      - name: Run php-cs-fixer
        run: php-cs-fixer fix

      - uses: stefanzweifel/git-auto-commit-action@v4
        with:
          commit_message: Apply php-cs-fixer changes


================================================
FILE: .github/workflows/test.yml
================================================
name: Run unit tests

on:
  - push
  - pull_request

env:
  COMPOSER_MEMORY_LIMIT: -1

jobs:
  test:
    runs-on: ${{ matrix.os }}

    timeout-minutes: 10

    strategy:
      fail-fast: false
      matrix:
        php: [8.3, 8.2, 8.1, 8.0, 7.4, 7.3, 7.2]
        laravel: ['6.*', '7.*', '8.*', '9.*', '10.*', '11.*', '12.*', '13.*']
        os: [ubuntu-latest]
        include:
          - laravel: 12.*
            testbench: 10.*
          - laravel: 11.*
            testbench: 9.*
          - laravel: 10.*
            testbench: 8.*
          - laravel: 9.*
            testbench: 7.*
          - laravel: 8.*
            testbench: 6.*
          - laravel: 7.*
            testbench: 5.*
          - laravel: 6.*
            testbench: 4.*
          - laravel: 13.*
            testbench: 11.*
        exclude:
          - laravel: 6.*
            php: 8.1
          - laravel: 6.*
            php: 8.2
          - laravel: 6.*
            php: 8.3
          - laravel: 7.*
            php: 8.1
          - laravel: 7.*
            php: 8.2
          - laravel: 7.*
            php: 8.3
          - laravel: 8.*
            php: 7.2
          - laravel: 8.*
            php: 8.0
          - laravel: 8.*
            php: 8.1
          - laravel: 8.*
            php: 8.2
          - laravel: 8.*
            php: 8.3
          - laravel: 9.*
            php: 7.2
          - laravel: 9.*
            php: 7.3
          - laravel: 9.*
            php: 7.4
          - laravel: 10.*
            php: 7.2
          - laravel: 10.*
            php: 7.3
          - laravel: 10.*
            php: 7.4
          - laravel: 10.*
            php: 8.0
          - laravel: 11.*
            php: 7.2
          - laravel: 11.*
            php: 7.2
          - laravel: 11.*
            php: 7.3
          - laravel: 11.*
            php: 7.4
          - laravel: 11.*
            php: 8.0
          - laravel: 11.*
            php: 8.1
          - laravel: 12.*
            php: 7.2
          - laravel: 12.*
            php: 7.2
          - laravel: 12.*
            php: 7.3
          - laravel: 12.*
            php: 7.4
          - laravel: 12.*
            php: 8.0
          - laravel: 12.*
            php: 8.1
          - laravel: 13.*
            php: 8.2
          - laravel: 13.*
            php: 8.1
          - laravel: 13.*
            php: 8.0
          - laravel: 13.*
            php: 7.4
          - laravel: 13.*
            php: 7.3
          - laravel: 13.*
            php: 7.2

    name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }}

    env:
      extensions: exif, json, mbstring, dom

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: ~/.composer/cache/files
          key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}

      - name: Setup PHP extensions
        id: cache-env
        uses: shivammathur/cache-extensions@v1
        with:
          php-version: ${{ matrix.php }}
          extensions: ${{ env.extensions }}
          key: php-extensions-cache-v1

      - name: Setup PHP ${{ matrix.php }}
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}
          extensions: ${{ env.extensions }}
          coverage: none

      - name: Install dependencies
        run: composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update

      - name: Update dependencies
        run: composer update --prefer-dist --no-interaction

      - name: Execute tests
        run: vendor/bin/phpunit


================================================
FILE: .gitignore
================================================
/vendor
/composer.lock
.idea/
.phpunit.result.cache

================================================
FILE: .php_cs.dist
================================================
<?php

// copied from https://gist.github.com/laravel-shift/cab527923ed2a109dda047b97d53c200

use PhpCsFixer\Config;
use PhpCsFixer\Finder;

$rules = [
    'array_syntax' => ['syntax' => 'short'],
    'binary_operator_spaces' => [
        'default' => 'single_space',
        'operators' => ['=>' => null],
    ],
    'blank_line_after_namespace' => true,
    'blank_line_after_opening_tag' => true,
    'blank_line_before_statement' => [
        'statements' => ['return'],
    ],
    'braces' => true,
    'cast_spaces' => true,
    'class_attributes_separation' => [
        'elements' => ['method'],
    ],
    'class_definition' => true,
    'concat_space' => [
        'spacing' => 'none',
    ],
    'declare_equal_normalize' => true,
    'elseif' => true,
    'encoding' => true,
    'full_opening_tag' => true,
    'fully_qualified_strict_types' => true, // added by Shift
    'function_declaration' => true,
    'function_typehint_space' => true,
    'heredoc_to_nowdoc' => true,
    'include' => true,
    'increment_style' => ['style' => 'post'],
    'indentation_type' => true,
    'linebreak_after_opening_tag' => true,
    'line_ending' => true,
    'lowercase_cast' => true,
    'lowercase_constants' => true,
    'lowercase_keywords' => true,
    'lowercase_static_reference' => true, // added from Symfony
    'magic_method_casing' => true, // added from Symfony
    'magic_constant_casing' => true,
    'method_argument_space' => true,
    'native_function_casing' => true,
    'no_alias_functions' => true,
    'no_extra_blank_lines' => [
        'tokens' => [
            'extra',
            'throw',
            'use',
            'use_trait',
        ],
    ],
    'no_blank_lines_after_class_opening' => true,
    'no_blank_lines_after_phpdoc' => true,
    'no_closing_tag' => true,
    'no_empty_phpdoc' => true,
    'no_empty_statement' => true,
    'no_leading_import_slash' => true,
    'no_leading_namespace_whitespace' => true,
    'no_mixed_echo_print' => [
        'use' => 'echo',
    ],
    'no_multiline_whitespace_around_double_arrow' => true,
    'multiline_whitespace_before_semicolons' => [
        'strategy' => 'no_multi_line',
    ],
    'no_short_bool_cast' => true,
    'no_singleline_whitespace_before_semicolons' => true,
    'no_spaces_after_function_name' => true,
    'no_spaces_around_offset' => true,
    'no_spaces_inside_parenthesis' => true,
    'no_trailing_comma_in_list_call' => true,
    'no_trailing_comma_in_singleline_array' => true,
    'no_trailing_whitespace' => true,
    'no_trailing_whitespace_in_comment' => true,
    'no_unneeded_control_parentheses' => true,
    'no_unreachable_default_argument_value' => true,
    'no_useless_return' => true,
    'no_whitespace_before_comma_in_array' => true,
    'no_whitespace_in_blank_line' => true,
    'normalize_index_brace' => true,
    'not_operator_with_successor_space' => true,
    'object_operator_without_whitespace' => true,
    'ordered_imports' => ['sortAlgorithm' => 'alpha'],
    'phpdoc_indent' => true,
    'phpdoc_inline_tag' => true,
    'phpdoc_no_access' => true,
    'phpdoc_no_package' => true,
    'phpdoc_no_useless_inheritdoc' => true,
    'phpdoc_scalar' => true,
    'phpdoc_single_line_var_spacing' => true,
    'phpdoc_summary' => true,
    'phpdoc_to_comment' => true,
    'phpdoc_trim' => true,
    'phpdoc_types' => true,
    'phpdoc_var_without_name' => true,
    'psr4' => true,
    'self_accessor' => true,
    'short_scalar_cast' => true,
    'simplified_null_return' => false, // disabled by Shift
    'single_blank_line_at_eof' => true,
    'single_blank_line_before_namespace' => true,
    'single_class_element_per_statement' => true,
    'single_import_per_statement' => true,
    'single_line_after_imports' => true,
    'single_line_comment_style' => [
        'comment_types' => ['hash'],
    ],
    'single_quote' => true,
    'space_after_semicolon' => true,
    'standardize_not_equals' => true,
    'switch_case_semicolon_to_colon' => true,
    'switch_case_space' => true,
    'ternary_operator_spaces' => true,
    'trailing_comma_in_multiline_array' => true,
    'trim_array_spaces' => true,
    'unary_operator_spaces' => true,
    'visibility_required' => [
        'elements' => ['method', 'property'],
    ],
    'whitespace_after_comma_in_array' => true,
];

$finder = Finder::create()
    ->in([
        __DIR__.'/src',
        __DIR__.'/config',
        __DIR__.'/tests',
    ])
    ->name('*.php')
    ->notName('*.blade.php')
    ->ignoreDotFiles(true)
    ->ignoreVCS(true);

return Config::create()
    ->setFinder($finder)
    ->setRules($rules)
    ->setRiskyAllowed(true)
    // this is disabled, due to unexpected errors in some environments. Fell free to enable this to fits your needs.
    ->setUsingCache(false);


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 Eduardo Gusmão

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
[![Packagist](https://img.shields.io/packagist/v/eduardokum/laravel-mail-auto-embed.svg?style=flat-square)](https://github.com/eduardokum/laravel-mail-auto-embed)
[![Packagist](https://img.shields.io/packagist/dt/eduardokum/laravel-mail-auto-embed.svg?style=flat-square)](https://github.com/eduardokum/laravel-mail-auto-embed)
[![Packagist](https://img.shields.io/packagist/l/eduardokum/laravel-mail-auto-embed.svg?style=flat-square)](https://github.com/eduardokum/laravel-mail-auto-embed)
[![GitHub Actions](https://img.shields.io/endpoint.svg?style=flat-square&url=https%3A%2F%2Factions-badge.atrox.dev%2Feduardokum%2Flaravel-mail-auto-embed%2Fbadge)](https://actions-badge.atrox.dev/eduardokum/laravel-mail-auto-embed/goto)
[![GitHub forks](https://img.shields.io/github/forks/eduardokum/laravel-mail-auto-embed.svg?color=lightgrey&style=flat-square)](https://github.com/eduardokum/laravel-mail-auto-embed)

# Laravel Mail Auto Embed

Automatically parses your messages and embeds the images found into your mail,
replacing the original online-version of the image.

Should work on Laravel 5.3+. Automatically tested for Laravel 5.4+ on PHP 7.0+.

## Version Compatibility

| Laravel | Package |
|---------|---------|
| \< 8.x  | 1.x     |
| \> 9.x  | 2.x     |

## Install

You can install the package via composer:
```shell
composer require eduardokum/laravel-mail-auto-embed
```

This package uses Laravel 5.5 Package Auto-Discovery.
For previous versions of Laravel, you need to add the following Service
Provider:

```php
$providers = [
    ...
    \Eduardokum\LaravelMailAutoEmbed\ServiceProvider::class,
    ...
 ];
```


## Usage

Its use is very simple, you write your markdown normally:

```markdown
<!-- eg: resources/vendor/mail/markdown/order-shipped.blade.php -->
@component('mail::message')
# Order Shipped

Your order has been shipped!

@component('mail::button', ['url' => $url])
View Order
@endcomponent

Purchased product:

![product](https://domain.com/products/product-1.png)

Thanks,<br>
{{ config('app.name') }}
@endcomponent
```

When sending, it will replace the link that would normally be generated:
> `<img src="https://domain.com/products/product-1.png">`

by an embedded inline attachment of the image:
> `<img src="cid:3991f143cf1a86257f8671883736613c@Swift.generated">`.

It works for raw html too:

```html
<!-- eg: resources/vendor/mail/html/header.blade.php -->
<tr>
    <td class="header">
        <a href="{{ $url }}">
            <img src="https://domain.com/logo.png" class="img-header">
        </a>
    </td>
</tr>
```

If you do not want to use automatic embedding for specific images (because they
are hosted elsewhere, if you want to use some kind of image tracker, etc.),
simply add the attribute `data-skip-embed` in the image tag:

```html
<img src="https://domain.com/logo.png" data-skip-embed class="img-header">
```
### Local resources

For local resources that are not available publicly, use `file://` urls:

```html
<img src="file://{{ resource_path('assets/img/logo.png') }}" alt="Logo" border="0"/>
```

## Configuration

The defaults are set in `config/mail-auto-embed.php`. You can copy this file to
your own config directory to modify the values using this command:

```shell
php artisan vendor:publish --provider="Eduardokum\LaravelMailAutoEmbed\ServiceProvider"
```

### Explicit embedding configuration

By default, images are embedded automatically, unless you add the
`data-skip-embed` attribute.

You can also disable auto-embedding globally by setting the `MAIL_AUTO_EMBED`
environment variable to `false`, or by modifying the `enabled` property in the
published config. You can then enable embedding for individual images with the
`data-auto-embed` attribute.

```env
# .env
MAIL_AUTO_EMBED=false
```

```php
return [
    /*
    |--------------------------------------------------------------------------
    | Mail auto embed
    |--------------------------------------------------------------------------
    |
    | If true, images will be automatically embedded.
    | If false, only images with the 'data-auto-embed' attribute will be embedded
    |
    */
    'enabled' => false,

    // …
];
```

```html
<p>
    <!-- Won't be embedded -->
    <img src="https://domain.com/logo.png" class="img-header">
</p>
<p>
    <!-- Explicit embedding -->
    <img src="https://domain.com/item.png"  data-auto-embed>
</p>
```

### Base64 embedding

If you prefer to use Base64 instead of inline attachments, you can do so by
setting the `MAIL_AUTO_EMBED_METHOD` environment variable or the `method`
config property to `base64`.

```php
return [
    // …

    /*
    |--------------------------------------------------------------------------
    | Mail embed method
    |--------------------------------------------------------------------------
    |
    | Supported: "attachment", "base64"
    |
    */
    'method' => 'base64',
];
```

Note that it will increase the e-mail size, and that it won't be decoded by
some e-mail clients such as Gmail.

## Mixed embedding methods

If you want to use both inline attachment and Base64 depending on the image,
you can specify the embedding method as the `data-auto-embed` attribute value:

```html
<p>
    <img src="https://domain.com/logo.png" data-auto-embed="base64">
</p>
<p>
    <img src="https://domain.com/item.png" data-auto-embed="attachment">
</p>
```


## Embedding entities

You might want to embed images that don't actually exist in your filesystem
(stored in the database).

In that case, make the entities you want to embed implement the
`EmbeddableEntity` interface:

```php
namespace App\Models;

use Eduardokum\LaravelMailAutoEmbed\Models\EmbeddableEntity;
use Illuminate\Database\Eloquent\Model;

class Picture extends Model implements EmbeddableEntity
{
    /**
     * @param  mixed  $id
     * @return Picture
     */
    public static function findEmbeddable($id)
    {
        return static::find($id);
    }

    /**
     * @return mixed
     */
    public function getRawContent()
    {
        return $this->data;
    }

    /**
     * @return string
     */
    public function getFileName()
    {
        return 'profile_'.$this->id.'.png';
    }

    /**
     * @return string
     */
    public function getMimeType()
    {
        return 'image/png';
    }
}
```

Then, you can use the `embed:ClassName:id` syntax in your e-mail template:

```html
<p>
    <img src="embed:App\Models\Picture:123">
</p>
```

## Contributing
Please feel free to submit pull requests if you can improve or add any
features.

We are currently using PSR-2. This is easy to implement and check with the PHP
Coding Standards Fixer.

<a target="_blank" href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=QPDFT3UXS6PTL&lc=GB&item_name=laravel%2dmail%2dauto%2dembed&item_number=laravel%2dmail%2dauto%2dembed&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted">
        <img alt="Donate with Paypal" src="https://www.paypalobjects.com/en_US/GB/i/btn/btn_donateCC_LG.gif"/></a>



================================================
FILE: composer.json
================================================
{
    "name": "eduardokum/laravel-mail-auto-embed",
    "description": "Library for embed images in emails automatically",
    "keywords": [
        "eduardokum",
        "laravel-mail-auto-embed"
    ],
    "homepage": "https://github.com/eduardokum/laravel-mail-auto-embed",
    "license": "MIT",
    "authors": [
        {
            "name": "Eduardo Gusmão",
            "email": "eduguscontra3@hotmail.com"
        }
    ],
    "require": {
        "php": "^7.2|^8.0|^8.1|^8.2",
        "ext-dom": "*",
        "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
        "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
        "illuminate/mail": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0|^13.0",
        "masterminds/html5": "^2.7",
        "ext-curl": "*",
        "ext-fileinfo": "*"
    },
    "require-dev": {
        "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
        "phpunit/phpunit": "^8.5.30|^9.0|^10.0|^11.0|^12.5.12",
        "squizlabs/php_codesniffer": "^3.5|^4.0"
    },
    "config": {
        "sort-packages": true
    },
    "autoload": {
        "psr-4": {
            "Eduardokum\\LaravelMailAutoEmbed\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Eduardokum\\LaravelMailAutoEmbed\\Tests\\": "tests"
        }
    },
    "extra": {
        "laravel": {
            "providers": [
                "Eduardokum\\LaravelMailAutoEmbed\\ServiceProvider"
            ]
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}


================================================
FILE: config/mail-auto-embed.php
================================================
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Mail auto embed
    |--------------------------------------------------------------------------
    |
    | If true, images will be automatically embedded.
    | If false, only images with the 'data-auto-embed' attribute will be embedded
    |
    */

    'enabled' => env('MAIL_AUTO_EMBED', true),

    /*
    |--------------------------------------------------------------------------
    | Mail embed method
    |--------------------------------------------------------------------------
    |
    | Supported: "attachment", "base64"
    |
    */

    'method' => env('MAIL_AUTO_EMBED_METHOD', 'attachment'),

    'curl' => [
        'connect_timeout' => 5, // Seconds
        'timeout' => 10, // Seconds
        'cache' => false,
        'cache_ttl' => 3600,
    ]
];


================================================
FILE: phpunit.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         backupGlobals="false"
         bootstrap="vendor/autoload.php"
         colors="true"
         processIsolation="true"
         stopOnFailure="false"
         xsi:noNamespaceSchemaLocation="/phpunit.xsd">
    <testsuites>
        <testsuite name="Application Test Suite">
            <directory suffix="Test.php">./tests</directory>
        </testsuite>
    </testsuites>
    <coverage>
        <include>
            <directory suffix=".php">./src</directory>
        </include>
    </coverage>
</phpunit>


================================================
FILE: src/Contracts/Embedder/EntityEmbedder.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Contracts\Embedder;

use Eduardokum\LaravelMailAutoEmbed\Models\EmbeddableEntity;

interface EntityEmbedder
{
    /**
     * @param  EmbeddableEntity $entity
     * @return string
     */
    public function fromEntity(EmbeddableEntity $entity);
}


================================================
FILE: src/Contracts/Embedder/UrlEmbedder.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Contracts\Embedder;

interface UrlEmbedder
{
    /**
     * @param  string  $url
     * @return string
     */
    public function fromUrl($url);

    /**
     * @param  string  $path
     * @return string
     */
    public function fromPath($path);

/**
     * @param string $base64
     * @return string
     */
    public function fromBase64($base64);
}


================================================
FILE: src/Contracts/Listeners/EmbedImages.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Contracts\Listeners;

interface EmbedImages
{

}

================================================
FILE: src/Embedder/AttachmentEmbedder.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Embedder;

use Eduardokum\LaravelMailAutoEmbed\Models\EmbeddableEntity;
use Exception;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
use Swift_EmbeddedFile;
use Swift_Message;
use Symfony\Component\Mime\Email;

class AttachmentEmbedder extends Embedder
{
    /**
     * @var  Swift_Message
     */
    protected $swiftMessage;

    /**
     * @var  Email
     */
    protected $symfonyMessage;

    /**
     * @param Swift_Message $message
     *
     * @return AttachmentEmbedder
     * @throws Exception
     */
    public function setSwiftMessage(Swift_Message $message)
    {
        if ($this->isLaravel9()) {
            throw new Exception('Laravel 9 and greater must use symfony mailer');
        }
        $this->swiftMessage = $message;
        return $this;
    }

    /**
     * @param Email $message
     *
     * @return AttachmentEmbedder
     * @throws Exception
     */
    public function setSymfonyMessage(Email $message)
    {
        if (!$this->isLaravel9()) {
            throw new Exception('Laravel 8 and below must use swift mailer');
        }
        $this->symfonyMessage = $message;
        return $this;
    }

    /**
     * @param string $url
     *
     * @throws Exception
     */
    public function fromUrl($url)
    {
        $localFile = str_replace(url('/'), public_path(), $url);

        if (file_exists($localFile)) {
            return $this->fromPath($localFile);
        }

        if ($embeddedFromRemoteUrl = $this->fromRemoteUrl($url)) {
            return $embeddedFromRemoteUrl;
        }

        return $url;
    }

    /**
     * @param $path
     *
     * @return string
     * @throws Exception
     */
    public function fromPath($path)
    {
        return $this->embed(file_get_contents($path), basename($path), mime_content_type($path));
    }

    /**
     * @param string $base64
     *
     * @return string
     * @throws Exception
     */
    public function fromBase64($base64)
    {
        $data = explode(',', $base64);
        $type = explode(';', explode(':', $data[0])[1])[0];
        $content = base64_decode($data[1]);
        $name = Str::random();

        return $this->embed($content, $name, $type);
    }

    /**
     * @param EmbeddableEntity $entity
     *
     * @return string
     * @throws Exception
     */
    public function fromEntity(EmbeddableEntity $entity)
    {
        return $this->embed($entity->getRawContent(), $entity->getFileName(), $entity->getMimeType());
    }

    /**
     * @param string $url
     *
     * @throws Exception
     */
    public function fromRemoteUrl($url)
    {
        if (filter_var($url, FILTER_VALIDATE_URL)) {
            $hashName = implode('_', [
                'laravel-mail-auto-embed',
                hash('sha256', $url)
            ]);

            if (config('mail-auto-embed.curl.cache', false) && $file = Cache::get($hashName)) {
                return $this->embed($file['content'], $file['name'], $file['type']);
            }

            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_HEADER, 0);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $raw = curl_exec($ch);
            $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
            curl_close($ch);

            if ($httpcode == 200) {
                $pathInfo = pathinfo($url);

                $queryStr = parse_url($url, PHP_URL_QUERY) ?: '';
                parse_str($queryStr ?? '', $queryParams);
                $basename = $queryParams['basename'] ?? $pathInfo['basename'];

                if (config('mail-auto-embed.curl.cache', false)) {
                    Cache::put($hashName, [
                        'content' => $raw,
                        'name' => $basename,
                        'type' => $contentType
                    ], config('mail-auto-embed.curl.cache_ttl', 3600));
                }

                return $this->embed($raw, $basename, $contentType);
            }
        }
        return $url;
    }

    /**
     * @param $body
     * @param $name
     * @param $type
     *
     * @return string
     * @throws Exception
     */
    protected function embed($body, $name, $type)
    {
        if ($this->isLaravel9() && !empty($this->symfonyMessage)) {
            if (gettype($name) !== 'string') {
                $name = Str::random();
            }
            $this->symfonyMessage->embed($body, $name, $type);
            return "cid:$name";
        }

        if (!$this->isLaravel9() && !empty($this->swiftMessage)) {
            return $this->swiftMessage->embed(
                new Swift_EmbeddedFile(
                    $body,
                    $name,
                    $type
                )
            );
        }

        throw new Exception('No message defined');
    }

    /**
     * @return bool
     */
    private function isLaravel9()
    {
        return version_compare(Application::VERSION, '9.0.0', '>=');
    }
}


================================================
FILE: src/Embedder/Base64Embedder.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Embedder;

use Illuminate\Support\Arr;
use Eduardokum\LaravelMailAutoEmbed\Models\EmbeddableEntity;

class Base64Embedder extends Embedder
{
    /**
     * @var  array
     */
    private $config;

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

    /**
     * @param  string  $url
     * @return string
     */
    public function fromUrl($url)
    {
        $localFile = str_replace(url('/'), public_path('/'), $url);
        if (file_exists($localFile)) {
            return $this->fromPath($localFile);
        }

        if ($embeddedFromRemoteUrl = $this->fromRemoteUrl($url)) {
            return $embeddedFromRemoteUrl;
        }

        return $url;
    }

    /**
     * @param $path
     *
     * @return string
     */
    public function fromPath($path)
    {
        if (file_exists($path)) {
            return $this->base64String(mime_content_type($path), file_get_contents($path));
        }

        return $path;
    }

    /**
     * @param  EmbeddableEntity  $entity
     * @return string
     */
    public function fromEntity(EmbeddableEntity $entity)
    {
        return $this->base64String($entity->getMimeType(), $entity->getRawContent());
    }

    /**
     * @param  string  $url
     */
    public function fromRemoteUrl($url)
    {
        if (filter_var($url, FILTER_VALIDATE_URL)) {
            $ch = curl_init($url);
            curl_setopt($ch, CURLOPT_HEADER, 0);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, Arr::get($this->config, 'curl.connect_timeout', 5));
            curl_setopt($ch, CURLOPT_TIMEOUT, Arr::get($this->config, 'curl.timeout', 10));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $raw = curl_exec($ch);
            $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
            curl_close($ch);

            if ($httpcode == 200) {
                return $this->base64String($contentType, $raw);
            }
        }

        return $url;
    }

    /**
     * @param string $base64
     * @return string
     */
    public function fromBase64($base64)
    {
        return $base64;
    }

    /**
     * @param  string  $mimeType
     * @param  mixed  $content
     */
    private function base64String($mimeType, $content)
    {
        return 'data:'.$mimeType.';base64,'.base64_encode($content);
    }
}


================================================
FILE: src/Embedder/Embedder.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Embedder;

use Eduardokum\LaravelMailAutoEmbed\Contracts\Embedder\UrlEmbedder;
use Eduardokum\LaravelMailAutoEmbed\Contracts\Embedder\EntityEmbedder;

abstract class Embedder implements UrlEmbedder, EntityEmbedder
{
}


================================================
FILE: src/Listeners/SwiftEmbedImages.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Listeners;

use DOMDocument;
use DOMElement;
use Eduardokum\LaravelMailAutoEmbed\Embedder\AttachmentEmbedder;
use Eduardokum\LaravelMailAutoEmbed\Embedder\Base64Embedder;
use Eduardokum\LaravelMailAutoEmbed\Embedder\Embedder;
use Eduardokum\LaravelMailAutoEmbed\Models\EmbeddableEntity;
use Exception;
use Masterminds\HTML5;
use ReflectionClass;
use Swift_Events_SendEvent;
use Swift_Events_SendListener;
use Swift_Message;
use Swift_Mime_SimpleMessage;
use Symfony\Component\Mime\Email;

class SwiftEmbedImages implements Swift_Events_SendListener
{
    /**
     * @var  array
     */
    private $config;

    /**
     * @var Email|Swift_Mime_SimpleMessage
     */
    private $message;

    /**
     * @param  array  $config
     */
    public function __construct($config)
    {
        $this->config = $config;
    }

    /**
     * @param  Swift_Events_SendEvent  $evt
     */
    public function beforeSendPerformed(Swift_Events_SendEvent $evt)
    {
        $this->handle($evt->getMessage());
    }

    /**
     * @param Swift_Mime_SimpleMessage $message
     * @return void
     */
    public function handle(Swift_Mime_SimpleMessage $message)
    {
        $this->message = $message;
        $this->attachImages();
    }

    /**
     * @param  Swift_Events_SendEvent  $evt
     * @return bool
     */
    public function sendPerformed(Swift_Events_SendEvent $evt)
    {
        return true;
    }

    /**
     *
     */
    private function attachImages()
    {
        // Get body
        $body = $this->message->getBody();

        // Parse document
        $parser = new HTML5();
        $document = $parser->loadHTML($body);
        if (! $document) {
            // Cannot read
            return;
        }

        // Invalid HTML (raw message)
        if ($this->shouldSkipDocument($document)) {
            return;
        }

        // Add images
        $this->attachImagesToDom($document);

        // Replace body
        $this->message->setBody($parser->saveHTML($document));

//        $html_body = $this->message->getBody();
//
        /*        $html_body = preg_replace_callback('/<img.*src="(.*?)"\s?(.*)?>/', [$this, 'replaceCallback'], $html_body);*/
//
//        $this->message->setBody($html_body);
    }

    /**
     * @param  DOMDocument $document
     * @return bool
     */
    private function shouldSkipDocument(DOMDocument $document)
    {
        if ($document->childNodes->count() != 1) {
            return false;
        }

        if ($document->childNodes->item(0)->nodeType == XML_DOCUMENT_TYPE_NODE) {
            return true;
        }

        return false;
    }

    /**
     * @param DOMDocument $document
     */
    private function attachImagesToDom(DOMDocument &$document)
    {
        foreach ($document->getElementsByTagName('img') as $image) {
            \assert($image instanceof DOMElement);

            // Skip if embed is not required
            if ($this->needsEmbed($image)) {
                // Get proper embedder
                $embedder = $this->getEmbedder($image);

                // Update src
                $image->setAttribute('src', $this->embed(
                    $embedder,
                    $image->getAttribute('src')
                ));
            }

            // Remove data properties
            $image->removeAttribute('data-skip-embed');
            $image->removeAttribute('data-auto-embed');
        }
    }

    /**
     * @param DOMElement $imageTag
     * @return bool
     */
    private function needsEmbed(DOMElement $imageTag)
    {
        // Don't embed if 'data-skip-embed' is present
        if ($imageTag->hasAttribute('data-skip-embed')) {
            return false;
        }

        // Don't embed if auto-embed is disabled and 'data-auto-embed' is absent
        if (! $this->config['enabled'] && ! $imageTag->hasAttribute('data-auto-embed')) {
            return false;
        }

        return true;
    }

    /**
     * @param DOMElement $imageTag
     *
     * @return Embedder
     * @throws Exception
     */
    private function getEmbedder(DOMElement $imageTag)
    {
        $method = $imageTag->getAttribute('data-auto-embed');
        if (empty($method)) {
            $method = $this->config['method'];
        }

        switch ($method) {
            case 'attachment':
            default:
                return (new AttachmentEmbedder())
                    ->setSwiftMessage($this->message);
            case 'base64':
                return new Base64Embedder($this->config);
        }
    }

    /**
     * @param  Embedder  $embedder
     * @param  string    $src
     * @return string
     */
    private function embed(Embedder $embedder, $src)
    {
        // Entity embedding
        if (strpos($src, 'embed:') === 0) {

            $embedParams = explode(':', $src);
            if (count($embedParams) < 3) {
                return $src;
            }

            $className = urldecode($embedParams[1]);
            $id = $embedParams[2];

            if (!class_exists($className)) {
                return $src;
            }

            $class = new ReflectionClass($className);
            if (! $class->implementsInterface(EmbeddableEntity::class) ) {
                return $src;
            }

            /** @var EmbeddableEntity $className */
            if (! $instance = $className::findEmbeddable($id)) {
                return $src;
            }

            return $embedder->fromEntity($instance);
        }

        // URL embedding
        if (filter_var($src, FILTER_VALIDATE_URL) !== false) {
            return $embedder->fromUrl($src);
        }

        $appPath = method_exists(app(), 'path') ? app_path($src) : null;
        $publicPath = app()->bound('path.public') ? public_path($src) : null;
        $storagePath = app()->bound('path.storage') ? storage_path($src) : null;
        $storageAppPath = app()->bound('path.storage') ? storage_path("app/$src") : null;
        if (file_exists($src)) {
            return $embedder->fromPath($src);
        } elseif ($publicPath && file_exists($publicPath)) { // Try to guess where the file is at that priority level
            return $embedder->fromPath($publicPath);
        } elseif ($appPath && file_exists($appPath)) {
            return $embedder->fromPath($appPath);
        } elseif ($storagePath && file_exists($storagePath)) {
            return $embedder->fromPath($storagePath);
        } elseif ($storageAppPath && file_exists($storageAppPath)) {
            return $embedder->fromPath($storageAppPath);
        }

        return $src;
    }
}

================================================
FILE: src/Listeners/SymfonyEmbedImages.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Listeners;

use DOMDocument;
use DOMElement;
use Eduardokum\LaravelMailAutoEmbed\Embedder\AttachmentEmbedder;
use Eduardokum\LaravelMailAutoEmbed\Embedder\Base64Embedder;
use Eduardokum\LaravelMailAutoEmbed\Embedder\Embedder;
use Eduardokum\LaravelMailAutoEmbed\Models\EmbeddableEntity;
use Exception;
use Illuminate\Mail\Events\MessageSending;
use Masterminds\HTML5;
use ReflectionClass;
use Swift_Message;
use Symfony\Component\Mime\Email;

class SymfonyEmbedImages
{
    /**
     * @var  array
     */
    private $config;

    /**
     * @var  Email
     */
    private $message;

    /**
     * @param  array  $config
     */
    public function __construct($config)
    {
        $this->config = $config;
    }

    /**
     * @param  MessageSending  $event
     */
    public function beforeSendPerformed(MessageSending $event)
    {
        $this->handle($event->message);
    }

    /**
     * @param Email $message
     * @return void
     */
    public function handle(Email $message)
    {
        $this->message = $message;
        $this->attachImages();
    }

    /**
     * Attaches images by parsing the HTML document.
     */
    private function attachImages()
    {
        // Get body
        $body = $this->message->getHtmlBody();
        if ($body === null) {
            // Not an HTML message
            return;
        }

        // Parse document
        $parser = new HTML5();
        $document = $parser->loadHTML($body);
        if (! $document) {
            // Cannot read
            return;
        }

        // Add images
        $this->attachImagesToDom($document);

        // Replace body
        $this->message->html($parser->saveHTML($document));
    }

    /**
     * @param DOMDocument $document
     *
     * @return void
     * @throws Exception
     */
    private function attachImagesToDom(DOMDocument &$document)
    {
        foreach ($document->getElementsByTagName('img') as $image) {
            \assert($image instanceof DOMElement);

            // Skip if embed is not required
            if ($this->needsEmbed($image)) {
                // Get proper embedder
                $embedder = $this->getEmbedder($image);
                // Update src
                $image->setAttribute('src', $this->embed(
                    $embedder,
                    $image->getAttribute('src')
                ));
            }

            // Remove data properties
            $image->removeAttribute('data-skip-embed');
            $image->removeAttribute('data-auto-embed');
        }
    }

    /**
     * @param DOMElement $imageTag
     *
     * @return bool
     */
    private function needsEmbed(DOMElement $imageTag)
    {
        // Don't embed if 'data-skip-embed' is present
        if ($imageTag->hasAttribute('data-skip-embed')) {
            return false;
        }

        // Don't embed if auto-embed is disabled and 'data-auto-embed' is absent
        if (! $this->config['enabled'] && ! $imageTag->hasAttribute('data-auto-embed')) {
            return false;
        }

        return true;
    }

    /**
     * @param DOMElement $imageTag
     *
     * @return Embedder
     * @throws Exception
     */
    private function getEmbedder(DOMElement $imageTag)
    {
        $method = $imageTag->getAttribute('data-auto-embed');
        if (empty($method)) {
            $method = $this->config['method'];
        }

        switch ($method) {
            case 'attachment':
            default:
                return (new AttachmentEmbedder())
                    ->setSymfonyMessage($this->message);
            case 'base64':
                return new Base64Embedder($this->config);
        }
    }

    /**
     * @param  Embedder  $embedder
     * @param  string    $src
     * @return string
     */
    private function embed(Embedder $embedder, $src)
    {
        // Entity embedding
        if (strpos($src, 'embed:') === 0) {
            $embedParams = explode(':', $src);
            if (count($embedParams) < 3) {
                return $src;
            }

            $className = urldecode($embedParams[1]);
            $id = $embedParams[2];

            if (! class_exists($className)) {
                return $src;
            }

            $class = new ReflectionClass($className);
            if (! $class->implementsInterface(EmbeddableEntity::class)) {
                return $src;
            }

            /** @var EmbeddableEntity $className */
            if (! $instance = $className::findEmbeddable($id)) {
                return $src;
            }

            return $embedder->fromEntity($instance);
        }

        // URL embedding
        if (filter_var($src, FILTER_VALIDATE_URL) !== false) {
            return $embedder->fromUrl($src);
        }

        // Base64 embedding
        if (preg_match('/^data:image\/[a-z]+;base64,/', $src)) {
            return $embedder->fromBase64($src);
        }

        $appPath = method_exists(app(), 'path') ? app_path($src) : null;
        $publicPath = app()->bound('path.public') ? public_path($src) : null;
        $storagePath = app()->bound('path.storage') ? storage_path($src) : null;
        $storageAppPath = app()->bound('path.storage') ? storage_path("app/$src") : null;
        if (file_exists($src)) {
            return $embedder->fromPath($src);
        } elseif ($publicPath && file_exists($publicPath)) { // Try to guess where the file is at that priority level
            return $embedder->fromPath($publicPath);
        } elseif ($appPath && file_exists($appPath)) {
            return $embedder->fromPath($appPath);
        } elseif ($storagePath && file_exists($storagePath)) {
            return $embedder->fromPath($storagePath);
        } elseif ($storageAppPath && file_exists($storageAppPath)) {
            return $embedder->fromPath($storageAppPath);
        }

        return $src;
    }
}



================================================
FILE: src/Models/EmbeddableEntity.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Models;

interface EmbeddableEntity
{
    /**
     * @param  mixed  $id
     * @return EmbeddableEntity|null
     */
    public static function findEmbeddable($id);

    /**
     * @return mixed
     */
    public function getRawContent();

    /**
     * @return string
     */
    public function getFileName();

    /**
     * @return string
     */
    public function getMimeType();
}


================================================
FILE: src/ServiceProvider.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed;

use Eduardokum\LaravelMailAutoEmbed\Contracts\Listeners\EmbedImages;
use Eduardokum\LaravelMailAutoEmbed\Listeners\SwiftEmbedImages;
use Eduardokum\LaravelMailAutoEmbed\Listeners\SymfonyEmbedImages;
use Illuminate\Foundation\Application;
use Illuminate\Mail\Events\MessageSending;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
use Throwable;

class ServiceProvider extends BaseServiceProvider
{
    /**
     * Bootstrap the application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->publishes([$this->getConfigPath() => config_path('mail-auto-embed.php')], 'config');

        $this->app->singleton(EmbedImages::class, function($app) {
            if (version_compare(Application::VERSION, '9.0.0', '>=')) {
                return new SymfonyEmbedImages($app['config']->get('mail-auto-embed'));
            }
            return new SwiftEmbedImages($app['config']->get('mail-auto-embed'));
        });

        if (version_compare(Application::VERSION, '9.0.0', '>=')) {
            Event::listen(function (MessageSending $event) {
                $this->app->make(EmbedImages::class)->beforeSendPerformed($event);
            });
        } else {
            foreach (Arr::get($this->app['config'], 'mail.mailers', []) as $driver => $mailer) {
                try {
                    // If transport not exists this will throw an exception
                    Mail::driver($driver)->getSwiftMailer()->registerPlugin($this->app->make(EmbedImages::class));
                } catch (Throwable $e) {}
            }
        }
    }

    /**
     * Register the application services.
     *
     * @return void
     */
    public function register()
    {
        $this->mergeConfigFrom($this->getConfigPath(), 'mail-auto-embed');
    }

    /**
     * @return string
     */
    protected function getConfigPath()
    {
        return __DIR__.'/../config/mail-auto-embed.php';
    }
}


================================================
FILE: tests/Embedder/AttachmentEmbedderTest.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Tests\Embedder;

use Eduardokum\LaravelMailAutoEmbed\Embedder\AttachmentEmbedder;
use Eduardokum\LaravelMailAutoEmbed\Tests\fixtures\PictureEntity;
use Eduardokum\LaravelMailAutoEmbed\Tests\TestCase;
use Swift_EmbeddedFile;
use Swift_Message;

class AttachmentEmbedderTest extends TestCase
{
    public function testOkAttachment(){
        $this->assertTrue(true);
    }
//    /** @var Swift_Message */
//    private $message;
//
//    /** @var AttachmentEmbedder */
//    private $embedder;
//
//    /**
//     * @before
//     * @return void
//     */
//    protected function setUpEmbedder()
//    {
//        $this->message = new Swift_Message();
//        $this->embedder = new AttachmentEmbedder($this->message);
//    }
//
//    /**
//     * @return int
//     */
//    private function getEmbeddedFilesCount()
//    {
//        return collect($this->message->getChildren())
//            ->filter(
//                function ($item) {
//                    return $item instanceof Swift_EmbeddedFile;
//                }
//            )
//            ->count();
//    }
//
//    /**
//     * @test
//     */
//    public function testLocalConversion()
//    {
//        $result = $this->embedder->fromUrl('http://localhost/test.png');
//
//        $this->assertStringStartsWith('cid:', $result);
//
//        $this->assertEquals(1, $this->getEmbeddedFilesCount());
//    }
//
//    /**
//     * @test
//     */
//    public function testEntityConversion()
//    {
//        $picture = new PictureEntity();
//
//        $result = $this->embedder->fromEntity($picture);
//
//        $this->assertStringStartsWith('cid:', $result);
//
//        $this->assertEquals(1, $this->getEmbeddedFilesCount());
//    }
//
//    /**
//     * @test
//     */
//    public function testRemoteUrl()
//    {
//        $result = $this->embedder->fromRemoteUrl('https://via.placeholder.com/1');
//
//        $this->assertStringStartsWith('cid:', $result);
//
//        $this->assertEquals(1, $this->getEmbeddedFilesCount());
//    }
}


================================================
FILE: tests/Embedder/Base64EmbedderTest.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Tests\Embedder;

use Eduardokum\LaravelMailAutoEmbed\Embedder\Base64Embedder;
use Eduardokum\LaravelMailAutoEmbed\Tests\fixtures\PictureEntity;
use Eduardokum\LaravelMailAutoEmbed\Tests\TestCase;

class Base64EmbedderTest extends TestCase
{
    public function testOkBase64(){
        $this->assertTrue(true);
    }
//    /**
//     * @before
//     * @return void
//     */
//    protected function setUpEmbedder(): void
//    {
//        $this->embedder = new Base64Embedder();
//    }
//
//    /**
//     * @test
//     */
//    public function testLocalConversion()
//    {
//        $embedder = new Base64Embedder();
//
//        $result = $embedder->fromUrl('http://localhost/test.png');
//
//        $this->assertStringStartsWith('data:image/png;base64,', $result);
//    }
//
//    /**
//     * @test
//     */
//    public function testEntityConversion()
//    {
//        $embedder = new Base64Embedder();
//
//        $picture = new PictureEntity();
//
//        $result = $embedder->fromEntity($picture);
//
//        $this->assertStringStartsWith('data:image/png;base64,', $result);
//    }
//
//    /**
//     * @test
//     */
//    public function testRemoteUrl()
//    {
//        $result = $this->embedder->fromRemoteUrl('https://via.placeholder.com/1.png');
//
//        $this->assertStringStartsWith('data:image/png;base64,', $result);
//    }
}


================================================
FILE: tests/FormatTest.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Tests;

use Eduardokum\LaravelMailAutoEmbed\Tests\Traits\InteractsWithMessage;

/**
 * Tests some scenarios, like HTML5 mails and mail with "invalid" HTML that mail clients
 * like.
 */
class FormatTest extends TestCase
{
    use InteractsWithMessage;

    private const HANDLE_CONFIG = [
        'enabled' => true,
        'method' => 'attachment',
    ];

    /**
     * @test
     */
    public function testValidHtml5Message()
    {
        $message = $this->handleBeforeSendPerformedEvent('formats/html5-valid.html', self::HANDLE_CONFIG);

        $this->assertEmailImageTags([
            'url' => 'cid:',
            'entity' => 'cid:',
        ], $message);
    }

    /**
     * @test
     */
    public function testUserGeneratedHtml5Message()
    {
        $message = $this->handleBeforeSendPerformedEvent('formats/html5-user-generated.html', self::HANDLE_CONFIG);

        $this->assertEmailImageTags([
            'url' => 'cid:',
            'entity' => 'cid:',
        ], $message);
    }
}


================================================
FILE: tests/MailTest.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Tests;

use Eduardokum\LaravelMailAutoEmbed\Listeners\SwiftEmbedImages;
use Eduardokum\LaravelMailAutoEmbed\Tests\Traits\InteractsWithMessage;

class MailTest extends TestCase
{
    use InteractsWithMessage;

    /**
     * @test
     */
    public function testAutomaticAttachmentConversion()
    {
        $message = $this->handleBeforeSendPerformedEvent('embed-when-enabled.html', [
            'enabled' => true,
            'method' => 'attachment',
        ]);

        $this->assertEmailImageTags([
            'url' => 'cid:',
            'entity' => 'cid:',
        ], $message);
    }

    /**
     * @test
     */
    public function testSkippedConversions()
    {
        $message = $this->handleBeforeSendPerformedEvent('embed-can-skip.html', [
            'enabled' => true,
            'method' => 'attachment',
        ]);

        $this->assertEmailImageTags([
            'embed' => 'cid:',
            'skip' => 'http://localhost/test.png',
        ], $message);
    }

    /**
     * @test
     */
    public function testManualConversions()
    {
        $message = $this->handleBeforeSendPerformedEvent('manual-embed-when-disabled.html', [
            'enabled' => false,
            'method' => 'attachment',
        ]);

        $this->assertEmailImageTags([
            'ignore' => 'http://localhost/test.png',
            'embed' => 'cid:',
        ], $message);
    }

    /**
     * @test
     */
    public function testOverrideTypeBase64()
    {
        $message = $this->handleBeforeSendPerformedEvent('override-to-base64.html', [
            'enabled' => true,
            'method' => 'attachment',
        ]);

        $this->assertEmailImageTags([
            'attachment' => 'cid:',
            'base64' => 'data:image/png;base64,',
        ], $message);
    }

    /**
     * @test
     */
    public function testOverrideTypeAttachment()
    {
        $message = $this->handleBeforeSendPerformedEvent('override-to-attachment.html', [
            'enabled' => true,
            'method' => 'base64',
        ]);

        $this->assertEmailImageTags([
            'attachment' => 'cid:',
            'base64' => 'data:image/png;base64,',
        ], $message);
    }

    /**
     * @test
     */
    public function testGracefulFailureWithAttachments()
    {
        $message = $this->handleBeforeSendPerformedEvent('graceful-fails.html', [
            'enabled' => true,
            'method' => 'attachment',
        ]);

        $this->assertEmailImageTags([
            'host' => 'http://example.com/test.png',
            'image' => 'http://localhost/other.png',
            'source' => 'whatever',
            'syntax' => 'embed:whatever',
            'class' => 'embed:WrongEntityClassName:1',
            'implementation' => 'embed:Eduardokum\\LaravelMailAutoEmbed\\Tests\\fixtures\\WrongEntity:1',
            'not found' => 'embed:Eduardokum\\LaravelMailAutoEmbed\\Tests\\fixtures\\PictureEntity:9',
        ], $message);
    }

    /**
     * @test
     */
    public function testGracefulFailureWithBase64()
    {
        $message = $this->handleBeforeSendPerformedEvent('graceful-fails.html', [
            'enabled' => true,
            'method' => 'base64',
        ]);

        $this->assertEmailImageTags([
            'host' => 'http://example.com/test.png',
            'image' => 'http://localhost/other.png',
            'source' => 'whatever',
            'syntax' => 'embed:whatever',
            'class' => 'embed:WrongEntityClassName:1',
            'implementation' => 'embed:Eduardokum\\LaravelMailAutoEmbed\\Tests\\fixtures\\WrongEntity:1',
            'not found' => 'embed:Eduardokum\\LaravelMailAutoEmbed\\Tests\\fixtures\\PictureEntity:9',
        ], $message);
    }

    /**
     * @test
     */
    public function testDoesntHandleSendPerformedEvent()
    {
        if ($this->isLaravel9()) {
            $this->assertTrue(true);
        } else {
            $message = $this->createMessage('<h1>Test</h1>');

            $embedPlugin = new SwiftEmbedImages(['enabled' => true, 'method' => 'attachment']);

            $this->assertTrue(
                $embedPlugin->sendPerformed($this->createSwiftEvent($message))
            );
        }
    }

    /**
     * @test
     */
    public function testDoesntTransformRawMessages()
    {
        $message = $this->handleBeforeSendPerformedEvent('raw-message.txt', [
            'enabled' => true,
            'method' => 'attachment',
        ]);

        $this->assertEquals(
            $this->getLibraryFile('raw-message.txt'),
            ($this->isLaravel9() ? $message->getTextBody() : $message->getBody())
        );
    }

    /**
     * @test
     */
    public function testDoesNotCreateHtmlBodyForSymfonyRawMessage()
    {
        if (! $this->isLaravel9()) {
            $this->assertTrue(true);

            return;
        }

        $message = $this->handleBeforeSendPerformedEvent(
            'raw-message.txt',
            ['enabled' => true, 'method' => 'attachment'],
            true
        );

        $this->assertNull($message->getHtmlBody());
    }
}


================================================
FILE: tests/TestCase.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Tests;

use Illuminate\Foundation\Application;

class TestCase extends \Orchestra\Testbench\TestCase
{
    /**
     * Define environment setup.
     *
     * @param  \Illuminate\Foundation\Application  $app
     * @return void
     */
    protected function getEnvironmentSetUp($app)
    {
        $path = __DIR__ . '/fixtures';

        if(version_compare(Application::VERSION, '10', '<')){
            $app['path.public'] = $path;
        }else{
            $app->usePublicPath($path);
        }
    }

    /**
     * @param  \Illuminate\Foundation\Application  $app
     * @return array
     */
    protected function getPackageProviders($app)
    {
        return [\Eduardokum\LaravelMailAutoEmbed\ServiceProvider::class];
    }

    /**
     * Returns a library file.
     * @param string $name
     * @return string
     */
    protected function getLibraryFile($name)
    {
        $path = __DIR__.'/lib/'.$name;
        if (! \file_exists($path) || ! \is_file($path)) {
            $this->fail("Cannot find {$name} in file library");
        }

        return \file_get_contents($path);
    }
}


================================================
FILE: tests/Traits/InteractsWithMessage.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Tests\Traits;

use Eduardokum\LaravelMailAutoEmbed\Listeners\SwiftEmbedImages;
use Eduardokum\LaravelMailAutoEmbed\Listeners\SymfonyEmbedImages;
use Illuminate\Mail\Events\MessageSending;
use Swift_Message;
use Symfony\Component\Mime\Email;

/**
 * Shared code for creating messages and events.
 */
trait InteractsWithMessage
{
    /**
     * @return bool
     */
    private function isLaravel9()
    {
        return version_compare($this->createApplication()->version(), '9.0.0', '>=');
    }

    /**
     * @param  string  $htmlMessage
     * @param  bool    $isRawMessage
     *
     * @return Email|Swift_Message
     */
    protected function createMessage($htmlMessage, $isRawMessage = false)
    {
        if ($this->isLaravel9()) {
            return (new Email())->to('test@test.com')->from('sender@test.com')->subject('test')
                ->text($htmlMessage)
                ->html($isRawMessage ? null : $htmlMessage);
        } else {
            return new Swift_Message('test', $htmlMessage, $isRawMessage ? 'text/plain' : null);
        }
    }

    /**
     * @param  Swift_Message  $message
     * @return \Swift_Events_SendEvent
     */
    protected function createSwiftEvent(Swift_Message $message)
    {
        $dispatcher = new \Swift_Events_SimpleEventDispatcher();
        $transport = new \Swift_Transport_NullTransport($dispatcher);
        $event = new \Swift_Events_SendEvent($transport, $message);

        return $event;
    }

    /**
     * @param  string  $libraryFile
     * @param  array   $options
     * @param  bool    $isRawMessage
     * @return Swift_Message|Email
     */
    protected function handleBeforeSendPerformedEvent($libraryFile, $options, $isRawMessage = false)
    {
        $htmlMessage = $this->getLibraryFile($libraryFile);
        $message = $this->createMessage($htmlMessage, $isRawMessage);

        if ($this->isLaravel9()) {
            $event = new MessageSending($message);
            (new SymfonyEmbedImages($options))
                ->beforeSendPerformed($event);
            $event->message->getBody();
            return $event->message;
        }

        $embedPlugin = new SwiftEmbedImages($options);
        $embedPlugin->beforeSendPerformed($this->createSwiftEvent($message));

        return $message;
    }

    /**
     * Check the body for image tags with the given keys as comment preceding them.
     * @param array $expectations
     * @param string $body
     * @return void
     */
    protected function assertEmailImageTags($expectations, $body)
    {
        foreach ($expectations as $comment => $src) {
            // Fix for PHPUnit <8.0
            // phpcs:ignore Generic.Files.LineLength.TooLong
            $method = \method_exists($this, 'assertStringContainsString') ? 'assertStringContainsString' : 'assertContains';

            // Check if the string is contained within the string
            $this->$method(
                sprintf('<!-- %s --><img src="%s', $comment, $src),
                $this->isLaravel9() ? $body->getHtmlBody() : $body->getBody()
            );
        }
    }
}


================================================
FILE: tests/fixtures/PictureEntity.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Tests\fixtures;

use Eduardokum\LaravelMailAutoEmbed\Models\EmbeddableEntity;

class PictureEntity implements EmbeddableEntity
{
    /**
     * @param  mixed  $id
     * @return EmbeddableEntity|null
     */
    public static function findEmbeddable($id)
    {
        if ($id == '1') {
            return new static();
        }

        return null;
    }

    /**
     * @return mixed
     */
    public function getRawContent()
    {
        return file_get_contents(public_path('test.png'));
    }

    /**
     * @return string
     */
    public function getFileName()
    {
        return 'test.png';
    }

    /**
     * @return string
     */
    public function getMimeType()
    {
        return 'image/png';
    }
}


================================================
FILE: tests/fixtures/WrongEntity.php
================================================
<?php

namespace Eduardokum\LaravelMailAutoEmbed\Tests\fixtures;

class WrongEntity
{
}


================================================
FILE: tests/lib/embed-can-skip.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
    <!-- embed --><img src="http://localhost/test.png" />
    <!-- skip --><img src="http://localhost/test.png" data-skip-embed />
</body>
</html>


================================================
FILE: tests/lib/embed-when-enabled.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
    <!-- url --><img src="http://localhost/test.png" />
    <!-- entity --><img src="embed:Eduardokum\LaravelMailAutoEmbed\Tests\fixtures\PictureEntity:1" />
</body>
</html>


================================================
FILE: tests/lib/formats/html5-custom-embeds.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
</head>

<body>
    <main>
        <article>
            <p>
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla, dolorum assumenda aliquam blanditiis,
                necessitatibus mollitia delectus sapiente amet earum minima qui non deserunt quidem, doloremque
                architecto voluptatem eveniet illo aperiam.
            </p>

            <p>
                <figure>
                    <!-- default --><img src="http://localhost/test.png" />
                    <caption>Lorem ipsum</caption>
                </figure>
            </p>
        </article>

        <aside>
            <header>
                <h1>Lorem Ipsum</h1>
            </header>

            <p>
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempore cum, blanditiis a minima aspernatur
                corporis pariatur, perferendis explicabo consectetur tenetur iste, fuga possimus corrupti dolorem
                laudantium sapiente sunt error autem.
            </p>

            <figure>
                <!-- custom embedder --><img data-auto-embed="custom" src="http://localhost/test.png" />
                <caption>Lorem Ipsum</caption>
            </figure>
        </aside>


        <aside>
            <header>
                <h1>Lorem Ipsum Two</h1>
            </header>

            <p>
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempore cum, blanditiis a minima aspernatur
                corporis pariatur, perferendis explicabo consectetur tenetur iste, fuga possimus corrupti dolorem
                laudantium sapiente sunt error autem.
            </p>

            <figure>
                <!-- invalid embedder --><img data-auto-embed="unknown" src="http://localhost/test.png" />
                <caption>Lorem Ipsum</caption>
            </figure>
        </aside>
    </main>
</body>

</html>


================================================
FILE: tests/lib/formats/html5-user-generated.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
</head>

<body>
    <main>
        <article>
            <h1>Lorem Ipsum</h1>

            <p>
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla, dolorum assumenda aliquam blanditiis,
                necessitatibus mollitia delectus sapiente amet earum minima qui non deserunt quidem, doloremque
                architecto voluptatem eveniet illo aperiam.
            </p>

            <p>
                <!-- Source: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup -->
                <table>
                    <caption>Superheros and sidekicks</caption>
                    <colgroup>
                        <col>
                        <col span="2" class="batman">
                        <col span="2" class="flash">
                    </colgroup>
                    <tr>
                        <td> </td>
                        <th scope="col">Batman</th>
                        <th scope="col">Robin</th>
                        <th scope="col">The Flash</th>
                        <th scope="col">Kid Flash</th>
                    </tr>
                    <tr>
                        <th scope="row">Skill</th>
                        <td>Smarts</td>
                        <td>Dex, acrobat</td>
                        <td>Super speed</td>
                        <td>Super speed</td>
                    </tr>
                </table>
            </p>

            <p>
                <figure>
                    <!-- url --><img src="http://localhost/test.png" />
                    <caption>Lorem ipsum</caption>
                </figure>
            </p>
        </article>

        <aside>
            <header>
                <h1>Lorem Ipsum</h1>
            </header>

            <p>
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempore cum, blanditiis a minima aspernatur
                corporis pariatur, perferendis explicabo consectetur tenetur iste, fuga possimus corrupti dolorem
                laudantium sapiente sunt error autem.
            </p>

            <figure>
                <!-- entity --><img src="embed:Eduardokum\LaravelMailAutoEmbed\Tests\fixtures\PictureEntity:1" />
                <caption>Lorem Ipsum</caption>
            </figure>
        </aside>
    </main>
</body>

</html>


================================================
FILE: tests/lib/formats/html5-valid.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
</head>

<body>
    <main>
        <article>
            <h1>Lorem Ipsum</h1>

            <p>
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Nulla, dolorum assumenda aliquam blanditiis,
                necessitatibus mollitia delectus sapiente amet earum minima qui non deserunt quidem, doloremque
                architecto voluptatem eveniet illo aperiam.
            </p>


            <figure>
                <!-- url --><img src="http://localhost/test.png" />
                <caption>Lorem ipsum</caption>
            </figure>
        </article>

        <aside>
            <header>
                <h1>Lorem Ipsum</h1>
            </header>

            <p>
                Lorem ipsum dolor sit amet consectetur adipisicing elit. Tempore cum, blanditiis a minima aspernatur
                corporis pariatur, perferendis explicabo consectetur tenetur iste, fuga possimus corrupti dolorem
                laudantium sapiente sunt error autem.
            </p>

            <figure>
                <!-- entity --><img src="embed:Eduardokum\LaravelMailAutoEmbed\Tests\fixtures\PictureEntity:1" />
                <caption>Lorem Ipsum</caption>
            </figure>
        </aside>
    </main>
</body>

</html>


================================================
FILE: tests/lib/graceful-fails.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
    <!-- host --><img src="http://example.com/test.png" />
    <!-- image --><img src="http://localhost/other.png" />
    <!-- source --><img src="whatever" />
    <!-- syntax --><img src="embed:whatever" />
    <!-- class --><img src="embed:WrongEntityClassName:1" />
    <!-- implementation --><img src="embed:Eduardokum\LaravelMailAutoEmbed\Tests\fixtures\WrongEntity:1" />
    <!-- not found --><img src="embed:Eduardokum\LaravelMailAutoEmbed\Tests\fixtures\PictureEntity:9" />
</body>
</html>


================================================
FILE: tests/lib/manual-embed-when-disabled.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
    <!-- ignore --><img src="http://localhost/test.png" />
    <!-- embed --><img src="http://localhost/test.png" data-auto-embed />
</body>
</html>


================================================
FILE: tests/lib/override-to-attachment.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
    <!-- attachment --><img src="http://localhost/test.png" data-auto-embed="attachment" />
    <!-- base64 --><img src="http://localhost/test.png" />
</body>
</html>


================================================
FILE: tests/lib/override-to-base64.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
    <!-- attachment --><img src="http://localhost/test.png" />
    <!-- base64 --><img src="http://localhost/test.png" data-auto-embed="base64" />
</body>
</html>


================================================
FILE: tests/lib/raw-message.txt
================================================
The is a raw message that should be skipped.
It doesn't contain any images.
Download .txt
gitextract_3h0c0u8y/

├── .github/
│   ├── FUNDING.yml
│   └── workflows/
│       ├── format_php.yml
│       └── test.yml
├── .gitignore
├── .php_cs.dist
├── LICENSE
├── README.md
├── composer.json
├── config/
│   └── mail-auto-embed.php
├── phpunit.xml
├── src/
│   ├── Contracts/
│   │   ├── Embedder/
│   │   │   ├── EntityEmbedder.php
│   │   │   └── UrlEmbedder.php
│   │   └── Listeners/
│   │       └── EmbedImages.php
│   ├── Embedder/
│   │   ├── AttachmentEmbedder.php
│   │   ├── Base64Embedder.php
│   │   └── Embedder.php
│   ├── Listeners/
│   │   ├── SwiftEmbedImages.php
│   │   └── SymfonyEmbedImages.php
│   ├── Models/
│   │   └── EmbeddableEntity.php
│   └── ServiceProvider.php
└── tests/
    ├── Embedder/
    │   ├── AttachmentEmbedderTest.php
    │   └── Base64EmbedderTest.php
    ├── FormatTest.php
    ├── MailTest.php
    ├── TestCase.php
    ├── Traits/
    │   └── InteractsWithMessage.php
    ├── fixtures/
    │   ├── PictureEntity.php
    │   └── WrongEntity.php
    └── lib/
        ├── embed-can-skip.html
        ├── embed-when-enabled.html
        ├── formats/
        │   ├── html5-custom-embeds.html
        │   ├── html5-user-generated.html
        │   └── html5-valid.html
        ├── graceful-fails.html
        ├── manual-embed-when-disabled.html
        ├── override-to-attachment.html
        ├── override-to-base64.html
        └── raw-message.txt
Download .txt
SYMBOL INDEX (89 symbols across 18 files)

FILE: src/Contracts/Embedder/EntityEmbedder.php
  type EntityEmbedder (line 7) | interface EntityEmbedder
    method fromEntity (line 13) | public function fromEntity(EmbeddableEntity $entity);

FILE: src/Contracts/Embedder/UrlEmbedder.php
  type UrlEmbedder (line 5) | interface UrlEmbedder
    method fromUrl (line 11) | public function fromUrl($url);
    method fromPath (line 17) | public function fromPath($path);
    method fromBase64 (line 23) | public function fromBase64($base64);

FILE: src/Contracts/Listeners/EmbedImages.php
  type EmbedImages (line 5) | interface EmbedImages

FILE: src/Embedder/AttachmentEmbedder.php
  class AttachmentEmbedder (line 14) | class AttachmentEmbedder extends Embedder
    method setSwiftMessage (line 32) | public function setSwiftMessage(Swift_Message $message)
    method setSymfonyMessage (line 47) | public function setSymfonyMessage(Email $message)
    method fromUrl (line 61) | public function fromUrl($url)
    method fromPath (line 82) | public function fromPath($path)
    method fromBase64 (line 93) | public function fromBase64($base64)
    method fromEntity (line 109) | public function fromEntity(EmbeddableEntity $entity)
    method fromRemoteUrl (line 119) | public function fromRemoteUrl($url)
    method embed (line 170) | protected function embed($body, $name, $type)
    method isLaravel9 (line 196) | private function isLaravel9()

FILE: src/Embedder/Base64Embedder.php
  class Base64Embedder (line 8) | class Base64Embedder extends Embedder
    method __construct (line 15) | public function __construct($config)
    method fromUrl (line 24) | public function fromUrl($url)
    method fromPath (line 43) | public function fromPath($path)
    method fromEntity (line 56) | public function fromEntity(EmbeddableEntity $entity)
    method fromRemoteUrl (line 64) | public function fromRemoteUrl($url)
    method fromBase64 (line 91) | public function fromBase64($base64)
    method base64String (line 100) | private function base64String($mimeType, $content)

FILE: src/Embedder/Embedder.php
  class Embedder (line 8) | abstract class Embedder implements UrlEmbedder, EntityEmbedder

FILE: src/Listeners/SwiftEmbedImages.php
  class SwiftEmbedImages (line 20) | class SwiftEmbedImages implements Swift_Events_SendListener
    method __construct (line 35) | public function __construct($config)
    method beforeSendPerformed (line 43) | public function beforeSendPerformed(Swift_Events_SendEvent $evt)
    method handle (line 52) | public function handle(Swift_Mime_SimpleMessage $message)
    method sendPerformed (line 62) | public function sendPerformed(Swift_Events_SendEvent $evt)
    method attachImages (line 70) | private function attachImages()
    method shouldSkipDocument (line 105) | private function shouldSkipDocument(DOMDocument $document)
    method attachImagesToDom (line 121) | private function attachImagesToDom(DOMDocument &$document)
    method needsEmbed (line 148) | private function needsEmbed(DOMElement $imageTag)
    method getEmbedder (line 169) | private function getEmbedder(DOMElement $imageTag)
    method embed (line 191) | private function embed(Embedder $embedder, $src)

FILE: src/Listeners/SymfonyEmbedImages.php
  class SymfonyEmbedImages (line 18) | class SymfonyEmbedImages
    method __construct (line 33) | public function __construct($config)
    method beforeSendPerformed (line 41) | public function beforeSendPerformed(MessageSending $event)
    method handle (line 50) | public function handle(Email $message)
    method attachImages (line 59) | private function attachImages()
    method attachImagesToDom (line 89) | private function attachImagesToDom(DOMDocument &$document)
    method needsEmbed (line 116) | private function needsEmbed(DOMElement $imageTag)
    method getEmbedder (line 137) | private function getEmbedder(DOMElement $imageTag)
    method embed (line 159) | private function embed(Embedder $embedder, $src)

FILE: src/Models/EmbeddableEntity.php
  type EmbeddableEntity (line 5) | interface EmbeddableEntity
    method findEmbeddable (line 11) | public static function findEmbeddable($id);
    method getRawContent (line 16) | public function getRawContent();
    method getFileName (line 21) | public function getFileName();
    method getMimeType (line 26) | public function getMimeType();

FILE: src/ServiceProvider.php
  class ServiceProvider (line 16) | class ServiceProvider extends BaseServiceProvider
    method boot (line 23) | public function boot()
    method register (line 53) | public function register()
    method getConfigPath (line 61) | protected function getConfigPath()

FILE: tests/Embedder/AttachmentEmbedderTest.php
  class AttachmentEmbedderTest (line 11) | class AttachmentEmbedderTest extends TestCase
    method testOkAttachment (line 13) | public function testOkAttachment(){

FILE: tests/Embedder/Base64EmbedderTest.php
  class Base64EmbedderTest (line 9) | class Base64EmbedderTest extends TestCase
    method testOkBase64 (line 11) | public function testOkBase64(){

FILE: tests/FormatTest.php
  class FormatTest (line 11) | class FormatTest extends TestCase
    method testValidHtml5Message (line 23) | public function testValidHtml5Message()
    method testUserGeneratedHtml5Message (line 36) | public function testUserGeneratedHtml5Message()

FILE: tests/MailTest.php
  class MailTest (line 8) | class MailTest extends TestCase
    method testAutomaticAttachmentConversion (line 15) | public function testAutomaticAttachmentConversion()
    method testSkippedConversions (line 31) | public function testSkippedConversions()
    method testManualConversions (line 47) | public function testManualConversions()
    method testOverrideTypeBase64 (line 63) | public function testOverrideTypeBase64()
    method testOverrideTypeAttachment (line 79) | public function testOverrideTypeAttachment()
    method testGracefulFailureWithAttachments (line 95) | public function testGracefulFailureWithAttachments()
    method testGracefulFailureWithBase64 (line 116) | public function testGracefulFailureWithBase64()
    method testDoesntHandleSendPerformedEvent (line 137) | public function testDoesntHandleSendPerformedEvent()
    method testDoesntTransformRawMessages (line 155) | public function testDoesntTransformRawMessages()
    method testDoesNotCreateHtmlBodyForSymfonyRawMessage (line 171) | public function testDoesNotCreateHtmlBodyForSymfonyRawMessage()

FILE: tests/TestCase.php
  class TestCase (line 7) | class TestCase extends \Orchestra\Testbench\TestCase
    method getEnvironmentSetUp (line 15) | protected function getEnvironmentSetUp($app)
    method getPackageProviders (line 30) | protected function getPackageProviders($app)
    method getLibraryFile (line 40) | protected function getLibraryFile($name)

FILE: tests/Traits/InteractsWithMessage.php
  type InteractsWithMessage (line 14) | trait InteractsWithMessage
    method isLaravel9 (line 19) | private function isLaravel9()
    method createMessage (line 30) | protected function createMessage($htmlMessage, $isRawMessage = false)
    method createSwiftEvent (line 45) | protected function createSwiftEvent(Swift_Message $message)
    method handleBeforeSendPerformedEvent (line 60) | protected function handleBeforeSendPerformedEvent($libraryFile, $optio...
    method assertEmailImageTags (line 85) | protected function assertEmailImageTags($expectations, $body)

FILE: tests/fixtures/PictureEntity.php
  class PictureEntity (line 7) | class PictureEntity implements EmbeddableEntity
    method findEmbeddable (line 13) | public static function findEmbeddable($id)
    method getRawContent (line 25) | public function getRawContent()
    method getFileName (line 33) | public function getFileName()
    method getMimeType (line 41) | public function getMimeType()

FILE: tests/fixtures/WrongEntity.php
  class WrongEntity (line 5) | class WrongEntity
Condensed preview — 38 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (72K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 18,
    "preview": "github: eduardokum"
  },
  {
    "path": ".github/workflows/format_php.yml",
    "chars": 583,
    "preview": "name: Format (PHP)\n\non:\n  pull_request:\n    paths:\n      - \"**.php\"\n\njobs:\n  php-cs-fixer:\n    runs-on: ubuntu-latest\n  "
  },
  {
    "path": ".github/workflows/test.yml",
    "chars": 3727,
    "preview": "name: Run unit tests\n\non:\n  - push\n  - pull_request\n\nenv:\n  COMPOSER_MEMORY_LIMIT: -1\n\njobs:\n  test:\n    runs-on: ${{ ma"
  },
  {
    "path": ".gitignore",
    "chars": 51,
    "preview": "/vendor\n/composer.lock\n.idea/\n.phpunit.result.cache"
  },
  {
    "path": ".php_cs.dist",
    "chars": 4793,
    "preview": "<?php\n\n// copied from https://gist.github.com/laravel-shift/cab527923ed2a109dda047b97d53c200\n\nuse PhpCsFixer\\Config;\nuse"
  },
  {
    "path": "LICENSE",
    "chars": 1071,
    "preview": "MIT License\n\nCopyright (c) 2017 Eduardo Gusmão\n\nPermission is hereby granted, free of charge, to any person obtaining a "
  },
  {
    "path": "README.md",
    "chars": 7032,
    "preview": "[![Packagist](https://img.shields.io/packagist/v/eduardokum/laravel-mail-auto-embed.svg?style=flat-square)](https://gith"
  },
  {
    "path": "composer.json",
    "chars": 1561,
    "preview": "{\n    \"name\": \"eduardokum/laravel-mail-auto-embed\",\n    \"description\": \"Library for embed images in emails automatically"
  },
  {
    "path": "config/mail-auto-embed.php",
    "chars": 882,
    "preview": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Mail auto "
  },
  {
    "path": "phpunit.xml",
    "chars": 617,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         backupGlo"
  },
  {
    "path": "src/Contracts/Embedder/EntityEmbedder.php",
    "chars": 297,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Contracts\\Embedder;\n\nuse Eduardokum\\LaravelMailAutoEmbed\\Models\\Embedda"
  },
  {
    "path": "src/Contracts/Embedder/UrlEmbedder.php",
    "chars": 407,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Contracts\\Embedder;\n\ninterface UrlEmbedder\n{\n    /**\n     * @param  str"
  },
  {
    "path": "src/Contracts/Listeners/EmbedImages.php",
    "chars": 104,
    "preview": "<?php\r\n\r\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Contracts\\Listeners;\r\n\r\ninterface EmbedImages\r\n{\r\n\r\n}"
  },
  {
    "path": "src/Embedder/AttachmentEmbedder.php",
    "chars": 5232,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Embedder;\n\nuse Eduardokum\\LaravelMailAutoEmbed\\Models\\EmbeddableEntity;"
  },
  {
    "path": "src/Embedder/Base64Embedder.php",
    "chars": 2579,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Embedder;\n\nuse Illuminate\\Support\\Arr;\nuse Eduardokum\\LaravelMailAutoEm"
  },
  {
    "path": "src/Embedder/Embedder.php",
    "chars": 267,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Embedder;\n\nuse Eduardokum\\LaravelMailAutoEmbed\\Contracts\\Embedder\\UrlEm"
  },
  {
    "path": "src/Listeners/SwiftEmbedImages.php",
    "chars": 6634,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Listeners;\n\nuse DOMDocument;\nuse DOMElement;\nuse Eduardokum\\LaravelMail"
  },
  {
    "path": "src/Listeners/SymfonyEmbedImages.php",
    "chars": 5924,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Listeners;\n\nuse DOMDocument;\nuse DOMElement;\nuse Eduardokum\\LaravelMail"
  },
  {
    "path": "src/Models/EmbeddableEntity.php",
    "chars": 439,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Models;\n\ninterface EmbeddableEntity\n{\n    /**\n     * @param  mixed  $id"
  },
  {
    "path": "src/ServiceProvider.php",
    "chars": 2100,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed;\n\nuse Eduardokum\\LaravelMailAutoEmbed\\Contracts\\Listeners\\EmbedImages;\n"
  },
  {
    "path": "tests/Embedder/AttachmentEmbedderTest.php",
    "chars": 2072,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Tests\\Embedder;\n\nuse Eduardokum\\LaravelMailAutoEmbed\\Embedder\\Attachmen"
  },
  {
    "path": "tests/Embedder/Base64EmbedderTest.php",
    "chars": 1415,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Tests\\Embedder;\n\nuse Eduardokum\\LaravelMailAutoEmbed\\Embedder\\Base64Emb"
  },
  {
    "path": "tests/FormatTest.php",
    "chars": 1055,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Tests;\n\nuse Eduardokum\\LaravelMailAutoEmbed\\Tests\\Traits\\InteractsWithM"
  },
  {
    "path": "tests/MailTest.php",
    "chars": 5144,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Tests;\n\nuse Eduardokum\\LaravelMailAutoEmbed\\Listeners\\SwiftEmbedImages;"
  },
  {
    "path": "tests/TestCase.php",
    "chars": 1152,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Tests;\n\nuse Illuminate\\Foundation\\Application;\n\nclass TestCase extends "
  },
  {
    "path": "tests/Traits/InteractsWithMessage.php",
    "chars": 3145,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Tests\\Traits;\n\nuse Eduardokum\\LaravelMailAutoEmbed\\Listeners\\SwiftEmbed"
  },
  {
    "path": "tests/fixtures/PictureEntity.php",
    "chars": 779,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Tests\\fixtures;\n\nuse Eduardokum\\LaravelMailAutoEmbed\\Models\\EmbeddableE"
  },
  {
    "path": "tests/fixtures/WrongEntity.php",
    "chars": 88,
    "preview": "<?php\n\nnamespace Eduardokum\\LaravelMailAutoEmbed\\Tests\\fixtures;\n\nclass WrongEntity\n{\n}\n"
  },
  {
    "path": "tests/lib/embed-can-skip.html",
    "chars": 229,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n</head>\n<body>\n    <!-- embed --><img src=\"http://loc"
  },
  {
    "path": "tests/lib/embed-when-enabled.html",
    "chars": 256,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n</head>\n<body>\n    <!-- url --><img src=\"http://local"
  },
  {
    "path": "tests/lib/formats/html5-custom-embeds.html",
    "chars": 1961,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"utf-8\">\n</head>\n\n<body>\n    <main>\n        <article>\n       "
  },
  {
    "path": "tests/lib/formats/html5-user-generated.html",
    "chars": 2388,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"utf-8\">\n</head>\n\n<body>\n    <main>\n        <article>\n       "
  },
  {
    "path": "tests/lib/formats/html5-valid.html",
    "chars": 1316,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"utf-8\">\n</head>\n\n<body>\n    <main>\n        <article>\n       "
  },
  {
    "path": "tests/lib/graceful-fails.html",
    "chars": 580,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n</head>\n<body>\n    <!-- host --><img src=\"http://exam"
  },
  {
    "path": "tests/lib/manual-embed-when-disabled.html",
    "chars": 231,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n</head>\n<body>\n    <!-- ignore --><img src=\"http://lo"
  },
  {
    "path": "tests/lib/override-to-attachment.html",
    "chars": 249,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n</head>\n<body>\n    <!-- attachment --><img src=\"http:"
  },
  {
    "path": "tests/lib/override-to-base64.html",
    "chars": 245,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"utf-8\">\n</head>\n<body>\n    <!-- attachment --><img src=\"http:"
  },
  {
    "path": "tests/lib/raw-message.txt",
    "chars": 76,
    "preview": "The is a raw message that should be skipped.\nIt doesn't contain any images.\n"
  }
]

About this extraction

This page contains the full source code of the eduardokum/laravel-mail-auto-embed GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 38 files (65.1 KB), approximately 17.7k tokens, and a symbol index with 89 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!