LaravelDaily / laravel-invoices

Laravel package to generate PDF invoices from various customizable parameters

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Get logo from remote file no longer works

Maxoulak opened this issue · comments

Describe the bug
Hi folks,

First of all, thanks for your contribution to this useful package.

A (bug?) was introduced with this PR #166 which replace the php functions file_get_contents() and pathinfo() by Symfony's filesystem component in the last release.
Indeed, it is no longer possible to use a logo stored on a remote server, such as this image.
This "bug" is due to :

  1. The constructor of Symfony\Component\HttpFoundation\File\File, which use is_file() function
  2. The Symfony function guessMimeType() which use both is_file() and is_readable() functions

To Reproduce
Exec the following code will thrown an Illuminate\View\ViewException (message: The file "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Image_created_with_a_mobile_phone.png/640px-Image_created_with_a_mobile_phone.png" does not exist)

use LaravelDaily\Invoices\Invoice;
use LaravelDaily\Invoices\Classes\Buyer;
use LaravelDaily\Invoices\Classes\InvoiceItem;

$customer = new Buyer([
    'name'          => 'John Doe',
    'custom_fields' => [
        'email' => 'test@example.com',
    ],
]);

$item = (new InvoiceItem())->title('Service 1')->pricePerUnit(2);

$invoice = Invoice::make()->buyer($customer)->addItem($item)->logo("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Image_created_with_a_mobile_phone.png/640px-Image_created_with_a_mobile_phone.png")->render();

I've tried to modify the getLogo() helper method from Traits\InvoicesHelpers.php with $checkPath parameter to false, but then another ViewException is thrown (message: the "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Image_created_with_a_mobile_phone.png/640px-Image_created_with_a_mobile_phone.png" file does not exists or is not readable)

Expected behavior
No exception

Additional context
As the Symfony component does not seem to works with an URL, a first alternative could be to add a second parameter to the logo() method, such as bool $useSymfonyFileComponent = true.
If the parameter is set to false, we will use the old code with pathinfo() function, otherwise we use Symfony's file component.

I've been thinking on an other alternative, such as adding a second parameter to the logo() method called checkPath, which should be passed to File constructor.
However, we will continue facing the guessMimeType() Exception, so this is not really a working solution.

Finally, another alternative, and maybe the most appropriate one, should be to:

  1. Add a second parameter to the logo() method, something like bool $isLogoRemoteFile = false
  2. In the getLogo() method, if $isLogoRemoteFile has been set to true previously, we first download it and put the content in a temp file
  3. Then, we can keep using the Symfony's file component to get mime type & content
  4. Then, we just need delete the temp file

If you agree with the final solution, I can submit a PR in the next few days.
Note: This is quiet important for us, as we can't update to laravel 9 with this issue.

Hi, it was never intended to use a logo from remote server. The reason it worked before at all is the allow_url_fopen = on flag in your local PHP configuration which is not something you should rely on. https://www.php.net/manual/en/filesystem.configuration.php

You should fetch your remote logo using your own implementation and store it locally, also fetching remote image every time invoice is generated might significantly impair performance also forces you to implement additional logic in case what if server timeouts? what if image no longer exist? What if you need to generate 1000 invoices but server responds only every 1s. It would take over 1000 seconds.

Right approach is to download image once and pass local path to it.

Or option number 2: just save an image locally without any additional implementations.

It would be even faster than to submit a PR.

Thank you for your fast answer.
I understand. The thing is we generate invoice for many different establishments, that's why the logo can change (and maybe often), so we MUST fetch logo every time we generate an invoice (because images are stored on a remote bucket).
I was thinking it could be useful for other developers, but I understand it's more related to our architecture / use than to the package.
I will handle it on our side.

If you absolutely need that functionality in a package, I would suggest to add a new remoteLogo method and cover it with tests.