Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW File converter API #595

Merged

Conversation

GuySartorelli
Copy link
Member

@GuySartorelli GuySartorelli commented Apr 4, 2024

Description

Provides:

  • An API for file converters, so the community can provide their own file converters and expect them to work consistently with anything we build ourselves
  • A file converter that uses Intervention Image for converting from one image format to another
  • A manager for file conversion so you can just try a conversion and if there's any converter which supports it, your conversion will be successful
  • A method on File and DBFile to leverage available converters

Note that one AC is self-fulfilling:

Converters can validate the presence of required libraries, binaries, PHP modules or dependencies.

Converters can validate whatever the heck they want. If they don't have the stuff they need, they should just return false from the method that checks if they support a given conversion.

Issue

@GuySartorelli GuySartorelli force-pushed the pulls/2/file-converter-api branch 2 times, most recently from 20539db to 2a8a715 Compare April 8, 2024 23:48
/**
* An exception that represents a failure to convert a file in a FileConverter class.
*/
class FileConversionException extends LogicException
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows us and developers to catch exceptions that were intentionally thrown for conversion failures - while still correctly getting errors in their faces if there are unexpected exceptions.

* Note that if a converter supports the conversion generally but doesn't support these options, that converter will not be used.
* @throws FileConversionException if the conversion failed or there were no converters available.
*/
public function convert(DBFile $from, string $toFormat, array $options = []): DBFile
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Providing $options here gives a higher level of control for developers (and converters) which might require it.

return $problems;
}

private function supportedByIntervention(string $from, string $to, string $driver): bool
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intervention Image doesn't currently tell you what it supports. It might in the future but for now we have to check for ourselves.

src/ImageManipulation.php Outdated Show resolved Hide resolved
* exceptions for end users.
* @throws FileConversionException If the conversion fails and $returnNullOnFailure is false.
*/
public function convert(string $toFormat, bool $returnNullOnFailure = false): ?AssetContainer
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally had $returnOriginalOnFailure instead of $returnNullOnFailure, and it would return $this if the conversion failed. But null is more consistent with how other methods in this file behave, and in many cases it would be more desirable to display nothing in the template instead of the wrong thing.

Copy link
Member Author

@GuySartorelli GuySartorelli Apr 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could optionally remove that parameter and just always return null on failure. I'll leave it as is for now pending review.

The main benefit of removing the parameter is that it is more consistent with other methods here. We'd probably want to at least log the failure though if we did that.
The main benefit of keeping it is that silent failing is an explicit decision developers have to make, which makes it safer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think remove the param and just return null

You have written this "This is useful if calling this method from a template, for example, where you don't want to be throwing for end users."

That sounds like the main scenario this method will be called in? In which case let's not force the developer to have to put in a param, which they don't have to add in for any other template methods.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like that personally but I don't feel strongly enough about it to have peer review ping pong about it.

That sounds like the main scenario this method will be called in?

There are all PHP methods and can be called from PHP. Any time you're dealing with an image (or file in this case) in PHP for any reason, you'll call these methods. File conversion will I suspect be just as common in PHP as it will in templates, for things like converting files before attaching to emails, etc.

That said, I've made the requested change and logged the error as a compromise.

@GuySartorelli GuySartorelli force-pushed the pulls/2/file-converter-api branch 2 times, most recently from cb08b83 to bf86a3f Compare April 9, 2024 02:18
Comment on lines 64 to 71
} catch (ImageException $e) {
throw new FileConversionException('Failed to convert: ' . $e->getMessage(), $e->getCode(), $e);
}
// This is very unlikely but the API for `manipulateExtension()` allows for it
if ($result === null) {
throw new FileConversionException('File conversion resulted in null. Check whether original file actually exists.');
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find a way to test either of these - I think all of the scenarios that would normally result in these are being explicitly validated against already. This is just here as some defensive programming.

* exceptions for end users.
* @throws FileConversionException If the conversion fails and $returnNullOnFailure is false.
*/
public function convert(string $toFormat, bool $returnNullOnFailure = false): ?AssetContainer
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this is in the ImageManipulation trait because this trait is shared in both the File and DBFile class. The issue originally had some comments about this that I wanted to have as part of the ACs but it was decided that they were not relevant - so we're stuck with putting a file-thing into an image trait.

@GuySartorelli GuySartorelli force-pushed the pulls/2/file-converter-api branch from bf86a3f to 047acbd Compare April 9, 2024 03:33
@GuySartorelli GuySartorelli marked this pull request as ready for review April 9, 2024 03:39
@michalkleiner
Copy link
Contributor

✅ I haven't done any functional testing so won't do a full PR approval but in general this looks good to me.

Copy link
Member

@emteknetnz emteknetnz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As usual my main thing is naming :-)

  • Change FileConversionManager to FileConverterManager
  • Change FileConversionException to FileConverterException
  • Change $fromFormat to $fromExtension
  • Change $toFormat to $toExtension
  • convert() should be Convert() to match convention when used from templates

Also make corresponding naming changes in silverstripe/developer-docs#491

Also, I had this in my template: $MyFile.convert('webp') - however the html rendered had the original png file as the alt tag. I would have expected a webp file in the alt tag.

* exceptions for end users.
* @throws FileConversionException If the conversion fails and $returnNullOnFailure is false.
*/
public function convert(string $toFormat, bool $returnNullOnFailure = false): ?AssetContainer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think remove the param and just return null

You have written this "This is useful if calling this method from a template, for example, where you don't want to be throwing for end users."

That sounds like the main scenario this method will be called in? In which case let's not force the developer to have to put in a param, which they don't have to add in for any other template methods.

if ($driver === 'gd' && !function_exists('imagewebp')) {
return false;
}
if ($driver === 'imagick' && !\Imagick::queryFormats('WEBP')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if ($driver === 'imagick' && !\Imagick::queryFormats('WEBP')) {
if ($driver === 'imagick' && !Imagick::queryFormats('WEBP')) {

Import Imagick up the top. Remove the trailing slash for other examples in this file

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@GuySartorelli GuySartorelli force-pushed the pulls/2/file-converter-api branch from 047acbd to a5574b5 Compare April 10, 2024 04:10
@GuySartorelli
Copy link
Member Author

Change FileConversionManager to FileConverterManager
Change FileConversionException to FileConverterException
Change $fromFormat to $fromExtension
Change $toFormat to $toExtension

Done, though I think the first two are not as good as the original names. Naming is always subjective though so 🤷‍♀️

convert() should be Convert() to match convention when used from templates

This was suggested in #595 (comment) and I provided reasoning why I didn't do that in that comment thread. Please read that - and if you still want this change after you have that context, I'll make the change at that stage to avoid additional ping pong.

Also, I had this in my template: $MyFile.convert('webp') - however the html rendered had the original png file as the alt tag. I would have expected a webp file in the alt tag.

This is unrelated to this API - that's a bug with the low-level API and will need to be logged as a separate issue.

@emteknetnz
Copy link
Member

emteknetnz commented Apr 10, 2024

convert() should be Convert() to match convention when used from templates

Yes it's not following our own best practice for PHP methods ... however when we wrote those we probably didn't consider methods intended to called from template. The convention there very much is initial caps for method names unfortunately and we should follow the existing convention. ImageManipulation is full of public methods named like that. End users will be expecting to use $MyFile.Convert('webp') rather than $MyFile.convert('webp') because they're already calling $MyFile.ThumbnailURL(300, 200)

@GuySartorelli
Copy link
Member Author

Methods are just methods. Some methods can be called from templates. Especially in this case, it is not exclusively intended to be used in templates.

Nevertheless, I will rename this method to avoid additional unnecessary ping pong - but in the future I will be more resistant to this sort of change because it does go against our best practices (which do not and should not make exceptions for methods which can be called from templates).

Done.

@GuySartorelli GuySartorelli force-pushed the pulls/2/file-converter-api branch from a5574b5 to 145e9a8 Compare April 10, 2024 06:02
@emteknetnz emteknetnz merged commit ff5e8a7 into silverstripe:2 Apr 10, 2024
9 checks passed
@emteknetnz emteknetnz deleted the pulls/2/file-converter-api branch April 10, 2024 06:48
GuySartorelli added a commit to creative-commoners/silverstripe-assets that referenced this pull request Jul 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants